Angular setting form with scope? - angularjs

What I want is a form that I can use for both creating and updating. So I pass before showing
$scope.form = {};
$scope.car = null;
$scope.getCar = function(hash) {
$http.get('/cars/'+hash).success(function(car) {
$scope.car = car;
$scope.form = car;
});
};
As you can see I add the result of the get to both car and form.
Now I'm opening the View:
<h1>{{ form.name }} <small>shows correctly</small></h1>
But a line after that I'm trying almost the same:
<form class="list" ng-submit="createOrUpdateForm(form)">
<label class="item">
<span class="input-label">Name</span>
<input type="text" ng-model="form.name">
Here it's not shown... But when I add the same line after it like this:
<input type="text" ng-model="car.name">
This does work, but then I can't use the ng-submit anymore, because that references to form.
Form some reason I can't set the form scope?

You should not manual assigning anything to form. A "form" is not the same as the data you manage using the form. Neither the empty object {} nor car make sense in that context.
Give the form a name, this will allow angular to assign it to a scope property.
<h1>{{ car.name }} <small>shows correctly</small></h1>
<form name="carForm" ng-submit="createOrUpdateForm(carForm)">
<label class="item">
<span class="input-label">Name</span>
<input type="text" ng-model="car.name">
$scope.createOrUpdateForm = function(form) {
if(form.$valid) {
console.log($scope.car.name);
// POST / PUT your data.
}
};

Related

Get pristine value in Angular controller

I need to be able to see in the Angular controller if the datepicker is pristine or not. Tried all sorts of things including sending the pristine value in a method but cannot get this value. Below is the view code:
<form name="myForm">
<!-- Datepicker From -->
<div class="small-6 medium-5 large-2 columns" ng-if="vm.subViewActive">
<div class="input-group">
<input name="valuationDatePickerFrom" ng-model="name" type="text" class="datepicker" id="valuationDatePickerFrom" placeholder="DD/MM/YYYY" pikaday="vm.datePickerFrom" on-select="vm.selectStartDate(pikaday)" year-range="{{ vm.yearRange }}" >
<div class="input-group-addon">
<label for="valuationDatePickerFrom" class="postfix">
<i class="fa fa-calendar"></i> From
</label>
</div>
</div>
</div>
</form>
and then I also tried :
var isPristine = $scope.myForm.valuationDatePickerFrom.$pristine;
console.log(isPristine);
in my controller but cannot get the pristine value. Read lots of posts here but mainly to do with CSS classes and front-end control or setting the pristine state from the backend not getting or checking the pristine state.
Thanks anybody that can help.
You are using:
var isPristine = $scope.myForm.valuationDatePickerFrom.$pristine;
but your form's name is not myForm.
Change <input name="name"... <input name="valuationDatePickerFrom"...
Then you can use:
var isPristine = $scope.userForm.valuationDatePickerFrom.$pristine;
Also, the controller is getting called before the view is created, so no myForm exists at the time the controller runs. Try adding a $timeout like so:
$timeout(function() {
var isPristine = $scope.userForm.valuationDatePickerFrom.$pristine;
console.log(isPristine);
}, 100);
plunkr
The above solution only works on page load, but you need to know this value when the page is being used. Instead pass the value to the controller when an action happens:
<form name="myForm">
<input type="text" name="valuationDatePickerFrom" ng-model="valuationDatePicker" ng-blur="alerty(myForm.$pristine)">
</form>
.controller('MainController', function($scope) {
$scope.alerty = function(isPristine){
alert('isPristine: ' + isPristine);
};
https://plnkr.co/edit/f0EWvYmoXCn8UOH3QCfE?p=preview

Elegant way in Angular to enable submit if all child forms but the last are valid

In the simple Angular app I'm working on there is a parent form with various child forms that are created from an ng-repeat. I'd like a submit button to be tied to the parent form's $valid property OR some other simple way to enable the submit button when the form is what I consider to be valid. However, due to the user workflow a new child form is added after the fields of the last child form are completed. Thus, even a perfect form's last row is always empty & thus invalid (as there are required fields). Any suggestions on how to handle enable/disable of a submit button in this situation? I've created a fiddle that shows the issue. There is a submit button linked to the parent form's $valid, which does not show up unless the last form (row) is valid. Then there's a second submit button, whose enabled property I control by explicitly looking at all child forms and setting a scope variable to true or false based on my requirements. I think this solution is inelegant and there's got to be a better way.
https://jsfiddle.net/godinger/r5yx5z4b/
Here's the HTML form:
<div ng-app="myApp" ng-controller="myCtrl">
<pre>Parent Form Valid: {{parentForm.$valid}}</pre>
<ng-form name="parentForm">
<div ng-repeat="person in people">
<ng-form name="childForm" novalidate>
<input name="firstName"
required
ng-model="person.firstName"
ng-init="person.form=childForm"/>
<input name="lastName"
required
ng-model="person.lastName"
ng-init="person.form=childForm"/>
<span>Valid: {{childForm.$valid}}</span>
</ng-form>
</div>
<button ng-submit ng-disabled="parentForm.$invalid">
Submit
</button>
<button ng-submit ng-disabled="!enableSubmit">
Workaround Submit
</button>
</ng-form>
</div>
And here's the Angular code:
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope) {
$scope.enableSubmit = false;
$scope.people = [
{firstName: 'John', lastName: "Adams"},
{firstName: 'Alex', lastName: 'Hamilton'},
{firstName: '', lastName:''}];
$scope.$watch(updateSubmit);
function updateSubmit(){
if (!$scope.people){
$scope.enableSubmit = false;
return;
}
var numberOfPeople = $scope.people.length;
for (var i=0; i<numberOfPeople-1; i++){
var personForm = $scope.people[i].form;
if (personForm.$invalid){
$scope.enableSubmit = false;
return;
}
}
var lastPerson = $scope.people[numberOfPeople-1];
var lastPersonForm = lastPerson.form;
if (lastPersonForm.$valid){
$scope.enableSubmit = true;
return;
}
if (lastPerson.firstName || lastPerson.lastName){
$scope.enableSubmit = false;
return;
}else{
$scope.enableSubmit = true;
return;
}
}
});
I'm not sure that your implementation makes the most sense, but if want you want to do is provide "different html" based on the last element in ng-repeat, the simplest way is to use the $last element local variable. There are a number of ways to do this...but here is the simplest answer to your question:
<ng-form ng-if="!$last" name="childForm" novalidate>
<input name="firstName" placeholder="First name"
ng-model="person.firstName"
ng-init="person.form=childForm"/>
<input name="lastName"
required placeholder="First name"
ng-model="person.lastName"
ng-init="person.form=childForm"/>
<span>Valid: {{childForm.$valid}}</span>
</ng-form>
<div ng-if="$last">
Do Other Logic / Show other stuff (this could even be repeat all the form elements again, but remove all of the 'required' properties)
</div>

Firebase .push() Error and ng-model not working

I'm getting this error:
Error: Firebase .push failed: first argument contains undefined property 'price'
This is my controller:
angular.module('starter')
.controller('EditPicsController', function($scope,$location,$state) {
var itemsRef = new Firebase('https://myapp.firebaseio.com/items');
var itemprice = this.price
$scope.createItem = function(itemprice){
console.log(itemprice);
var newItemRef = itemsRef.push({'price':itemprice});
};
});
And my template:
<form>
<div class="list">
<label ng-model="price" class="item item-input item-stacked-label">
<span class="input-label">Price</span>
<input type="text" placeholder="$200.00" >
</label>
</div>
<button ng-click="createItem(price)" class='button button-dark'>
test-additem
</button>
</form>
my console.log output is blank.
What's going on here?
You should apply ng-model to input rather label.
<input ng-model="price" type="text" placeholder="$200.00" >
instead of
<label ng-model="price" class="item item-input item-stacked-label">
And in your code you've created a object of a class i.e. itemsRef. Its an object not an array. So because of this line itemsRef.push({'price':itemprice}); you're getting error. push is for arrays not for object.
You don't seem to use angularJS in its entirety.
As mentioned in the other answer you should set ng-model on input element rather than label because then you can leverage the angularJS philosophy of two-way data binding.
This is because user can interact with your client-side application in angularJS and can only change the value of HTML elements which are editable.
So those values are ng-model and will be binded to your controller and exposed through the glue called $scope in your controller.
So your HTML will be:
<form>
<div class="list">
<label class="item item-input item-stacked-label">
<span class="input-label">Price</span>
<input ng-model="price" type="text" placeholder="$200.00" >
</label>
</div>
<button ng-click="createItem()" class='button button-dark'>
test-additem
</button>
And your controller code:
angular.module('starter')
.controller('EditPicsController', function($scope,$location,$state) {
$scope.itemsRef = new Firebase('https://myapp.firebaseio.com/items');
$scope.createItem = function(){
var newItemRef = $scope.itemsRef.push({'price': $scope.price});
};
});
Also if your $scope.price is undefined then Firebase will complain because it wont allow you to save values which are undefined so set some Form validation or initialise the ng-model ($scope.price) with some value to test.
UPDATE: Your code $scope.itemsRef.push({'price': $scope.price}); for saving to Firebase is absolutely correct its just that Firebase doesn't like undefined in javascript so your model $scope.price needs to have a value to get this working
Since Firebase does not like undefined attributes like this:
item = { 'name': 'hot dog', 'price': undefined };
itemsRef.push(item);
I do a quick and dirty clean up of my objects before I push
for( let attr in item){
try {
if( item[attr] == undefined ) delete item[attr];
} catch(e){}
}
itemsRef.push(item);
which will push
{ 'name': 'hot dog' }
and firebase is happy :-) ..

Angular form name is passed as string when passed as parameter

I'm simply trying to reset a form using the angular functions $setPristine & $setUntouched (several forms are created with ng-repeat).
I assign the form name dynamically by using the syntax {{ someName }} (the name is build on the server side and is passed as json (string)).
The name of the form is correctly assigned in the markup and validations are working as expected. The problem arrises when I pass that name as a parameter in the ng-click="reset(someName)" function.
When debugging the name comes as a string and not as the form object which causes the error. I did a quick test by hard-coding the name and pass that same name and it works fine.
My assumption is, the name coming from json is a string and the type is forwarded to the function as is, instead of the object.
So the question is: is there a way to convert that name so it is interpretated correctly by the controller. Or maybe there is something else I'm missing...
Here is the markup ( notice the name of the form uses {{ resto.contactForm }} ):
<form novalidate name="{{ resto.contactForm }}" ng-submit="submit(restoContact, resto.contactForm.$valid)" class="sky-form">
<div class="form-group">
<label class="checkbox state-success">
<input type="checkbox" ng-model="restoContact.sameAsUser" name="sameAsUser" id="sameAsUser" value="true" ng-click="contactAutoFill()"><i></i>Contact name is same as current user.
<input type="hidden" name="sameAsUser" value="false" />
</label>
</div>
<div class="form-group">
<label class="control-label" for="contactName">Contact Name</label>
<input type="text" ng-model="restoContact.contactName" name="contactName" id="contactName" placeholder="John, Doe" class="form-control" required />
<div ng-show="{{ resto.contactForm }}.contactName.$error.required && !{{ resto.contactForm }}.contactName.$pristine" class="note note-error">Please enter a name or check the box 'Same as current user'.</div>
</div>
<div class="form-group">
<label class="control-label" for="contactPhoneNumber">Contact Phone Number</label>
<input type="text" ng-model="restoContact.contactPhoneNumber" name="contactPhoneNumber" id="contactPhoneNumber" placeholder="+1 555-1234-567" class="form-control" required ng-pattern="phoneNumberPattern" />
<div ng-show="({{ resto.contactForm }}.contactPhoneNumber.$error.required || {{ resto.contactForm }}.contactPhoneNumber.$error.pattern) && !{{ resto.contactForm }}.contactPhoneNumber.$pristine" class="note note-error">Please enter a valid phone number.</div>
</div>
<div class="margin-leftM19">
<button class="btn btn-primary">Save Changes </button>
<button class="btn btn-default" ng-click="reset(resto.contactForm)">Cancel </button>
</div>
</form>
Here is the reset function in the controller (form comes as "contactForm1" which is the correct name but is a string and not the object):
$scope.reset = function (form) {
if (form) {
form.$setPristine();
form.$setUntouched();
}
//$scope.user = angular.copy($scope.master);
};
I have not implemented th submit method but I'm sure I will be running into the same issue.
Any suggestions or advices are welcome.
Thanks in advance...
Here is the fidle.js. the variable data is an exact response from the server.
[http://jsfiddle.net/bouchepat/v0mtbxep/]
SOLUTION:
http://jsfiddle.net/bouchepat/v0mtbxep/3/
I removed $setUntouched as it throws an error.
You can't dynamically name a <form> or <ng-form>.
Although what you want, is make the form usable in the controller. You could do the following:
// in controller
$scope.form = {};
$scope.reset = function() {
$scope.form.contact.$setPristine();
$scope.form.contact.$setUntouched();
};
// in html
<form name="form.contact">
This is happening because resto.contactForm is a string defined on the scope. The angular directive for form is just creating a variable on the scope with the same name. To get the variable by a string, use $eval. This should work:
$scope.reset = function (formName) {
var form = $scope.$eval(formName);
if (form) {
form.$setPristine();
form.$setUntouched();
}
//$scope.user = angular.copy($scope.master);
};

How to add object properties from ng-checked items to my ng-model object

I have four input fields which I'm using to add properties to a single object using ng-model= model.propertyName. I have a series of check boxes that I'm creating with ng-repeat that I could not figure out how to add unique propertyNames for each ng-model as they were created with the ng-repeat. As a work-around(or maybe this is correct, I'm not sure) I was able to write a function to add the checked items to an array. I was then trying to use a for-loop to iterate over the array and add each selected propertyName(string) to the ng-model object as a new property using a ng-click to call the function.
As-is when I click the "Add Technician" button I get the following error output:
TypeError: Cannot read property 'selection' of undefined
at Scope.$scope.addTechnician (..../scripts/controllers.js:
This occurs because $scope is undefined inside the conditional of my for loop in the addTechnician function. I can't understand why because when I pass $scope to the addTechnician function it recognizes the newTech inside the for loop. When I don't pass $scope to the addTechnician function it says newTech is undefined with the following error:
TypeError: Cannot set property 'cert1' of undefined
at Scope.$scope.addTechnician(.../scripts/controllers)
I'm pretty sure this has something to do with the way ng-repeat creates a new scope, which prototypically inherits from the parent scope. But again, I'm not sure.
Here is my controller
use strict';
angular.module('Carrepair2.controllers', [])
.controller('SetupCtrl', function($scope) {
$scope.certifications = [
{'name':'Engine Repair'},
{'name':'A/T & Transaxle'},
{'name':'Manual Drive Train & Axles'},
{'name':'Suspension & Steering'},
{'name':'Brakes'},
{'name':'Electrical & Electronic Systems'},
{'name':'Heating & Air Conditioning'},
{'name':'Engine Performance'},
{'name':'Light Vehicle Diesel Engines'}
];
// selected certifications
$scope.selection = [];
$scope.toggleCert = function(name) {
var idx = $scope.selection.indexOf(name);
//is currently selected
if (idx > -1) {
$scope.selection.splice(idx, 1);
}
//is newly selected
else{
$scope.selection.push(name);
}
};
$scope.addTechnician = function($scope) {
for(var i=0; i < $scope.selection.length - 1; i++){
$scope.newTech['cert' + (i + 1).toString()] = $scope.selection[i].name;
}
};
})
Here is my template
<form class="col-md-6">
<div class="input_wrapper">
<input type="text" name="first-name" ng-model="newTech.firstName" required>
<label for="first-name">First Name</label>
</div>
<div class="input_wrapper">
<input type="text" name="last-name" ng-model="newTech.lastName" required>
<label for="last-name">Last Name</label>
</div>
<div class="input_wrapper">
<input type="email" name="email" ng-model="newTech.email" required>
<label for="email">Email</label>
</div>
<div class="input_wrapper">
<input type="tel" name="phone" ng-model="newTech.phone" required>
<label for="phone">Phone</label>
</div>
<h5>Check all held ASE certifications</h5>
<ul class="list">
<li class="item item-checkbox" ng-repeat="certification in certifications">
<label class="checkbox">
<input type="checkbox" value="{{certification.name}}" ng-checked="selection.indexOf(certification.name) > -1" ng-click="toggleCert(certification)">
</label>
{{certification.name}}
</li>
</ul>
<button class="button button-block" ng-click="addTechnician()">Add Technician</button>
</form>
Ideally after the "Add Technician" button is clicked I want to end up with an object, my newTech ng-model object, that has the input field data and the properties from the checked items. Here is a jsFiddle with simplified code replicating the problem
http://jsfiddle.net/aq93z/7/
I solved it. I had to format my loop using angular.forEach(values, function(value, index){here is the jsFiddle with the solution http://jsfiddle.net/aq93z/9/. If you look at the $scope.newTech object created in the console the checkbox selections are added as properties of the newTech ng-model object.

Resources