Angular form validation using the controllerAs syntax - angularjs

I am currently facing the following problem:
I would like to validate my form input using the Angular ngModel directives.
When using those together with $scope they work fine.
Now, working with the controllerAs syntax, they fail to work.
This problem is poorly documented, the only help I could find is this article.
Here is a small example of my code:
The template gets called with myController as vm
<form name="vm.signUpForm" ng-submit="vm.signup(vm.user)">
<label for="name">Name</label>
<input type="text"
class="form-control"
id="name"
name="name"
placeholder="Full name"
ng-model="vm.user.name"
ng-minlength="2" required />
<div ng-show="vm.signUpForm.$submitted || vm.signUpForm.name.$touched">
<span ng-show="vm.signUpForm.name.$error.required">Please fill in your name</span>
<span ng-show="vm.signUpForm.name.$error.minlength">A minimum of 2 [...]</span>
</div>
[...]
</form>
Am I forced to use $scope to validate the form? Or did I miss something ?
Thanks in advance!
Solution by: Andrew Gray
I had to change the following lines to get this to work:
<form name="vm.signUpForm" ... >
<!-- To -->
<form name="signUpForm" ...>
<div ng-show="vm.signUpForm.$submitted || vm.signUpForm.name.$touched">
<!-- To -->
<div ng-if="signUpForm.name.$invalid">
<span ng-show="vm.signUpForm.name.$error.required" ... >
<!-- To -->
<span ng-show="signUpForm.name.$error.required" ... >

First things first - you don't need the vm. on the form.
<form novalidate name="someForm">
<label>
Some Text:
<span class="danger-text" ng-if="someForm.someText.$invalid">
ERROR!
</span>
</label>
<input type="text" name="someField" />
</form>
The way it winds up working, is that there's a validation object that is not tied to the scope. The controllerAs syntax is just spinning off a instance of the controller as a scope variable.
Try shaving off the vm. from the form and child elements' names, and you should be OK.

Related

ng-show doesn't work in form

I create form and use AngularJs. I need display errors and I have a problem.
My expression for ng-show doesn't work.
My code:
<form name="createProductForm" ng-submit="createProduct(product)" novalidate>
<input type="text" ng-model="product.name" ng-minlength="3" required> <br>
<p ng-show="createProductForm.product.name.$error.required && createProductForm.product.name.$dirty">Nazwa produktu jest wymagana.</p>
<p ng-show="createProductForm.product.name.$error.minlength && createProductForm.product.name.$dirty">Nazwa produktu jest zbyt krótka.</p>
<button type="submit" ng-disabled="createProductForm.$invalid">Dodaj produkt</button>
</form>
Your input tag MUST have a name attribute. ngModel does not provide validation states.
<input name="nameAttributeHere"/>
<p ng-show="createProductForm.nameAttributeHere.$error.required && createProductForm.nameAttributeHere.$dirty">...</p>
ngModel and form validation states are completely separate directives.

using ng-mask for phone

I still have doubt in using ng-mask, I went through most of the web pages for it to work but it still remains the same. And many people told to use it with input tag of angularjs, after doing so Im not able to mask the input.Or am I making mistake please somebody correct me and give the clarity of using the ng-mask.
<input type="tel" name="phoneno" maxlength=13 ng-model="phone.number" ng-mask="(999)999-9999"/>
</div>
<button class="button2" ng-click="home()">Back</button> &nbsp &nbsp &nbsp
<button class="button3" ng-click="addphone()">Add</button>
Download the ngMask.min.js from net
Call the ngMask.min.js before app.js and Include app.js module
var yourApp= angular.module("yourApp", [
'ngMask'
]);
HTML :
<input type="tel" name="phoneno" maxlength=13 ng-model="phone.number" mask="(999)999-9999" />
Below code is working for me.
I think you should use data-on attribute like below
<input type="text" class="form-control input-small" data-ng-model="zipcode" placeholder="Enter the zip code" data-ng-mask="#####-###" data-on="keyup">
// 2. Add ngMask module dependency to your app.
angular.module('yourApp', ['ngMask']);
<!-- 1. Add ngMask plugin after your AngularJS. -->
<script src="angular.min.js"></script>
<script src='ngMask.min.js'></script>
<!-- 3. Use the avaiable patterns to create your mask. Set the mask attribute. -->
<input type='text' ng-model='maskModel' mask='39/19/9999' />
<!-- 4. Adjust your mask options. -->
<input type='text' ng-model='maskModel' ng-value='0/3/9' mask='3/9?' mask-repeat='2' mask-restrict='accept' mask-clean='true' mask-validate='false' mask-limit='false' />
source: https://github.com/candreoliveira/ngMask/blob/master/README.md
Use ui-mask instead:
<div class="form-group">
<label for="birth-date">Date of birth</label>
<input type="text"
class="form-control"
id="birth-date"
ng-model="formData.date_of_birth"
ng-value="formData.date_of_birth"
ui-mask="99.99.9999"
ui-mask-placeholder
ui-mask-placeholder-char="_"
>
</div>
https://github.com/angular-ui/ui-mask

How to set mask/pattern for a phonenumber textbox as xxx-xxx-xxxx in angularJS

I have a textbox for phonenumber and I need to enter the phonenumber in xxx-xxx-xxxx format in the textbox.
I have tried below examples but thy are not working
<input type='text' ng-model='nerecord.phonenumber' mask='xxx-xxx-xxxx'/>
<input type='text' ng-model='nerecord.phonenumber' ng-pattern='/^[1-10]\d{2}([.-]?)\d{3}\1\d{4}$/'/>
Can anyone help me. How to do this?
Seems your regex isn't correct.
try this one /^\d{3}-\d{3}-\d{4}$/
I don't see anything to handle the case your phone number is invalid.
try to render something like that :
<form name="myForm" ng-submit="yourSaveFunction()">
<p ng-class="{ error: !myForm.phoneNumber.$valid }"> <!-- You must handle your css with this class -->
<label ng-if="!myForm.phoneNumber.$valid">Phone Number not OK</label>
<input name="phoneNumber" type='text' ng-model='nerecord.phonenumber' ng-pattern='someRegex'/> <!-- idk anything about regex -->
</p>
<p>
<button type="submit" ng-disabled="myForm.$invalid">Submit</button>
</p>
UI Mask example:
<input ng-model="nerecord.phonenumber" ui-mask="999-999-9999" type="text"/>
Also if you have to validate the input then there is an option in config to prevent clearing the value on blur.
clearOnBlur: false
Mask.js

AngularJS - how to sync result of calculated input field to a scope variable

I'm trying to sync the result of a calculated form field into a scope variable. For example:
<div class="form-group">
<label for="val1" class="control-label">Val 1</label>
<input class="form-control" name="val1" type="number" ng-model="data.val1" id="val1">
</div>
<div class="form-group">
<label for="val2" class="control-label">Val 2</label>
<input class="form-control" name="val2" type="number" ng-model="data.val2" id="val2">
</div>
<div class="form-group">
<label for="score" class="control-label">Score</label>
<input class="form-control" name="score" value="{{data.val1+data.val2}}" type="text" id="score">
</div>
<br/>data: {{data}}
How can I sync the result (i.e. the score field) into the scope variable $scope.data.score? I have tried ng-model="data.score" but that breaks the calculation.
You can see the example in action here:
http://plnkr.co/edit/fc9XcyyYGtAk0aGVV35t?p=preview
How do I get the last line to read data: {"val1":1,"val2":2,"score":3}?
Note that I'm looking for a solution that involves minimal to no code support at the controller level. For example, I know you can set up a watch in the controller for both val1 and val2 and then update the score in the watcher. This is what I wanted to avoid, if it's possible in angular at all. If it's not (theoretically) possible, I'd really appreciate an explanation of why it's not.
A quick background might help. Basically we have a simple form builder app that defines a form and all its fields in an xml file. Here's a sample of what the xml would look like:
<form name="test">
<field name="val1" control="textbox" datatype="number">
<label>Val 1</label>
</field>
<field name="val2" control="textbox" datatype="number">
<label>Val 2</label>
</field>
<field name="score" control="textbox" datatype="number">
<label>Score</label>
<calculate>[val1]+[val2]</calculate>
</field>
</form>
When a form is requested, the system will need to pick up the xml, loop through all the fields and generate an angular style html to be served to the browser and processed by angular. Ideally, I want to keep all the form specific logic (validation, show/hide, calculation etc) confined to the html, and keep the controller (js) logic generic for all forms.
The only solution I can come up with is to dynamically load watcher functions, through something like this: eval("$scope.$watch('[data.val1,data.val2]')..."), but as I said, I really want to avoid this, because it's just tedious, and feels extremely dodgy :)
The first dirty way.
In your case you can move all logic from controller into html with using combination of ng-init and ng-change directives.
<div class="form-group">
<label for="val1" class="control-label">Val 1</label>
<input class="form-control" name="val1" type="number" ng-model="data.val1" ng-change="data.score = data.val1 + data.val2" id="val1">
</div>
<div class="form-group">
<label for="val2" class="control-label">Val 2</label>
<input class="form-control" name="val2" type="number" ng-model="data.val2" ng-change="data.score = data.val1 + data.val2" id="val2">
</div>
<div class="form-group" ng-init="data.score = data.val1 + data.val2">
<label for="score" class="control-label">Score</label>
<input class="form-control" name="score" ng-model="data.score" type="text" id="score">
</div>
<br/>data: {{data}}
I don't think that it's the clearest solution, but you can leave your controller without any changes with it.
Demo on plunker.
The second dirty way.
There is one more way, but now you don't need ng-init and ng-change directives. You can add just one dirty expression in html:
<div id="main-container" class="container" style="width:100%" ng-controller="MainController">
{{data.score = data.val1 + data.val2;""}} <!-- key point -->
<div class="form-group">
<label for="val1" class="control-label">Val 1</label>
<input class="form-control" name="val1" type="number" ng-model="data.val1" id="val1">
</div>
<div class="form-group">
<label for="val2" class="control-label">Val 2</label>
<input class="form-control" name="val2" type="number" ng-model="data.val2" id="val2">
</div>
<div class="form-group">
<label for="score" class="control-label">Score</label>
<input class="form-control" name="score" ng-model="data.score" type="text" id="score">
</div>
<br/>data: {{data}}
;"" in expression stops evaluating of angular expression to text in html.
Demo on plunker.
See if this works, in your HTML change,
<input class="form-control" name="score" ng-model = "data.score" type="text" id="score">
and, in your controller do,
var myApp = angular.module('myapp', [])
.controller('MainController', function($scope) {
$scope.data = { val1: 1, val2: 2, score: 3};
$scope.$watch('[data.val1,data.val2]', function (newValue, oldValue) {
$scope.data.score = newValue[0] + newValue[1];
}, true);
})
Demo plunk, http://plnkr.co/edit/gS0UenjydgId4H5HwSjL?p=preview
If you want to know how you can do it, then i have one solution for you make a ng-change event for both of your text box and sum both the number there and use ng-model in the third text box then, you can see it will work as per your need.
For the first time load you need to calculate it out side only.

Validating nested form in angular

Having this ordinary (name attribute is requred by server) form with angular and can't figured out how to make validations work. What should i put into ng-show="TODO"
http://jsfiddle.net/Xk3VB/7/
<div ng-app>
<form ng-init="variants = [{duration:10, price:100}, {duration:30, price:200}]">
<div ng-repeat="variant in variants" ng-form="variant_form">
<div>
<label>Duration:</label>
<input name="variants[{{$index}}][duration]" ng-model="variant.duration" required />
<span ng-show="TODO">Duration required</span>
</div>
<div>
<label>Price:</label>
<input name="variants[{{$index}}][price]" ng-model="variant.price" />
<span ng-show="TODO">Price required</span>
</div>
</div>
</form>
</div>
ps: this is just piece of form, which is more complicated
Thanks
AngularJS relies on input names to expose validation errors.
Unfortunately, as of today it is not possible (without using a custom directive) to dynamically generate a name of an input. Indeed, checking input docs we can see that the name attribute accepts a string only.
Long story short you should rely on ng-form to validate dynamically created inputs. Something like :
<div ng-repeat="variant in variants" >
<ng-form name="innerForm">
<div>
<label>Duration:</label>
<input name="duration" ng-model="variant.duration" required />
<span ng-show="innerForm.duration.$error.required">Duration required</span>
</div>
<div>
<label>Price:</label>
<input name="price" ng-model="variant.price" required/>
<span ng-show="innerForm.price.$error.required">Price required</span>
</div>
</ng-form>
Working fiddle here
UPDATE : Base on your serverside requirement why not do something like that :
<input type="hidden" name="variants[{{$index}}][duration]" ng-model="variant.duration"/>
<input name="duration" ng-model="variant.duration" required />
The hidden input will be the one read by the server while the other one will be used to do the client side validation (later discarded by server). It s kind of an hack but should work.
PS : Be sure that your form is valid before actually submitting it. Can be done with ng-submit

Resources