I'm currently learning AngularJS and am having difficulty understanding the difference between ng-bind and ng-model.
Can anyone tell me how they differ and when one should be used over the other?
ng-bind has one-way data binding ($scope --> view). It has a shortcut {{ val }}
which displays the scope value $scope.val inserted into html where val is a variable name.
ng-model is intended to be put inside of form elements and has two-way data binding ($scope --> view and view --> $scope) e.g. <input ng-model="val"/>.
tosh's answer gets to the heart of the question nicely. Here's some additional information....
Filters & Formatters
ng-bind and ng-model both have the concept of transforming data before outputting it for the user. To that end, ng-bind uses filters, while ng-model uses formatters.
filter (ng-bind)
With ng-bind, you can use a filter to transform your data. For example,
<div ng-bind="mystring | uppercase"></div>,
or more simply:
<div>{{mystring | uppercase}}</div>
Note that uppercase is a built-in angular filter, although you can also build your own filter.
formatter (ng-model)
To create an ng-model formatter, you create a directive that does require: 'ngModel', which allows that directive to gain access to ngModel's controller. For example:
app.directive('myModelFormatter', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, controller) {
controller.$formatters.push(function(value) {
return value.toUpperCase();
});
}
}
}
Then in your partial:
<input ngModel="mystring" my-model-formatter />
This is essentially the ng-model equivalent of what the uppercase filter is doing in the ng-bind example above.
Parsers
Now, what if you plan to allow the user to change the value of mystring? ng-bind only has one way binding, from model-->view. However, ng-model can bind from view-->model which means that you may allow the user to change the model's data, and using a parser you can format the user's data in a streamlined manner. Here's what that looks like:
app.directive('myModelFormatter', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, controller) {
controller.$parsers.push(function(value) {
return value.toLowerCase();
});
}
}
}
Play with a live plunker of the ng-model formatter/parser examples
What Else?
ng-model also has built-in validation. Simply modify your $parsers or $formatters function to call ngModel's controller.$setValidity(validationErrorKey, isValid) function.
Angular 1.3 has a new $validators array which you can use for validation instead of $parsers or $formatters.
Angular 1.3 also has getter/setter support for ngModel
ngModel
The ngModel directive binds an input,select, textarea (or custom form control) to a property on the scope.
This directive executes at priority level 1.
Example Plunker
JAVASCRIPT
angular.module('inputExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.val = '1';
}]);
CSS
.my-input {
-webkit-transition:all linear 0.5s;
transition:all linear 0.5s;
background: transparent;
}
.my-input.ng-invalid {
color:white;
background: red;
}
HTML
<p id="inputDescription">
Update input to see transitions when valid/invalid.
Integer is a valid value.
</p>
<form name="testForm" ng-controller="ExampleController">
<input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
aria-describedby="inputDescription" />
</form>
ngModel is responsible for:
Binding the view into the model, which other directives such as
input, textarea or select require.
Providing validation behavior (i.e. required, number, email, url).
Keeping the state of the control (valid/invalid, dirty/pristine,
touched/untouched, validation errors).
Setting related css classes on the element (ng-valid, ng-invalid,
ng-dirty, ng-pristine, ng-touched, ng-untouched) including
animations.
Registering the control with its parent form.
ngBind
The ngBind attribute tells Angular to replace the text content of the specified HTML element with the value of a given expression, and to update the text content when the value of that expression changes.
This directive executes at priority level 0.
Example Plunker
JAVASCRIPT
angular.module('bindExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.name = 'Whirled';
}]);
HTML
<div ng-controller="ExampleController">
<label>Enter name: <input type="text" ng-model="name"></label><br>
Hello <span ng-bind="name"></span>!
</div>
ngBind is responsible for:
Replacing the text content of the specified HTML element with the
value of a given expression.
If you are hesitating between using ng-bind or ng-model, try to answer these questions:
Do you only need to display data?
Yes: ng-bind (one-way binding)
No: ng-model (two-way binding)
Do you need to bind text content (and not value)?
Yes: ng-bind
No: ng-model (you should not use ng-bind where value is required)
Is your tag a HTML <input>?
Yes: ng-model (you cannot use ng-bind with <input> tag)
No: ng-bind
ng-model
ng-model directive in AngularJS is one of the greatest strength to bind the variables used in application with input components. This works as two way data binding. If you want to understand better about the two way bindings, you have an input component and the value updated into that field must be reflected in other part of the application. The better solution is to bind a variable to that field and output that variable whereever you wish to display the updated value throughoput the application.
ng-bind
ng-bind works much different than ng-model. ng-bind is one way data binding used for displaying the value inside html component as inner HTML. This directive can not be used for binding with the variable but only with the HTML elements content. Infact this can be used along with ng-model to bind the component to HTML elements. This directive is very useful for updating the blocks like div, span, etc. with inner HTML content.
This example would help you to understand.
angular.module('testApp',[]).controller('testCTRL',function($scope)
{
$scope.testingModel = "This is ModelData.If you change textbox data it will reflected here..because model is two way binding reflected in both.";
$scope.testingBind = "This is BindData.You can't change this beacause it is binded with html..In above textBox i tried to use bind, but it is not working because it is one way binding.";
});
div input{
width:600px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<head>Diff b/w model and bind</head>
<body data-ng-app="testApp">
<div data-ng-controller="testCTRL">
Model-Data : <input type="text" data-ng-model="testingModel">
<p>{{testingModel}}</p>
<input type="text" data-ng-bind="testingBind">
<p ng-bind="testingBind"></p>
</div>
</body>
ngModel usually use for input tags for bind a variable that we can change variable from controller and html page but ngBind use for display a variable in html page and we can change variable just from controller and html just show variable.
We can use ng-bind with <p> to display, we can use shortcut for ng-bind {{model}}, we cannot use ng-bind with html input controls, but we can use shortcut for ng-bind {{model}} with html input controls.
<input type="text" ng-model="name" placeholder="Enter Something"/>
<input type="text" value="{{name}}" placeholder="Enter Something"/>
Hello {{name}}
<p ng-bind="name"</p>
Related
Why if you choose on the panel of a rating value, then changing the model and the field value select. But if on the contrary, choose select, then duplicate values on the panel rating is not happening?
Html code:
<select name="post[vote][cinema]" ng-model="cinema" ng-change="set(cinema)">
<p class="dash ng-isolate-scope ng-valid ng-dirty" ng-mouseleave="reset()"
tabindex="0" role="slider" aria-valuemin="0" aria-valuemax="10"
aria-valuenow="4" ng-model="cinema" max="max">
<a ng-repeat="r in range track by $index" ng-mouseenter="enter($index + 1)"
ng-click="rate($index + 1)" ng-class="{...}" class="ng-scope"></a>
Fiddle: http://jsfiddle.net/smtncL3q/1/
I've managed to get this working by injecting ngModel in the rating directives scope, and assigning a watch on the value.
scope: {
ngModel: '='
},
... and then within the link function:
scope.$watch('ngModel', function(value) {
scope.value = value;
});
Updated Fiddle
It was a bit hard for me to grasp how your JSFiddle exactly works, but I suggest you to use $parsers and $formatters on your ngModel. The parser function updates the model when you click on a rating and the formatter function updates the rating view value when the model is updated by the select.
Your solution could be fixed with more hackish code, but it will not improve the readibility.
My company has theme requirements and has asked me to do some research on AngularJs. To that end, I'd like to show how Angular can be used to implement our theme.
Here is a plunk of what I have so far.
Here is the HTML of what I have that works great.
<form name="frmLogin" ng-submit="submit()" novalidate>
<div class="form-field" data-ng-class="{'error': frmLogin.txtLastName.$invalid && frmLogin.txtLastName.$dirty && !frmLogin.txtLastName.$focused}">
<label for="txtLastName" class="required">Email</label>
<input id="txtLastName" name="txtLastName" type="text" data-ng-model="user.lastName" required autofocus ng-focus />
<div class="inline-validation" data-ng-show="frmLogin.txtLastName.$invalid && frmLogin.txtLastName.$dirty && !frmLogin.txtLastName.$focused">
{{getError(frmLogin.txtLastName.$error,
{
required: "LastName is required"
}
)}}
</div>
</div>
</form>
I'm working on a directive to simplify the HTML for the developer.
Here's my idea.
<form name="frmUserName" novalidate>
<ff-text-input control-name="txtFirstName" control-label="First Name" ng-model="user.userEmail"></ff-text-input>
</form>
Being new to this, I'm struggling with the directive.
I don't know how to grab the whole frmLogin.txtLastName.$invalid && frmLogin.txtLastName.$dirty && !frmLogin.txtLastName.$focused so the developer doesn't have to.
I'm able to determine the form name and control name by using ctrl.$name + "." + scope.controlName but I'm lost on how to apply that to the $invalid etc...
Thanks,
Duane
Since, you want your directive to interact with form states, you need to have a form controller in your directive.
You can do this with
app.directive('inputValidate', function(){
return {
restrict: 'A',
scope: true,
require: '^form',
The require tell Angular that your directive depends on the form, so Angular moves up the DOM, until it finds a form, and when it finds a form, it passes a controller to the directive and you can receive that controller like this.
link: function(scope, element, attrs, formController){
Here formController refers to the controller of the form.
Now, for the your last query, suppose you have an input box with type defined as employeeID, with some custom rule plus juggling, you are able to conclude that input is wrong. To inform Angular that this input is wrong, you can use $setValidity method of the formController.
The syntax is like
formController[controlName].$setValidity(str, value);
where controlName is the name of the control, str is the field for which validation has failed, and value would be either true or false depending upon the your validation state.
Depending upon your validation state and key, Angular will apply fallowing class to the element. For example, if your key in empId and input is invalid, the in class attribute of the element, you can see this.
class = "ng-invalid-empId"
And if it is valid and you have told angular with formController using this.
formController[controlName].$setValidity('empId', true);
You would have something like this.
class="ng-valid-empId"
what would be the way to handle being able to change the expression from the text field and the other way around as well. For example:
myApp.controller('MainContrller', ['$scope', function($scope) {
$scope.inputs = {
'input1': 'text'
}
}]);
Then in the HTML:
<div ng-controller="MainController">
<input type="text" ng-model="inputs.input1">
<p>This will output {{ inputs.input1 }}</p>
</div>
This works well when the binding goes from the input to the expression, but I am also using contenteditable and would like for the expression in there to replace what is in the input box as well. Possible?
Look at the content editable example of ngModelController documentation, under section "Custom Control Example"
https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
Basically it creates directive to bind the ng-model field to changes in content editable. You can then use the same ng-model expression to bind the contenteditable and input so changes are in sync.
I am using ng-pattern to validate some form fields, and I am using ng-change with it to watch and process any changes, however ng-change (or $scope.$watch) will only fire when the form element is in the $valid state! I'm new to angular, so I don't know how to solve this issue, although I suspect a new directive is the way to go.
How can I get ng-change to fire in both $invalid and $valid form element states, with ng-pattern still setting the form element states as before?
Html:
<div ng-app="test">
<div ng-controller="controller">
<form name="form">
<input type="text" name="textbox" ng-pattern="/^[0-9]+$/" ng-change="change()" ng-model="inputtext"> Changes: {{ changes }}
</form>
<br>
Type in any amount of numbers, and changes should increment.
<br><br>
Now enter anything that isn't a number, and changes will stop incrementing. When the form is in the $invalid state, ng-change doesn't fire.
<br><br>
Now remove all characters that aren't numbers. It will increment like normal again. When the form is in the $valid state, ng-change will fire.
<br><br>
I would like ng-change to fire even when the the form is $invalid.
<br><br>
form.$valid: <font color="red">{{ form.$valid }}</font>
</div>
</div>
Javascript:
angular.module('test', []).controller('controller', function ($scope) {
$scope.changes = 0;
$scope.change = function () {
$scope.changes += 1;
};
});
I have created a working JS Fiddle which shows the problem I am having.
http://jsfiddle.net/JAN3x/1/
By the way, this angular issue also seems to be relevant:
https://github.com/angular/angular.js/issues/1296
You can change the behavior of your input by using ng-model-options.
Just add this attribute to your input and the ng-change event will fire:
ng-model-options="{allowInvalid: true}"
see: https://docs.angularjs.org/api/ng/directive/ngModelOptions
you just need to add
ng-model-options="{ updateOn: 'default' , allowInvalid:'true'}"
this indicates that the model can be set with values that did not validate correctly instead of the default behaviour.
Edit This was answered when ng-model-options was not available. Please see the top-voted answer.
you can write a simple directive to listen input event.
HTML:
<input type="text" name="textbox" ng-pattern="/^[0-9]+$/" watch-change="change()" ng-model="inputtext"> Changes: {{ changes }}
JS:
app.directive('watchChange', function() {
return {
scope: {
onchange: '&watchChange'
},
link: function(scope, element, attrs) {
element.on('input', function() {
scope.$apply(function () {
scope.onchange();
});
});
}
};
});
http://jsfiddle.net/H2EAB/
Inspired by the Li Yin Kong ingenious solution :
His solution has an issue concerning the ndModel update (see the comments of his post).
My fix essentially changes the scope type of the directive. It lets directive access to controller scope (and methods)
Then, watch-change directive does not need an "instruction to eval" (change()) anymore, but only the "name of the controller method to call" (change).
And to get the new value of the input in this function, I pass the context (this = the input itself). So I can get the value or any property of it.
This way, we don't care about ngModel updates (or if the form is invalid, which was another issue of the initial solution : ngModel is deleted if form is invalid)
HTML :
<input type="text" name="textbox" ng-pattern="/^[0-9]+$/" watch-change="change" ng-model="inputtext">
JAVASCRIPT :
app.directive('watchChange', function() {
return {
restrict : 'A',
link: function(scope, element, attrs) {
element.on('input', function(){
scope[attrs.watchChange](this);
})
}
};
});
DEMO : http://jsfiddle.net/msieurtoph/0Ld5p2t4/
I coded the following in my form:
<td><input type="text" ng-model="row.title" /></td>
When I look at my DOM with Chrome developer tools I see the following:
<input type="text" ng-model="row.title" class="ng-pristine ng-valid">
How can I make it so that when there is a change made to the input that the input has a class added to it?
There are two good ways to approach this problem:
1. Use the built-in ng-dirty class that Angular puts on the element.
When you change an input managed by Angular, it adds some CSS classes to the input for various states. These include:
ng-pristine - the input has not been modified
ng-dirty - the input has been modified
So, if you can modify your CSS to be based off the .ng-dirty class, you're good to go.
2. Use a form directive with the $dirty flag.
When you use a form element, Angular assigns a FormController instance on the scope with the same name as the name attribute on the form; each input inside the form gets attached to that FormController instance as a property, again with the same name as the name attribute on the input. For example,
<form name="myForm">
<input type="text" name="myInput">
</form>
gives you
$scope.myForm.myInput
Each input property has some of its own properties on it, including $pristine and $dirty; these work just like the CSS classes listed above. Thus, you can check for the $dirty flag on the input and use ng-class to conditionally apply a class to the element. An example:
<div ng-controller="MainController">
<form name="myForm">
<input name="myInput" ng-model="model" ng-maxlength="3"
ng-class="{changed: myForm.myInput.$dirty}">
</form>
</div>
You can find a working example here: http://jsfiddle.net/BinaryMuse/BDB5b/
Take a look at this jsfiddle: http://jsfiddle.net/hNrEV/2/
The main idea is using $scope.$watch to watch for changes to the input box. I gave it an id of rowTitle, and used a directive called watchRowTitle that watches for changes to $scope.row.title, and adds a class 'red' that colors the text red whenever the text in the input box is equal to 'wrong title'.
It is probably good practice to do DOM manipulation in directives. Here, the watchRowTitle directive returns an object with 4 keys:
template - the html that replaces the watch-row-title tag. we dont need this here
scope - Here we make use of an isolated scope. Basically, the '=' establishes a 2-way data binding between between scope.title inside the watch-row-title directive and the $scope.row.title value inside the MyCtrl controller.
restrict - We give it a value of E, which stands for element. So this restricts the use of the watch-row-title directive within html tags, in other words: <watch-row-title></watch-row-title>
link - this is the link function, where the interesting stuff happens. In here, we use scope.$watch on title. We have to supply a function with 2 parameters newValue and oldValue (you can name them to something else, but naming them this way is more meaningful), that holds the new and old values of the variable being watched. Whenever the scope.title variable becomes the string 'wrong title', it adds the CSS class 'red' to the input box with id rowTitle (notice how the text in the input box turns red). Otherwise, it removes that CSS class. This portion is done using JQuery.
HTML:
<div ng-app="myApp" ng-controller="MyCtrl">
<input id="rowTitle" type="text" ng-model="row.title" class="ng-pristine ng-valid" />
<watch-row-title title="row.title"></watch-row-title>
</div>
CSS:
.red {
color: red;
}
JavaScript:
angular.module('myApp', [])
.controller('MyCtrl', [
'$scope',
function ($scope) {
$scope.row = {};
}
])
.directive('watchRowTitle', [
function () {
return {
template: '',
scope: {
title: '='
},
restrict: 'E',
link: function(scope, element, attr) {
scope.$watch('title', function(newValue, oldValue) {
if (newValue === 'wrong title') {
$('#rowTitle').addClass('red');
} else {
$('#rowTitle').removeClass('red');
}
});
}
};
}
]);
HTML
<input type="text" id="inputTitle" ng-model="row.title" />
JS
$scope.$watch('row.title', function(newValue) {
// Add CSS class on input
$('#inputTitle').addClass('YourCSSClass');
}, true);