I am trying to capture data from a form input that utilizes the Google Places API.
If I begin typing a location like for instance "Kingst", and I then choose "Kingston ON, Canada" from the drop-down list of related suggestions, the input field populates with "Kingston ON, Canada" as it should.
However, when I hit the save button, only what I originally typed is saved to the database. ie: "Kingst".
Here is a possible hint:
During multiple tests, I have found out that if I put my cursor back into that same input field and then remove it again, the proper full string is saved to the database when I hit the submit button.
I'm not sure how to get the full string to save upon selecting a suggestion from the dropdown, without having to put my cursor into the field and change the string.
In my form I have this input field:
<form>
<div class="form-group">
<input id="eventLocation" placeholder="Event Location..." class="form-control" name="eventLocation" ng-model="newEvent.location">
</div>
<div class="form-group">
<input type="hidden" id="eventLat" placeholder="Latitude..." class="form-control" name="eventLat" ng-model="newEvent.lat">
</div>
<div class="form-group">
<input type="hidden" id="eventLng" placeholder="Longitude..." class="form-control" name="eventLng" ng-model="newEvent.lng">
<button type="submit" ng-disabled="fuck.$invalid" class="btn btn-primary pull-right" ng-click="addEvent(newEvent);newEvent = null;">send</button>
</form>
In the HTML below the form I have the following:
<script>
// This example displays an address form, using the autocomplete feature
// of the Google Places API to help users fill in the information.
// This example requires the Places library. Include the libraries=places
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
var placeSearch, autocomplete;
function initAutocomplete() {
// Create the autocomplete object, restricting the search to geographical
// location types.
autocomplete = new google.maps.places.Autocomplete(
/** #type {!HTMLInputElement} */(document.getElementById('eventLocation')),
{types: ['geocode']});
autocomplete.addListener('place_changed', fillInLatLng);
}
function fillInLatLng() {
var place = autocomplete.getPlace();
var lat = place.geometry.location.lat(),
lng = place.geometry.location.lng();
var eventLat = document.getElementById('eventLat');
var eventLon = document.getElementById('eventLng');
eventLat.value = lat;
eventLon.value = lng;
console.log(lat);
console.log(lng);
}
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
var circle = new google.maps.Circle({
center: geolocation,
radius: position.coords.accuracy
});
autocomplete.setBounds(circle.getBounds());
});
}
}
</script>
In the controller I have:
$scope.addEvent = function(newEvent) {
var eventLat = document.getElementById('eventLat').value;
var eventLng = document.getElementById('eventLng').value;
if (newEvent) {
// push a event to the end of the array
$scope.events.$add({
location: newEvent.location,
latitude: eventLat,
longitude: eventLng,
createdBy: user.uid,
createdAt: Firebase.ServerValue.TIMESTAMP
})
// display any errors
.catch(alert);
}
document.getElementById('eventLat').value = '';
document.getElementById('eventLng').value = '';
};
I solved this issue by doing the following:
In my controller I added:
var eventLoc = document.getElementById('eventLocation').value;
...
$scope.events.$add({
location: eventLoc,
...
Better you used this code. Because while using this code circular to json conversion is build inside the js
Controller.js file
.directive('googleplace', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, model) {
var options = {
types: [],
componentRestrictions: {country: 'in'}
};
scope.gPlace = new google.maps.places.Autocomplete(element[0], options);
google.maps.event.addListener(scope.gPlace, 'place_changed', function() {
scope.$apply(function() {
model.$setViewValue(element.val());
});
});
}
};
});
Note:If u can read on specfic country means , include this tag
componentRestrictions: {country: 'in'}
In HTML file you just add googleplace tag only.
Related
Currently learning AngularJS, and I want to use it to upload and display images as part of a way to document project summaries. I found Angular File Upload, but I'm not exactly too sure as to how I should go about implementing it in my program.
Basically what I have so far is a button that generates a form, and clicking the submit adds those fields to a display that generates upon the button click. The text/number fields display fine, but obviously attaching/uploading images would require more work.
Demo picture
I'm using a C9 development environment so I'm not too sure how to make my workspace publicly viewable, but I pasted the main code segments on JSFiddle (it doesn't exactly work properly but at least you can view the JS code):
https://jsfiddle.net/edmond_wu/63v98qt6/9/
var app = angular.module('myApp', []);
app.controller('ctrl', function($scope) {
$scope.global_savings = 0;
$scope.items = [];
$scope.itemsToAdd = [];
$scope.addReport = function(item) {
var index = $scope.itemsToAdd.indexOf(item);
$scope.global_savings += Number(item.savings);
if ($scope.global_savings >= 100000) {
alert("Your benchmark of $100,000 has been reached!");
}
$scope.itemsToAdd.splice(index, 1);
$scope.items.push(angular.copy(item));
}
$scope.addNew = function() {
$scope.itemsToAdd.push({
title: '',
manager: '',
savings: '',
summary: '',
result: '',
image: ''
});
}
The HTML is something like this with a Submit button at the bottom (the submit button should upload the image along with the rest of the form data):
<input type="file" class="button" ng-model="itemToAdd.image" accept="image/*">
<input type="submit" class="button" ng-click="addReport(itemToAdd)">
I don't think you can use ng-model on an input of type="file".
I managed to get it working using a custom directive from here:
app.directive("fileread", [function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
var reader = new FileReader();
reader.onload = function (loadEvent) {
scope.$apply(function () {
scope.fileread = loadEvent.target.result;
});
}
reader.readAsDataURL(changeEvent.target.files[0]);
});
}
}
}]);
Then change your input to:
<input type="file" class="button" fileread="itemToAdd.image" accept="image/*">
All you have to do is add directive to your current angular code and change the one line in the html, thats it.
jsfidlle: https://jsfiddle.net/63v98qt6/10/
The above creates a directive fileread which binds the image to the itemToAdd.image of your object on scope.
I'm trying to dynamically add a textbox to an Angular form. I'm using ng-repeat and I can add the text box easily by just pushing, an empty string to the array. It will add another textbox, the problem is that ng-model is not syncing the data, and when text is added it remains an empty string.
The text boxes that get created from the initial array sync just fine, it's just the newly added text boxes that are not working.
I've been looking around and one suggestion I saw was to always use a "." when using ng-model, but that didn't work for me.
<div ng-repeat="text in image.text track by $index">
<md-input-container class="md-block" novalidate>
<label>Text {{$index + 1}}:</label>
<textarea md-maxlength="2500" md-midlength="1" required md-no-asterisk name="text"
placeholder="{{text}}"
ng-model="text"></textarea>
</md-input-container>
</div>
Controller:
(function () {
'use strict';
angular
.module('app.article')
.controller('ArticleEditController', ArticleEditController);
ArticleEditController.$inject= ['articleEditDataService', '$routeParams', '$log'];
function ArticleEditController(articleEditDataService, $routeParams, $log) {
var vm = this;
var site = $routeParams.site;
var articleName = $routeParams.article;
var articleRevision_id = $routeParams.revision_id;
vm.data = {};
vm.addNewText = addNewText;
vm.removeText = removeText;
vm.saveArticle = saveArticle;
vm.numMessage = 1;
activate();
function activate(){
getArticle();
}
function getArticle(){
var data = articleEditDataService.getArticle(site, articleName, articleRevision_id);
data.then(function successResponse(res) {
vm.data = res.results.data;
}, function errorResponse (res) {
console.log(res);
});
}
function saveArticle(){
var article = articleEditDataService.postArticle(vm.data, site, articleName, articleRevision_id);
console.log(vm.data);
article
.then(updateArticleSuccess)
.catch(updateArticleError);
}
function updateArticleSuccess(message){
$log.info(message);
}
function updateArticleError(errorMessage){
$log.error(errorMessage);
}
function addNewText (index, key) {
vm.data.content.image_sets[key].text.push("");
}
function removeText (index, key) {
if(vm.data.content.image_sets[key].text.length > 1){
vm.data.content.image_sets[key].text.pop();
}
}
};
})();
initialize the model variable within your controller as an empty object.
then within your text input your ng-model="model[$index]".
I'm using AngularJS/Firebase (AngularFire) & Google Maps API to create a basic travel wishlist.
So far I have the following:
HTML:
<form ng-submit="setNewEntry(field)">
<input type="text" googleplace ng-model="field" />
</form>
Controller function:
$scope.setNewEntry = function(val) {
ref.child("places").push({
"location": val
})
$scope.field = "";
}
Directive: (to bind autocomplete to input)
.directive('googleplace', function() {
return {
link: function(scope, element, attr) {
scope.gPlace = new google.maps.places.Autocomplete(element[0]);
}
}
})
This all works fine and pushes the value through to my Firebase app. The issue I'm having is with ng-model not picking up the value that Google provides.
Say I type 'nashville' and I get an autocomplete suggestion, I click on it and it fills in the input with 'Nashville, TN, United States' - once I hit enter to submit the form, it pushes through my initial search query 'nashville'.
Any ideas on how I would pass through the final value of the input?
Thanks!
I think you have not created a variable that whill hold the values of the autocomplete results . There is a simple directive for adding google places autocomplete to a textbox element which is called as ng-Autocomplete.
Please refer to the following code example to know more about the code implementation I am talking about.
//create new autocomplete
//reinitializes on every change of the options provided
var newAutocomplete = function() {
scope.gPlace = new google.maps.places.Autocomplete(element[0], opts);
google.maps.event.addListener(scope.gPlace, 'place_changed', function() {
scope.$apply(function() {
// if (scope.details) {
scope.details = scope.gPlace.getPlace();
// }
scope.ngAutocomplete = element.val();
});
})
}
newAutocomplete()
I have a simple form like so:
<form name="add-form" data-ng-submit="addToDo()">
<label for="todo-name">Add a new item:</label>
<input type="text" data-ng-model="toDoName" id="todo-name" name="todo-name" required>
<button type="submit">Add</button>
</form>
with my controller as follows:
$scope.addToDo = function() {
if ($scope.toDoName !== "") {
$scope.toDos.push(createToDo($scope.toDoName));
}
}
what I'd like to do is clear the text input after submission so I simply clear the model value:
$scope.addToDo = function() {
if ($scope.toDoName !== "") {
$scope.toDos.push(createToDo($scope.toDoName));
$scope.toDoName = "";
}
}
Except now, because the form input is 'required' I get a red border around the form input. This is the correct behaviour, but not what I want in this scenario... so instead I'd like to clear the input and then blur the input element. Which leads me to:
$scope.addToDo = function() {
if ($scope.toDoName !== "") {
$scope.toDos.push(createToDo($scope.toDoName));
$scope.toDoName = "";
$window.document.getElementById('todo-name').blur();
}
}
Now, I know that modifying the DOM from the controller like this is frowned upon in the Angular documentation - but is there a more Angular way of doing this?
When you give a name to your form it automatically gets added to the $scope.
So if you change your form name to "addForm" ('cause I don't think add-from is a valid name for angular, not sure why), you'll have a reference to $scope.addForm.
If you use angular 1.1.1 or above, you'll have a $setPristine() method on the $scope.addForm. which should recursively take care of resetting your form. or if you don't want to use the 1.1.x versions, you can look at the source and emulate it.
For those not switching over to 1.1.1 yet, here is a directive that will blur when a $scope property changes:
app.directive('blur', function () {
return function (scope, element, attrs) {
scope.$watch(attrs.blur, function () {
element[0].blur();
});
};
});
The controller must now change a property whenever a submit occurs. But at least we're not doing DOM manipulation in a controller, and we don't have to look up the element by ID:
function MainCtrl($scope) {
$scope.toDos = [];
$scope.submitToggle = true;
$scope.addToDo = function () {
if ($scope.toDoName !== "") {
$scope.toDos.push($scope.toDoName);
$scope.toDoName = "";
$scope.submitToggle = !$scope.submitToggle;
}
};
}
HTML:
<input type="text" data-ng-model="toDoName" name="todo-name" required
blur="submitToggle">
Plnkr
I have made it work it as below code.
HTML SECTION
<td ng-show="a">
<input type="text" ng-model="e.FirstName" />
</td>
Controller SECTION
e.FirstName= '';
A function to reset form fields to pristine state (reset dirty state) is on the roadmap for AngularJS 1.1.x. Unfortunately such a function is missing from the current stable release.
What is the best way to reset all form fields to their initial pristine state for AngularJS 1.0.x.?
I would like to know if this is fixable with a directive or other simple workaround. I prefer a solution without having to touch the original AngularJS sources. To clarify and demonstrate the problem, a link to JSFiddle. http://jsfiddle.net/juurlink/FWGxG/7/
Desired feature is on the Roadmap - http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html
Feature request - https://github.com/angular/angular.js/issues/856
Proposed solution Pull request - https://github.com/angular/angular.js/pull/1127
Updated with possible workaround
Good enough workaround?
I just figured out I can recompile the HTML part and put it back into the DOM. It works and it's fine for a temporarily solution, but also as #blesh mentioned in the comments:
Controllers should be used for business logic only, not for DOM!
<div id="myform">
<form class="form-horizontal" name="form">
</form>
</div>
And in my Controller on resetForm():
Save the original untouched HTML
Recompile the saved original HTML
Remove the current form from the DOM
Insert the new compiled template into the DOM
The JavaScript:
var pristineFormTemplate = $('#myform').html();
$scope.resetForm = function () {
$('#myform').empty().append($compile(pristineFormTemplate)($scope));
}
I think it's worth mentioning that in later versions of Angular (e.g. 1.1.5), you can call $setPristine on the form.
$scope.formName.$setPristine(true)
This will set all the form controls to pristine state as well.
FormController.$setPristine
Solution without a workaround
I came up with a solution which uses AngularJS without any workaround. The trick here is to use AngularJS ability to have more than one directive with the same name.
As others mentioned there is actually a pull request (https://github.com/angular/angular.js/pull/1127) which made it into the AngularJS 1.1.x branch which allows forms to be reset. The commit to this pull request alters the ngModel and form/ngForm directives (I would have liked to add a link but Stackoverflow doesn't want me to add more than two links).
We can now define our own ngModel and form/ngForm directives and extend them with the functionality provided in the pull request.
I have wrapped these directives in a AngularJS module named resettableForm. All you have to do is to include this module to your project and your AngularJS version 1.0.x behaves as if it was an Angular 1.1.x version in this regard.
''Once you update to 1.1.x you don't even have to update your code, just remove the module and you are done!''
This module also passes all tests added to the 1.1.x branch for the form reset functionality.
You can see the module working in an example in a jsFiddle (http://jsfiddle.net/jupiter/7jwZR/1/) I created.
Step 1: Include the resettableform module in your project
(function(angular) {
// Copied from AngluarJS
function indexOf(array, obj) {
if (array.indexOf) return array.indexOf(obj);
for ( var i = 0; i < array.length; i++) {
if (obj === array[i]) return i;
}
return -1;
}
// Copied from AngularJS
function arrayRemove(array, value) {
var index = indexOf(array, value);
if (index >=0)
array.splice(index, 1);
return value;
}
// Copied from AngularJS
var PRISTINE_CLASS = 'ng-pristine';
var DIRTY_CLASS = 'ng-dirty';
var formDirectiveFactory = function(isNgForm) {
return function() {
var formDirective = {
restrict: 'E',
require: ['form'],
compile: function() {
return {
pre: function(scope, element, attrs, ctrls) {
var form = ctrls[0];
var $addControl = form.$addControl;
var $removeControl = form.$removeControl;
var controls = [];
form.$addControl = function(control) {
controls.push(control);
$addControl.apply(this, arguments);
}
form.$removeControl = function(control) {
arrayRemove(controls, control);
$removeControl.apply(this, arguments);
}
form.$setPristine = function() {
element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
form.$dirty = false;
form.$pristine = true;
angular.forEach(controls, function(control) {
control.$setPristine();
});
}
},
};
},
};
return isNgForm ? angular.extend(angular.copy(formDirective), {restrict: 'EAC'}) : formDirective;
};
}
var ngFormDirective = formDirectiveFactory(true);
var formDirective = formDirectiveFactory();
angular.module('resettableForm', []).
directive('ngForm', ngFormDirective).
directive('form', formDirective).
directive('ngModel', function() {
return {
require: ['ngModel'],
link: function(scope, element, attrs, ctrls) {
var control = ctrls[0];
control.$setPristine = function() {
this.$dirty = false;
this.$pristine = true;
element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
}
},
};
});
})(angular);
Step 2: Provide a method on your controller which resets the model
Please be aware that you must reset the model when you reset the form. In your controller you can write:
var myApp = angular.module('myApp', ['resettableForm']);
function MyCtrl($scope) {
$scope.reset = function() {
$scope.form.$setPristine();
$scope.model = '';
};
}
Step 3: Include this method in your HTML template
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<form name="form">
<input name="requiredField" ng-model="model.requiredField" required/> (Required, but no other validators)
<p ng-show="form.requiredField.$errror.required">Field is required</p>
<button ng-click="reset()">Reset form</button>
</form>
<p>Pristine: {{form.$pristine}}</p>
</div>
</dvi>
EDIT... I'm removing my old answer, as it was not adequate.
I actually just ran into this issue myself and here was my solution: I made an extension method for angular. I did so by following a bit of what $scope.form.$setValidity() was doing (in reverse)...
Here's a plnkr demo of it in action
Here's the helper method I made. It's a hack, but it works:
angular.resetForm = function (scope, formName, defaults) {
$('form[name=' + formName + '], form[name=' + formName + '] .ng-dirty').removeClass('ng-dirty').addClass('ng-pristine');
var form = scope[formName];
form.$dirty = false;
form.$pristine = true;
for(var field in form) {
if(form[field].$pristine === false) {
form[field].$pristine = true;
}
if(form[field].$dirty === true) {
form[field].$dirty = false;
}
}
for(var d in defaults) {
scope[d] = defaults[d];
}
};
Hopefully this is helpful to someone.
Your form fields should be linked to a variable within your $scope. You can reset the form by resetting the variables. It should probably be a single object like $scope.form.
Lets say you have a simple form for a user.
app.controller('Ctrl', function Ctrl($scope){
var defaultForm = {
first_name : "",
last_name : "",
address: "",
email: ""
};
$scope.resetForm = function(){
$scope.form = defaultForm;
};
});
This will work great as long as your html looks like:
<form>
<input ng-model="form.first_name"/>
<input ng-model="form.last_name"/>
<input ng-model="form.address"/>
<input ng-model="form.email"/>
<button ng-click="resetForm()">Reset Form</button>
</form>
Maybe I'm not understanding the issue here, so if this does not address your question, could you explain why exactly?
Here I have found a solution for putting the from to its pristine state.
var def = {
name: '',
password: '',
email: '',
mobile: ''
};
$scope.submited = false;
$scope.regd = function (user) {
if ($scope.user.$valid) {
$http.post('saveUser', user).success(function (d) {
angular.extend($scope.user, def);
$scope.user.$setPristine(true);
$scope.user.submited = false;
}).error(function (e) {});
} else {
$scope.user.submited = true;
}
};
Just write angular.extends(src,dst) ,so that your original object is just extends the blank object, which will appear as blank and rest all are default.
Using an external directive and a lot of jquery
app.controller('a', function($scope) {
$scope.caca = function() {
$scope.$emit('resetForm');
}
});
app.directive('form', function() {
return {
restrict: 'E',
link: function(scope, iElem) {
scope.$on('resetForm', function() {
iElem.find('[ng-model]').andSelf().add('[ng-form]').each(function(i, elem) {
var target = $(elem).addClass('ng-pristine').removeClass('ng-dirty');
var control = target.controller('ngModel') || target.controller('form');
control.$pristine = true;
control.$dirty = false;
});
});
}
};
});
http://jsfiddle.net/pPbzz/2/
The easy way: just pass the form into the controller function. Below the form "myForm" is referenced by this, which is equivalent to $scope.
<div ng-controller="MyController as mc">
<ng-form name="myform">
<input ng-model="mc.myFormValues.name" type="text" required>
<button ng-click="mc.doSometing(this.myform)" type="submit"
ng-disabled="myform.$invalid||myform.$pristine">Do It!</button>
</ng-form>
</div>
The Controller:
function MyController(MyService) {
var self = this;
self.myFormValues = {
name: 'Chris'
};
self.doSomething = function (form) {
var aform = form;
MyService.saveSomething(self.myFromValues)
.then(function (result) {
...
aform.$setPristine();
}).catch(function (e) {
...
aform.$setDirty();
})
}
}