I'm beginner to AngularJs but now I have used and understand how AngularJs works quite a bit.
The thing which I want to ask about is the few lines in the ngModel directive documentation.
These lines are:
Note: ngModel will try to bind to the property given by evaluating the
expression on the current scope. If the property doesn't already exist
on this scope, it will be created implicitly and added to the scope.
I don't understand what they are trying to say. I do know that the ngModel directive binds a property to the input, select and textarea controls. Just like a very simple code below:
Name: <input type="text" ng-model="myName">
{{myName}}
So, can anybody come up with any other precise example which helps me to understand those lines?
ngModel is a standard way that Angular binds a scope property (usually declared in a controller) to the UI.
So, typically, a controller is created with the property declared inside:
angular.controller('Ctrl', function($scope) {
$scope.myProperty = '';
});
And you would bind it to the UI like this:
<div ng-controller="Ctrl">
<input type="text" ng-model="myProperty"/>
</div>
In this example $scope is scoped in the UI on the div element because the ng-controller attribute binds the Ctrl to that element.
However, Angular allows you to be a bit lazy as well and not bother defining the myProperty in the controller:
angular.controller('Ctrl', function($scope) {
});
And still using it in the UI:
<div ng-controller="Ctrl">
<input type="text" ng-model="myProperty"/>
</div>
In this case Angular will implicitly (dynamically) create a myProperty on $scope. That can be used in the UI inside the ng-controller's scope.
This can be used for UI only properties that you don't want to bother the controller about. An example would be when you want to hide/show something based on a user's interaction.
Hope this helps.
You have a controller with a $scope.
You can init your $scope with values(e.g.):
$scope.myName= 'test';
And then you input will have a default value of test.
Name: <input type="text" ng-model="myName">
{{myName}}
You can choose not to init your scope with the value, and then $scope.myName will be created behind the scenes and bound with no default value.
Let's break this down:
Note: ngModel will try to bind to the property given by evaluating the expression
on the current scope. If the property doesn't already exist on this scope, it
will be created implicitly and added to the scope.
1) ngModel will try to bind to the property given by evaluating the expression on the current scope.
So if you have the element:
<input ng-model="theProperty">
This element will have a scope:
var theScope = element.scope()
Most of the times this scope is accessed in a controller constructor... now, let's go back to the statement:
will try to bind to the property given... on the current scope.
Note that it says "try" because it is possible that the current scope contains the property or not. Because angular will do something like this:
theScope['theProperty'] = the_inputs_value;
When the property did not exist, the property gets created. So this explains the next statement.
If the property doesn't already exist on this scope,
it will be created implicitly and added to the scope.
Related
Here is my Plunk
I need to understand how AngularJS handles scope variable and method part of given scope.
Below is my controller code
var app = angular.module('plunker', []);
app.controller('MainCtrl', ["$scope", function($scope) {
$scope.name = "";
$scope.getNameLength = function(){
return $scope.name.length;
}
}]);
Here is my html body (just keeping my div for simplicity)
<body ng-controller="MainCtrl">
<div>
Enter Your Name :<input type="text" ng-model="name">
<br>
{{ "Your entered name whoes length is = " + getNameLength() }}
</div>
</body>
As and when i enter something in the text box, the getNameLength() is called and the DOM is updated to reflect the name's length.
As long as the method being referenced in a directive the method is called whenever there is a change in the name.
Here is my doubt:
Why angular calling all the method in the scope (which are being referenced in directive) whenever there is a change in view model? is it possible to disable this behavior? Are there any performance implication in this?
If you are concerned that Angular is calling your method too many times and you want to limit the execution, you can always use the ngModelOptions directive and pass in debounce. You can see the documentation on the AngularJS page. For example:
<input type="text" ng-model="name" ng-model-options="{debounce: 500}">
Will only update the model once the model has stopped updating for 500 milliseconds. You could also use something like ng-model-options="{updateOn: 'blur'}" to only update the model after the field has lost focus.
As far as performance is concerned, if it is something simple like calculating a string's length, you shouldn't have too much to worry about. If it is something more complex, you could run into issues.
Why angular calling all the method in the scope (which are being
referenced in directive) whenever there is a change in view model?
I do not see a custom directive in your example, but Angular directives will either inherit scope properties from their parent, use the parent scope, or have an isolate scope.
If you do not have an isolate scope, it will look for the property in the parent's scope unless you override it.
Because you have an Angular expression (the {{ and }} surrounds it), Angular makes a watcher for whatever is in the expression. When it detects a watched variable or object has changed, it will update all things dependant on it.
is it possible to disable this behavior?
Yes, indeed, as mentioned by 'YOU' in the comment to your question, you can use a 'one time binding'.
Example:
{{normalBinding}}
{{::oneTimeBinding}}
Are there any performance implication in this?
Yes, the more bindings you have, the more watchers, the more the digest cycles will take, the longer it will take for your application to reflect changes. This is a concern for big applications.
More information about the scope, and watchers, can be found here.
I just started AngularJS a few days ago and I've read that scopes are immediately updated whenever the value of their linked element changes. I have this HTML code with my controller:
<div ng-controller="lyricsMod">
<textarea ng-model="valueB"></textarea>
{{valueA}}
And my AngularJS controller:
myMod.controller('lyricsMod', function($scope) {
$scope.valueA = $scope.valueB;
});
However, this outputs nothing. But, changing the HTML code to:
<div ng-controller="lyricsMod">
<textarea ng-model="valueA"></textarea>
{{valueA}}
Produces the wanted result. Pretty sure it has nothing to do with the AngularJS and that its just linking two things together in HTML. I don't understand, if the scope is immediately updated, why is this not working?
try it:
u need to make use of $watch if you want change scope variable on change of other scope variable
myMod.controller('lyricsMod', function ($scope) {
$scope.$watch('valueB',function(){
$scope.valueA=$scope.valueB;
});
$scope.valueA=$scope.valueB;
});
scopes are immediately updated whenever the value of their linked
element changes
Yes but only scope variables bound using ng-model. In your fist example ValueB is bound using ng-model not valueA.If you want to change value of another scope variable when another changes(and its bound using ng-model). Use ng-change directive.
<div ng-controller="lyricsMod">
<textarea ng-model="valueB" ng-change="changeA()"></textarea>
{{valueA}}
$scope.changeA = function () {
$scope.valueA = $scope.valueB;
}
In my application I would like to preserve the option of using plain controllers for certain sections of code - as opposed to creating directives for one-off things that will never be re-used.
In these cases I often want to publish some data from the controller to be used in the contained section. Now, I am aware that I could simply bind items in the controller's scope, however I'd like to specify the "model" location explicitly just to make the code more maintainable and easier to read. What I'd like to use is ng-model as it would be used on a custom directive, but just along side my plain controller:
<div ng-controller="AppController" ng-model='fooModel'>
{{fooModel}}
</div>
However I can see no way to get a reference to the generated ngModelController without using a directive and the 'require' injection.
I am aware that I could make my own attribute fairly easily by injecting the $attr into my controller and do something like:
<div ng-controller="AppController" my-model='fooModel'>
{{fooModel}}
</div>
In which case I just manually take or parse the myModel value and stick my model into the $scope under that name. However that feels wrong in this case - I really only need one "model" for a controller and I'd prefer not to have to add this boilerplate to every controller when ngModel exists. (It's the principle of the thing!)
My questions are:
1) Is there some way to use ngModel along with a plain controller to get the effect above?
2) I have been trying to figure out where ngModelControllers are stored so that I could look at the situation in the debugger but have not been able to find them. When using an ngModel directive should I see these in the scope or parent scope? (Where do they live?!?)
UPDATE: As suggested in answers below $element.controller() can be used to fetch the controller. This works (http://plnkr.co/edit/bZzdLpacmAyKy239tNAO?p=preview) However it's a bit unsatisfying as it requires using $evalAsync.
2) I have been trying to figure out where ngModelControllers are stored so that I could look at the situation in the debugger but have not been able to find them. When using an ngModel directive should I see these in the scope or parent scope? (Where do they live?!?)
The answer depends slightly on where you want to access the controller from.
From outside the element with ng-model
It requires "name" attributes on both the element with the ng-model attribute, and a parent form (or ngForm). So say you have the form with name myForm and the element with ng-model attribute with name myInput, then you can access the ngModelController for myFoo from the parent scope as myForm.myInput. For example, for debugging purposes:
<p>myFoo: {{myForm.myInput.$modelValue}}<p>
<form name="myForm">
<div ng-controller="InnerController" name="myInput" ng-model="model.foo"></div>
</form>
as can be seen at http://plnkr.co/edit/IVTtvIXlBWXGytOEHYbn?p=preview
From inside the element with ng-model
Similar to the answer from #pixelbits, using $evalAsync is needed due to the order of controller creation, but you can alternatively use angular.element.controller function to retrieve it:
app.controller('InnerController', function($scope, $element) {
$scope.$evalAsync(function() {
$scope.myModelController = $element.controller('ngModel');
});
});
Used, inside the controller to view it, for debugging purposes, as:
<div ng-controller="InnerController" ng-model="model.foo">
<p>myFoo: {{myModelController.$modelValue}}<p>
</div>
As can be seen at http://plnkr.co/edit/C7ykMHmd8Be1N1Gl1Auc?p=preview .
1) Is there some way to use ngModel along with a plain controller to get the effect above?
Once you have the ngModelController inside the directive, you can change its value just as you would were you using a custom directive accessing the ngModelController, using the $setViewValue function:
myModelController.$setViewValue('my-new-model-value');
You can do this, for example, in response to a user action that triggers an ngChange handler.
app.controller('InnerController', function($scope, $element) {
$scope.$evalAsync(function() {
$scope.myModelController = $element.controller('ngModel');
});
$scope.$watch('myModelController.$modelValue', function(externalModel) {
$scope.localModel = externalModel;
});
$scope.changed = function() {
$scope.myModelController.$setViewValue($scope.localModel);
};
});
Note the extra watcher on $modelValue to get the initial value of the model, as well as to react to any later changes.
It can be used with a template like:
{{model.foo}}
<div ng-controller="InnerController" ng-model="model.foo">
<p><input type="text" ng-model="localModel" ng-change="changed()"></p>
</div>
Note that this uses ngChange rather than a watcher on localModel. This is deliberate so that $setViewValue is only called when the user has interacted with the element, and not in response to changes to the model from the parent scope.
This can be seen at http://plnkr.co/edit/uknixs6RhXtrqK4ZWLuC?p=preview
Edit: If you would like to avoid $evalAsync, you can use a watcher instead.
$scope.$watch(function() {
return $element.controller('ngModel');
}, function(ngModelController) {
$scope.myModelController = ngModelController;
});
as seen at http://plnkr.co/edit/gJonpzLoVsgc8zB6tsZ1?p=preview
As a side-note, so far I seem to have avoided nesting plain controllers like this. I think if a certain part of the template's role is to control a variable by ngModel, it is a prime candidate for writing a small directive, often with an isolated scope to ensure there are no unexpected effects due to scope inheritance, that has a clear API, and uses require to access the ngModelController. Yes, it might not be reused, but it does help enforce a separation of responsibilities between parts of the code.
When you declare directives on an element:
<div ng-controller="AppController" ng-model='fooModel'>
{{fooModel}}
</div>
You can retrieve the controller instance for any directive by calling jQlite/jQuery $element.data(nameOfController), where nameOfController is the normalized name of the directive with a $ prefix, and a Controller suffix.
For example, to retrieve the controller instance for the ngModel directive you can do:
var ngModelController = $element.data('$ngModelController');
This works as long as the ngModel directive has already been registered.
Unfortunately, ngController executes with the same priority as ngModel, and for reasons that are implementation specific, ngModel is not registered by the time that the ngController function executes. For this reason, the following does not work:
app.controller('ctrl', function ($scope, $element) {
var ngModelController = $element.data('$ngModelController');
// this alerts undefined because ngModel has not been registered yet
alert(ngModelController);
});
To fix this, you can wrap the code within $scope.$evalAsync, which guarantees that the directives have been registered before the callback function is executed:
app.controller('ctrl', function ($scope, $element) {
$scope.$evalAsync(function() {
var ngModelController = $element.data('$ngModelController');
alert(ngModelController);
});
});
Demo JSFiddle
I have an select field with an ng-model attribute in a tab within ui-bootstrap's tabset. There is also a button on the tab. On click of the button I would like to get the selected value in the model. I tried using
<select ... ng-model="selectedOption"></select>
<button ng-click="buttonClick()">Click</button>
and then in the controller
$scope.buttonClick = function() {
//try to access $scope.selectedOption
}
But that does not seem to contain the value. I tried looking into the $scope variable and it seems to contain the selectedOption inside something called $$childTail
Is there any other way to access the selectedOption value or should I change the structure of my view?
I have a Plunker here of what I'm trying to do.
Both Mathew Berg's and Bumblebee Man's answers should work but they do not explain what's happening.
Okay basically it goes down like this:
The tabset directive that you are using does transclusion so the selectedOption is actually in an inner scope(tabset's scope), not in your TabsDemoCtrl's cope. This answer explains what transclusion does, and how you can access an transcluded model.
Transclusion makes shallow copies of your models, so even if you init selected option like $scope.selectedOption = "5"; in your TabsDemoCtrl, your select will show 5 as selected at the begining but changes will not be reflected to your TabsDemoCtrl controller's scope because in the tabset's child scope 5 is just copied, and the one being updated is in the tabset's scope not your scope.
You can also use an object for your select's model. Since the transclusion creates a shallow copy, objects/arrays/functions are copied by reference and variables are copied by value. This is also why you can access your TabsDemoCtrl's button click from the inner scope.
updated plnkr with another alternative solution(the object version) :
created an object in your controller
$scope.selection = {};
and used in model like this
ng-model="selection.option"
http://plnkr.co/edit/5sI0arorV9dULzaDxRra?p=preview
Angular ui tab/tabset directive create new scopes, so in order to access the parent scope (What you're trying to do) just attach $parent
<select name="selectedCampaign" ng-model="$parent.selectedOption" ng-options="option for option in options"></select>
Updated plunkr: http://plnkr.co/edit/BgMMn4tJRWm72ZXeQG3N
Edit: Apologies, I misread the question, can't you just do this?
<select ... ng-model="selectedOption"></select>
<button ng-click="buttonClick(selectedOption)">Click</button>
$scope.buttonClick = function(selectedOption) {
selectedOption.whatever...
}
I think I'm missing something simple (and important) here. I'm using an included template that contains an input that's mapped to some value:
<div ng-controller="Ctrl">
<div ng-include src="'template.html'"></div>
</div>
<!-- template -->
<script type="text/ng-template" id="template.html">
<input ng-model="testvalue" />
</script>
Controller:
function Ctrl($scope) {
$scope.testvalue= "initial value";
}
Alerting the value of $scope.testvalue always shows the initial value, not the updated value (when you type in the input). Help me Obi-Wan. You're our only hope.
Fiddle: http://jsfiddle.net/h5aac/
This is the all too common of binding to a primitive instead of an object. The value of the string gets passed around and not a reference to an object. If you use an object instead of a primitive, it works fine. Something like this in your scope.
$scope.foo = {testvalue: "initial value"};
See http://jsfiddle.net/h5aac/2/
Also:
Using `ng-model` within a transcluded directive in AngularJS
binding issue when a directive in a ngRepeat
AngularJS - updating scope value with asynchronous response
I'm sure there are more...
An alternative to referencing an object property in the parent scope is to use $parent to access the primitive in the parent scope:
<input ng-model="$parent.testvalue" />
ng-include creates a child scope. That scope prototypically inherits from Ctrl's parent scope. Here's how the 3 variations work:
$parent.testvalue ties the model to the property in the parent scope
testvalue by itself ties the model to a new property that will be created on the child scope. This property "shadows/hides" the parent scope property by the same name.
foo.testvalue (e.g., see #dnc253's answer) also ties the model to a parent property. It works like this: Javascript doesn't see/find 'foo' in the child scope, so it looks for it in the parent scope (due to prototypical inheritance) and finds it there.
To see what the child scope looks like, use your original fiddle, and add this code to your template somewhere:
<a ng-click="showScope($event)">show scope</a>
And add this code to your Ctrl:
$scope.showScope = function(e) {
console.log(angular.element(e.srcElement).scope());
}
Before you type into the textbox, click the "show scope" link. In the console (I'm using Chrome), you can expand the "Child" scope and see it does not contain a testvalue property yet (which I find surprising, because I don't know how it is displaying the "initial value" in the textbox). You can expand the $parent and you'll see the testvalue property there -- a property with this name appears to only be in the parent scope at this point.
Now, clear the console, type into the textbox, and click the "show scope" link again. You'll see that the "Child" scope now has a new testvalue property. It shadows/hides the parent property. So, things in the child scope see the child scope testvalue property, and things in the parent scope see the parent scope testvalue property.
Update: FYI, I recently wrote an extensive answer/article about scope prototypical inheritance that explains the above concepts in much more detail, with lots of pictures: What are the nuances of scope prototypal / prototypical inheritance in AngularJS?