how does binding to a service method work? - angularjs

In this first example, the the view is updated everytime the return value of the method sessionStatus() of userService changes.
but If a change the sessionStatus method like in the second example :
sessionStatus: function(){
return Date.now();
},
it no longer refreshes "live" in the view. I would expect to see the date in milliseconds progressing in the view.
Is it because the second example, the value returned by the sessionStatus method is changing to fast ? Why isn't it updating the view ?

The value is not updating because there is no AngularJS event to cause a digest cycle:
Add a button that has an ng-click and the value will update everytime it is clicked:
<body ng-app="myServiceModule">
<div id="simple" ng-controller="MyController">
<p>I would expect the number bellow to be ticking</p>
<p>Session Status: {{sessionStatus()}}</p>
</div>
<button ng-click="">Update</button>
</body>
By clicking on the Update button, an AngularJS digest cycle gets initiated and the session status updates.
The DEMO on PLNKR.
Or use the $interval service to initiate a repeating digest cycle:
angular.
module('myServiceModule', []).
controller('MyController', function ($scope, $interval, userService) {
$scope.sessionStatus = function(){
return userService.sessionStatus();
};
$interval(null, 250);
})
In this example, the $interval statement starts a digest cycle every 250 milliseconds.
The DEMO on PLNKR

Related

Angular: ng-show and functions

Say you have a template like
<a ng-show=function()>a link</a>
My question is: when is function run? How can I tell angular that it is time to re-run the function?
Well, ng-show takes a Boolean value, so your best option for using it is to call your function somewhere in the logic that sets ng-show.
$scope.show = false;
$scope.showSomething = function(myCondition){
if(myCondition){
myFunction();
$scope.show = true;
}
};
<div ng-show='show'></div>
Any expression in ng-show is evaluated at every digest cycle, so the function is run on every digest cycle as well. Thus any change to the value returned by the function, will reflect in the view. If your function makes any changes to the application state that triggers a new digest cycle, you may run into an infinite digest loop (which isn't great).
So be careful about using functions in directives like ng-show. If the function is pure though (it doesn't have any side-effects), you should be safe using it.
Here's a plunker that shows the function being called on every digest.
(Note: in the plunker example, clicking the button actually triggers two digest cycles, since the click event triggers one, and toggling the $scope variable triggers another.)
ng-show="booleanVar" takes a condition or a Boolean value . You can change the value of condition expression to hide and show values at run time .
But if you want some function to be called when ng-show is triggered then you can use $watch in your model .
<div ng-app="myApp" ng-controller="myCtrl" ng-init="isDisplayed = true">
<div ng-show="isDisplayed">something</div>
<button ng-click="isDisplayed = !isDisplayed">Toggle</button>
</div>
var myApp = angular.module('myApp', [])
.controller('myCtrl', function($scope, $log) {
$scope.$watch('isDisplayed', function(newValue, oldValue) {
if (newValue !== oldValue) {
$log.log('Changed!');
}
});
});
See details here

Not seeing scope in directive

I have a controller that passes two variables to my view via $scope. I can display those variables in the view.
In my view I have a directive, where I pass in the two variables to the directive scope.
Here is my directive:
.directive('notes', function(){
return {
restrict: 'E',
templateUrl:'views/directives/notes-tpl.html',
scope:{
time:'=',
unit:'='
},
controller:['$scope',function($scope){
console.log('Current Time Stamp: ' + $scope.time);
console.log('Current Unit Stamp: ' + $scope.unit);
};
}]
};
})
and here is my html:
<notes time='currentTimeStamp' unit='currentUnitStamp'></notes>
Problem is my console.log() shows them as undefined.
I'm calling the directive from a modal:
<!-- NOTES MODAL -->
<script type="text/ng-template" id="notesModal.html">
<div class="modal-header">
<a class="close" data-dismiss="modal" aria-label="Close" ng-click="close()"><span aria-hidden="true">×</span></a>
<h3 class="modal-title">Notes</h3>
</div>
<notes currentTimeStamp='{{currentTimeStamp}}' currentUnitStamp='{{currentUnitStamp}}'></notes>
</script>
After seeing your example a new guess:
That's your HTML for calling the directive
<notes currentTimeStamp='{{currentTimeStamp}}' currentUnitStamp='{{currentUnitStamp}}'></notes>
But your notes.js hasn't any binding for this.
return {
...
scope: {
currentTimeStamp="",
currentUnitStamp=""
}
...
}
You need to pass this into your directive like this:
<notes current-time-stamp='currentTimeStamp' current-unit-stamp='currentUnitStamp'></notes>
Pay attention to the current-time-stamp instead of currentTimeStamp.
Check out the Normalization section.
https://docs.angularjs.org/guide/directive
I hope this one will solve your issue.
Another guess:
Could it be you are updating "out of angular" in pure JS.
So angular doesn't know it has to update your values.
Let's say you would update some values on the click Event. Now we are using angulars ng-click for updating and calling a method (so angular would kick of the digest cycle by itself and would update the values).
Like this: ng-click="updateTimeStamp()
Now for some reason we don't use angular, but instead a native event listener:
element.addEventListener("click", updateTimeStamp);
So if we want now update the scope and its values, we have to kick of the digest cycle for example with a $timeout service.
More Information about the digest cycle: https://docs.angularjs.org/guide/scope
For verification put a $timeout(angular.noop); at the end of the listener where you set the values.
Example:
myElement.addEventListener("click", function () {
var newTime = takeMeFromSomewhere;
$scope.time = newTime;
// Angular doesn't know to update the binding!
$timeout(angular.noop);
// now you kicked off the digest cycle and angular will update the values
});

updating $scope value on $rootScope.$on doesnt update DOM (UI) [duplicate]

I'm trying to making some custom elements with AngularJS's and bind some events to it, then I notice $scope.var won't update UI when used in a binding function.
Here is a simplified example that describing the probelm:
HTML:
<!doctype html>
<html ng-app="test">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="Ctrl2">
<span>{{result}}</span>
<br />
<button ng-click="a()">A</button>
<button my-button>B</button>
</div>
</body>
</html>
JS:
function Ctrl2($scope) {
$scope.result = 'Click Button to change this string';
$scope.a = function (e) {
$scope.result = 'A';
}
$scope.b = function (e) {
$scope.result = 'B';
}
}
var mod = angular.module('test', []);
mod.directive('myButton', function () {
return function (scope, element, attrs) {
//change scope.result from here works
//But not in bind functions
//scope.result = 'B';
element.bind('click', scope.b);
}
});
DEMO : http://plnkr.co/edit/g3S56xez6Q90mjbFogkL?p=preview
Basicly, I bind click event to my-button and want to change $scope.result when user clicked button B (similar to ng-click:a() on button A). But the view won't update to the new $scope.result if I do this way.
What did I do wrong? Thanks.
Event handlers are called "outside" Angular, so although your $scope properties will be updated, the view will not update because Angular doesn't know about these changes.
Call $scope.$apply() at the bottom of your event handler. This will cause a digest cycle to run, and Angular will notice the changes you made to the $scope (because of the $watches that Angular set up due to using {{ ... }} in your HTML) and update the view.
This might be also a result of different problem but with the same symptoms.
If you destroy a parent scope of the one that is assigned to the view, its changes will not affect the view in any way even after $apply() call. See the example - you can change the view value through the text input, but when you click Destroy parent scope!, model is not updated anymore.
I do not consider this as a bug. It is rather result of too hacky code in application :-)
I faced this problem when using Angular Bootstrap's modal. I tried to open second modal with scope of the first one. Then, I immediately closed the first modal which caused the parent scope to be destroyed.
use timeout
$timeout(function () {
code....
},
0);

How do I get my changes to persist in Angular using 2-way Data Binding with an onDrag callback?

I am trying to update a span's text after a call back function in Angular.
Here is my HTML:
<div ng-controller="onDragController">
<div id="draggableArea">
<div id="rectangle1" data-drag="true" jqyoui-draggable="{onDrag: 'dragCallback'}" data-jqyoui-options="{containment: '#draggableArea'}"></div>
</div>
<div>
<span ng-model="rectangleOne">{{rectangleOne.leftOffset}}</span>
</div>
</div>
And my controller is:
var App = angular.module('drag-and-drop', ['ngDragDrop', 'ui.bootstrap']);
App.controller('onDragController', function($scope) {
$scope.rectangleOne = {};
$scope.rectangleOne.leftOffset = 'ASDF';
$scope.dragCallback = function (event, ui) {
$scope.rectangleOne = {leftOffset: '12345'};
};
});
If I toss an alert in my callback function then I am seeing that the leftOffSet is updated, but on my HTML page the {{rectangleOne.leftOffset}} is staying the same.
What am I missing here...?
Use $apply in your dragCallback as follows:
$scope.dragCallback = function(event, ui) {
$scope.$apply(function() {
$scope.rectangleOne = {
leftOffset: '12345'
};
});
};
This will update your leftOffset in the scope. Here is a Plunker.
It might be helpful to better understand how Angular does two-way data binding. This blog post on how apply works, and why one would be motivated to use it, is pretty good.
In summary, for your changes to $scope.rectangleOne:
You can call $scope.$digest() every time you make a simple change like this. You need to do this so Angular knows to check if your bound data got updated.
Alternatively, you can use $scope.$apply, make the changes to $scope.rectangleOne inside a callback to $apply. It's doing the same thing, $apply ends up calling $digest() indirectly.
In code:
$scope.$apply(function() {
$scope.rectangleOne.leftOffset = '12345';
});
Hope that helps solve your problem, and add a bit of understanding to what's happening behind the scenes!

How does AngularJS respond to a form value being updated in Javascript?

I don't use Angular regularly, but I understand that one of the key features is that when data is updated on a form element, it is automatically updated in the model.
If you are instead using a library like jQuery, you must manually attach an event to the form input that updates the model when it is changed, as in $('#myInput').on('change', updateModel);
Although the above handler will be fired when myInput is changed by the user, it will not be fired if myInput is changed by Javascript code such as $('#myInput').val('hello world');
My question is, how does Angular know when a form input is changed in Javascript code?
Angular applies a scope digest every time it's needed (by an Angular function) during which it checks the states of all the scope variables, including the models used, of course.
If you modify some of those variables manually, using JavaScript, jQuery, etc... Angular will not know that the changes have occured and you need to tell it so either by doing $scope.$apply() or by wrapping the code block in a $timeout callback (these are the most commonly used methods).
If you don't do it manually, you'd have to wait for some (if any) other Angular event to trigger the digest cycle, which is never good.
See this example, note how nothing happens when you just update the value, but you need to do it manually (ng-click does it) in order for DOM to update:
angular.module('app', [])
.controller('Ctrl', function($scope){
$scope.ourValue = 'Initial Value';
window.exposedFunc = function(v, digest) {
$scope.ourValue = v;
if (digest) {
$scope.$apply();
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="Ctrl">
<button onclick="exposedFunc('First Button Value')">Update Value - No Digest</button>
<button onclick="exposedFunc('Second Button Value', true)">Update Value - Force Digest</button>
<button ng-click="">Force Digest only</button>
<p>{{ourValue}}</p>
</div>
Here's a super simple example of binding using keyup event. It should be enough to get you started on your projects:
var res = document.getElementById('r');
function handleChange(v) {
res.textContent = v;
}
<input onkeyup="handleChange(this.value)" type="text" value="Initial value" />
<p id="r">No binding yet</p>

Resources