$watch not detecting model change with checkbox - javascript

I'm trying to do something relatively simple in angular and I can't figure out why it's not working. Ultimately I just want to perform one function when a checkbox is checked and then a different function when it's unchecked.
So I tried to use ng-model to evaluate foo to true or false if the checkbox is checked or unchecked and then I could do an if statement to pick a function. The {{foo}} in my view switches to true/false but the console.log always says undefined. Anyone know why?
HTML:
<div ng-repeat="element in elements">
<input type="checkbox" ng-model="foo" /> {{foo}}
</div>
Script:
$scope.$watch('foo', function(){
console.log($scope.foo);
});
EDIT: Oops! I had it in a repeat. Is there a way to achieve this effect inside a repeat?

What you did
Your watcher is outside the ngRepeat. Your ngModel is inside the ngRepeat.
What is wrong
The inner ngModel creates a foo property on the inner scope. Your watcher reads the not existing foo property of the outer scope.
Solution
Have a model object for example, attached to your outer scope
$scope.model = { foo: false };
Have the inner ngModel set to model.foo.
What is the difference ?
Using this solution, the ngModel gets the right model object because of scope inheritance, what it does not try to do when assigning the foo property without traversing the prototype chain in what you wrote.

Related

Updating Controller Object Properties in the console

Not sure if this is possible, but I have some content with an ng-if that I want to test by updating a property on one of the controller's variables within the console.
<div class="manager-only" ng-if="teamSchedule.userObject.isManager">
<a href="#add-game">
<input class="btn btn-success" type="button" value="Add New Game" />
</a>
</div>
As you can probably surmise, the userObject has an isManager property that is a boolean. On page load, it's initially set to false. I'm trying to update this within the console to test the ng-if.
I found that I could add window.teamSchedule = teamSchedule; to the controller in order to have a reference to it and its variables within the console, and this does allow me to change the object properties of the userObject, it appears. However the ng-if does not seem to respond to the change.
I've tried changing the ng-if to an ng-show and that didn't seem to work either. Anyone have a way to make this work or know if it's not possible?
Thanks!
First explanation why it doesn't work.
You change the Object out of the AngularJs scope, so Angular doesn't realize that you changed something.
If you don't use production mode on your development system, you can use this to access the scope.
angular.element($0).scope()
$0 is the last element selected in the chrome element inspector.
Try to access your object this way, if this does not trigger the changes, try to use
scope.$apply(function() { your modification })

Angular changing parent model from transclude scope

I'm confused from angular transclude scope. I'm trying to make let say collapsible directive. But binding inside the transclude scope will not change model of parent unless I use some object for the model eg. data.
<div>
data.prop: {{data.prop}} <br>
prop: {{prop}}
<collapsible>
data.prop: <input type="text" ng-model="data.prop" /> <br> // WILL CHANGE PARENT
prop: <input type="text" ng-model="prop" /> // WONT CHANGE PARENT
</collapsible>
</div>
I already read this topic and still I don't get it why I must use prefix to the model.
Confused about Angularjs transcluded and isolate scopes & bindings
Working example at http://plnkr.co/edit/z3IvR1a37jdNRCJWG0Yq?p=preview
In my app I'm using object for forms, so it works fine but I just want to know why is that.
When you use an object to bind to the model, your object is passed to the different scope as an reference, not a copy, in Javascript objects are passed to functions as a reference. In that case it will still reference to previous scope.

Can we use more than one controller in same div

How can I use two different controllers in the same div element?
How does one access the parent controller model data from inside a child controller?
e.g
<div ng-controller="abc">
<div ng-controller="def"><span>{{name}}</span></div>
</div>
Suppose the model name is in controller abc then how does one access its value?
AngularJS automatically look up from the first $scope to the parent till the $rootScope to find the property matching name.
Thus to answer your question, if the controller def doesn't have a property named name, it is already visible. Otherwise, you should use:
{{$parent.name}}
In your HTML code
The correct way to do this is by having an alias. See Plunker
In your html it would look like
<div ng-controller="Ctrl as Alias">
{{Alias.foo}}
In your javascript it would look like
function Ctrl(){
this.foo = "bar"
}

Angularjs - ng-model undefined

I am building a rather complex directive in which I need access to ng-model on a specific element in the template which is enclosed into ng-if directive... I have a plunker and the explanation on how to see the issue is below.
In the .compile function of the directive I have a line of code that looks like this
ngModel = elm.find('textarea').controller('ngModel');
console.log(ngModel);
In my template, I have a something like this:
<div ng-if="views.view">
<textarea ng-model="exp.val"></textarea>
</div>
When I use ng-show my console.log gets an object of ng-model, no problem ...
The requirement is very strict however and I have to use ng-if. When I use ng-if my console log gets undefined
The actual working version of the problem exists in this plunker
If you change the line 6 in template.html into ng-if you can see the behavior.
How do I have to write this line to retrieve the model when inclosed in ng-if.
ngModel = elm.find('textarea').controller('ngModel');
I also tried using attach-if directive by Simon Sparks. This directive is pretty cool, it preserves the scope unlike ng-if so if you specifically need to not render HTML but you need to preserve scope it works great.
I still have the same problem with invoking ngModel as I am doing it but because of applying custom filters in the directive I have to update ng-model in this way.
I am this one step away from finishing this directive. Hoping someone can help.

AngularJS - ng-repeat and ng-show

I got the following code:
<div class="map" ng-controller="DealerMarkerListCtrl">
<a ng-click="showdetails=!showdetails" href="#/dealer/{{marker.id}}" class="marker" style="left:{{marker.left}}px;top:{{marker.top}}px" ng-repeat="marker in dealer"></a>
</div>
and this view:
<div ng-show="showdetails" class="alldealermodal">
<div ng-view></div>
</div>
This same "ng-show" stuff is working properly with just one link outside of the ng-repeat but in this ng-repeat it isn't working.
The link shall open an overlay. The ng-view works too.
What am I missing?
Since ngRepeat creates a new scope the showdetails being referenced outside of your ng-repeat is not the same instance as the showdetails being updated in your repeated ng-click.
You can see this post for more details but one way around the new scope(s) is to bind to an object property instead of a primitive type.
This fiddle shows a small example binding to details.show instead of showdetails with:
$scope.details = { show: true };
"What am I missing?"
As #Gloopy already mentioned, you probably didn't realize that ng-repeat creates child scopes (one for each item). In addition, an understanding about how JavaScript prototypal inheritance works is also necessary because each child scope prototypically inherits from the same parent scope, and that affects how JavaScript finds (or creates) properties on scopes.
Note that a number of Angular built-in directives create child scopes: ng-repeat, ng-include, ng-switch, ng-controller, directives (can, but may not). For (much) more information about how prototypal inheritance works, why it is a problem when trying to bind to primitives, and how it relates to Angular scopes, see here.
To extend #Gloopy's answer, there are two other alternatives you could consider:
use $parent inside the ng-repeat to bind to the parent scope property (instead of creating child scope properties):
<a ng-click="$parent.showdetails=!$parent.showdetails" ...
define a method on the parent scope and call it, thereby also changing a parent scope property (rather than a child scope properties):
<a ng-click="toggleDetails()" ...

Categories