I am new to angular and I realized that there is a lot of duplicated code in my templates, for example for a single field.
What is the best way to make the templates DRY. I would like to avoid to concatenate strings in some helper functions.
<div class="form-group">
<label for="Name">
{{'Name'|i18n}}: <span class="required">*</span>
</label>
<input type="text" class="form-control" ng-model="schema.Name" id="Name" name="Name" placeholder="{{'Name'|i18n}}" maxlength="100" required />
<div ng-show="(submitted || form.Name.$dirty) && form.Name.$invalid">
<span class="validation-text">{{'Required'|i18n:'i18n.Name'}}</span>
<span class="validation-arrow"></span>
</div>
<div class="form-hint">{{'NameHint':i18n}}</div>
</div>
For example, in ASP.NET MVC there are a lot of helper functions, would be nice to something similar, where I can use e.g.:
textBox('schema.Name', { required: true, maxlength')
hint('schema.Name')
You can use custom directives to reduce the repetition, at the cost of scattering. If really all you need is to include a snippet then the include directive should do the job.
<ng-include src="views/foo.html"/>
Related
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>      
<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
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.
Following this video tutorial, I am trying to bind three text inputs together.
But only the first get updated.
Here is my JsFiddle attempt
the index.html relevant part :
<body ng-app="">
<input type="text" placeholder="Your text" ng-model="data.message"></input>
<h2>{{data.message}}</h2>
<div ng-controller="FirstController">
<input type="text" placeholder="Your text" ng-model="data.message"></input>
<h2>{{data.message}}</h2>
</div>
<div ng-controller="SecondController">
<input type="text" placeholder="Your text" ng-model="data.message"></input>
<h2>{{data.message}}</h2>
</div>
</body>
script.js
function FirstController($scope){
}
function SecondController($scope){
}
What did I miss ?
Please also notice, that I do need the nested div tags and controller, as the purpose is to use scope inheritance.
Also, these global function definitions for the controllers are a choice, as I wanted to keep the video author method for now, in my real projects I will use the best practise which consist of using a module var controller() method).
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.
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