Variable defined on the $scope not updating until next cycle - javascript

Here is a plunker - http://plnkr.co/edit/PaG1k5N37BTOflObnN7K?p=preview
Scenario 1:
Step 1 - When "m" is entered in the tags input box, Marie is displayed as a suggestion.
Step 2 - When "j" is entered, John appears as a suggestion.
Step 3 - When "m" is entered again, there are no suggestions. I have implemented this functionality in script.js. This works perfectly as, Marie is being removed from $scope.to because it is already added to the tags input box in step 1.
Scenario 2:
Step 1 - When "m" is entered in the tags input box, Marie is displayed as a suggestion.
Step 2 - When "m" is entered again, Marie still appears as a suggestion.
So, until John or Ghita are entered in the tags input box, Marie keeps appearing as a suggestion. As soon as John is entered (e.g. the above scenario) Marie is not displayed as a suggestion anymore.
Does anyone have ideas as to why this behavior is caused?
I tried to use $scope.$apply(), but it gives me an error that says, a $digest cycle is already in progress. So, when $scope.$digest() is already executing, why is the $scope.to variable not getting updated?

You are running into an issue with the Typeahead library. You will want to wrap your tag added callback in a $timeout call.
$scope.$on("decipher.tags.added", function(info, obj) {
$timeout(function(){
tagAdded(info, obj);
});
});
Will fix the issue. You can see that the angular-tags directive itself does this when filtering the internal tag list:
https://github.com/boneskull/angular-tags/blob/master/src/tags.js#L69
Edit:
Here's a working fork of your original Plunker.

I think the problem is the angular-tags library itself:
https://github.com/boneskull/angular-tags/issues/28
Looks like it's not being updated for versions of angular greater than 1.2.0-rc.2

As stated by another user, it seems as though this is a bug with the current version of the tags you're using. A temporary workaround is to use $compile to update the tags in tagAdded. To use it, include $compile as a parameter in your emailViewController along with $scope and inject it as a dependency at the bottom along with $scope. After that, you can call
$compile(document.getElementsByTagName("tags")[0])($scope);
to update it. A side effect of this is that the input area loses focus each time a tag is added, which may or may not make a large impact in the usability of your app.
Refer to http://plnkr.co/edit/UVELXnrjOKasRnuGVA0e?p=preview for a demonstration of using $compile

You can try to use a $timeout $timeout(function(){//your code}); In angular $timeout forces your digest to update your variables, and it doesn't thrown a digest already in progress error.

try this:
if(!$scope.$$phase) {
$scope.$digest();
}
$scope.$$phase is a flag set to true while angular is in the $digest cycle

Related

ng-repeat with ng-selected

I am trying to figure out how to generate list of option elements using ng-repeat, but one of them to be marked as the selected option on load.
After googling, what I found is this base, which I modified by adding the selected property to the data objects
https://plnkr.co/edit/7g4MeAQnG4PzpnrebBGc?p=preview
However, it seems that ng-selected="option.selected == true" has no effect :(
Why? I also have the more complex example here: http://jsfiddle.net/ej5fx3kr/14/ which works, although I am not sure what is the difference, or what is the model here used for (note: changing the model name from "program" to anything, it still works... so not sure what is the purpose).
Bonus points: How do I debug code in AngularJS in directives? Like experiment in debug mode line by line to actually see what are the variable values in that particular scope, what is available to use, etc...
My ultimate goal in this question, is to load list of values via ajax on page load in the controller, IF there is a routeParam in the URL, find it in the list of loaded values, and add selected attribute, then set selected=true in the generated HTML on page load, otherwise not pre-select anything in the populated select box on the page load, so this is why its important for me to understand this on the simplest example before trying to plug this in.
Thanks!
The "Angular Way" to handle this is to use ng-options instead of ng-repeat, and then simply set the model equal to the default value on controller load.
For example:
<select ng-options="option.name for option in data.availableOptions"
ng-model="selectedItem"></select>
$scope.selectedItem=$scope.data.availableOptions[2];
For a more advanced case, where your ng-model might not be the object in the array, but a single property, you can use a modified version of ng-options:
<select ng-options="option.id as option.name for option in data.availableOptions"
ng-model="selectedId"></select>
$scope.selectedId = '2';
Here is a modified version of your plunker showing these different possibilities: https://plnkr.co/edit/xzYmXf8C3WuZaelwj5hO?p=preview
Using ng-selected = true inside ng-repeat would solve this .
However be sure of the data that you call in ng-repeat.For additional debugging and experiment line by line use chrome debugger .go to source tab and set break points on the js lines that you need to debug.This will let you debug line by line by pause and play . Hope this helps.Thanks

Not able to make two way data binding work on an array

I am trying to create kind of a drag and drop playground using AngularJS.
The code is on plunker. And to understand the question, mostly you'll need to see the plunker.
https://plnkr.co/edit/i82UOOqHRSJyEPN9293E
I am trying to create node like structures here which can be dragged over to the playground on the right side.
On drag, I am adding the node id to the nodeChain variable in $scope.
Now I am iterating the nodeChain array in UI with ng-repeat where I am drawing the node just to check that the nodes added to the chain are reflecting correctly.
However,
I do not see the nodeChain updating from watchCollection logs as well as the nodes drawn on the playground are not updating when new nodes are added.
Can someone please tell me why the binding is not taking effect?
Thanks in advance!
You need to trigger a $digest cycle to update the values. When the $digest cycle starts, it fires each of the watchers. These watchers check if the current value of the scope model is different from last calculated value. If yes, then the corresponding listener function executes. As a result if you have any expressions in the view they will be updated.
if (!Bounds.within(event.pageX, event.pageY, leftPane)) {
scope.$parent.nodeChain.push(NodeChain.getNodeFromId(attr.nid))
scope.$apply();
}
Working Plunker:https://plnkr.co/edit/SNNsVOZps5Fy35dAQIqk?p=preview
You are getting ng-repeat dupes error in your console because there are duplicate values in your array nodeChain.
If i understand your question correctly, you want to add the dropped nodes in the below white space. Please see the plunkr.
You should add $scope.apply() when you change the models outside the scope.

KendoUI Switch control with AngularJS

I am trying to use kendo UI's switch control (http://demos.telerik.com/kendo-ui/web/mobile/switch.html) with angularjs. I am having problem that the value is not being bound with model. I am using it like this:
<input type="checkbox" id="locked" kendo-mobile-switch on-label="Yes" off-label="No" ng-model="Model.IsLocked" checked="{{Model.IsLocked}}" data-role="switch">
Basically the variable in model keep the value received from db irrespective of the state on UI.
Second problem I am having is with on and off labels that it keeps on displaying default On and Off.
I opened an issue on the github site and a fix was applied. See this link:
"I pushed a fix, it should work now if you use k-ng-model. The plain ng-model is still broken."
https://github.com/kendo-labs/angular-kendo/issues/333
The second problem (the on/off labels) is because it should be:
k-on-label="'Yes'" k-off-label="'No'"
Note the string literals, otherwise it is interpreted as a variable.

Why is Angular.js calling my function so frequently with ng-change and how do I make it call only once per change?

I'm building my first proper angular.js app after going through many tutorials. I've hit a problem with the ngChange directive. I'm trying to use it to call a function each time the value of a drop down list is changed by the user. I find that it calls the function multiple times upon page load and also multiple times each time the option is selected.
I've put together this jsfiddle which demonstrates the problem I'm having.
I'd like to know why it's exhibiting this behaviour and how I can achieve my desired outcome of one function call per option change and no calling of change() on page load. (this second criterion is less critical for my app, but I would like to know how to suppress this behaviour nonetheless).
I've reproduced the code below for those of you who may be able to find an immediate error.
HTML
<body ng-app ng-controller="Controller">
<div>
<h2>Number of changes: {{numOfChanges}}</h2>
<select ng-change="{{change()}}" ng-model="currentSelection">
<option ng-repeat="option in options">{{option}}</option>
</select>
</div>
</body>
JavaScript
Controller = function($scope) {
$scope.numOfChanges = 0;
$scope.change = function() {
$scope.numOfChanges++;
}
$scope.options = ["do", "ray", "me", "far", "so", "la", "tee","dah"]
}
I'm aware this could well be due to my improper use/understanding of the angular methodology. I'd appreciate answers that address all the failings of this small example.
Anything inside ng-change is already recognised by angular, so you don't need to wrap it in {{ }}. Just ng-change="change()" will do.
Wrapping it in braces will cause angular to evaluate it on page load and any time it updates the view, hence it firing multiple times.
Remove {{ }} from your ng-change() code
<select ng-change="change()" ng-model="currentSelection">
{} are in case of the bindings so you are firing change event so binding is not there.
You better look at this ngChange documentation
Updated Fiddle
The answers of DKM and jonnyynnoj already explain what you have to do to make the ng-change work.
The reason why in your example the change listener is firing 10 times is because the "digest" cycle is updating 10 times and then aborting as explained here: http://docs.angularjs.org/api/ng.$rootScope.Scope#$digest
The $digest() is called directly by AngularJS. Here is the explanations from the docs which explains it:
Processes all of the watchers of the current scope and its children.
Because a watcher's listener can change the model, the $digest() keeps
calling the watchers until no more listeners are firing. This means
that it is possible to get into an infinite loop. This function will
throw 'Maximum iteration limit exceeded.' if the number of iterations
exceeds 10.
In the javascript developer console you can also see that your example throws the following error: Error: 10 $digest() iterations reached. Aborting!

AngularJS: Update template binding via directive

I modified the AngularUI-Bootstrap direvtive for tooltips to help me display error messages, here is what I did so far: http://jsfiddle.net/distractedBySquirrels/TaAHZ/
But I have a problem with the dynamic messages. The first time you mouseover the first input field, the message is in the wrong place, this is because the model is not correctly updated yet. Of course, after the second mouseover the tooltip is in the right place. But if the message to display changes again the same failure occurs.
I am using $parse to update the template:
$parse('tt_content').assign( scope,
getErrorMessage( ctrl.$error ) || attrs.tooltip
);
Seems like not all the listeners have revceived and updated and "ttHeight" and "ttWidth" are still set to the "old" values. The correct update of the message/model only works with the $observe, which is illustrated by the third example.
I am stuck and don't know what I did wrong? :(
The problem is that when you modify your models with $apply, you need to manually activate the digest cycle in order to notify all watchers of such models. So basically, you need to add scope.$digest(); whenever you change the scope doing things like scope.$apply( show );
I modified your code with my suggestions here http://jsfiddle.net/TaAHZ/2/

Categories

Resources