how does this break the watching value? - angularjs

$scope.$watch('num',function(){
$scope.nums.push($scope.num)
})
Changing the above code breaks the watching value on clicked (I'm unclear because breakit still pushing the value to nums):
$scope.breakit = $scope.$watch('num',function(){
$scope.nums.push($scope.num)
})
But I'm unclear how does this break the watching value?
Full code:
<div ng-controller="MainController">
<div>Num: {{num}}</div>
<div>Nums: {{nums}}</div>
<button ng-click="increment()">Increment</button>
<button ng-click="breakit()">Break It</button>
</div>
var app = angular.module('app',[]);
app.controller('MainController',function($scope){
$scope.num = 0
$scope.nums = []
$scope.increment = function(){
$scope.num++;
}
$scope.breakit = $scope.$watch('num',function(){
$scope.nums.push($scope.num)
})
});
So, using $scope.breakit why does this stops pushing the num to nums clicking to break it button and continue clicking to increment button?
You may check this video.

The documentation for $scope.watch() says:
Returns
function() Returns a deregistration function for this listener
When you call $scope.watch(), it returns a function that you can call at any time to unregister it.
Your controller assigns that function to $scope.breakit, so when $scope.breakit is called, the watch is turned off.
Edit:
There was a question in the comments about how $scope.break pushes values to the nums array. The answer is that it doesn't. $scope.watch() takes care of that all on its own, and $scope.break is used as a way to turn that process off.

Related

ng-bind value not updating second time when updating from controller

i am trying to bind value in label when i get signal in session.
for first it shows values in label but when i send signal second time it shows blank label and is not updating value.
code i am using
HTML
<div class="modal-content">
<h4>Incoming Call From...</h4>
<label class="alignCenter incomingReason">{{requestedCall.reason}}</label>
<label class="alignCenter">{{requestedCall.hname}}</label>
</div>
Controller
session.on("signal:chat", function (event) {
var data = JSON.parse(event.data);
$scope.requestedCall.reason = data.complain_name;
$scope.requestedCall.hname = data.username;
$scope.PlayRingtone();
$scope.$apply();
$scope.showModel();
});
when i send signal , i gets data in var data = JSON.parse(event.data);
i always have to use $scope.$apply() other wise it doesn't bind value for first time also but for second time it doesn't work at all.
i have tried $timeout , $digest but nothing works at all , any help would be appreciated.
When we are out of angular, things doesn't work. So if you are doing something in jQuery or any other library angular will not know what has been changed. For that you've to tell angular explicitly by using $apply or $digest
$scope.$apply(function () {
$scope.requestedCall.reason = data.complain_name;
$scope.requestedCall.hname = data.username;
$scope.PlayRingtone();
$scope.showModel();
});
Let me know if it works!

How to get Angular to increment counter while alert popup is visible?

In this fiddle (http://jsfiddle.net/edwardtanguay/6pn8tb83/1), Angular increments a number every second, yet when an alert window pops up, the counting stops and when I close the alert window, I get the error
Error: [$rootScope:inprog] $apply already in progress
http://errors.angularjs.org/1.2.1/$rootScope/inprog?p0=%24apply
This error link actually takes me to a page which seems to be explaining what I need to do, but since I'm not explicitly using $scope.$apply() or $scope.$digest(), I don't understand what I need to do so that Angular simply continues to increment and show the incremented number while the alert window is popped up.
<div ng-controller="MyCtrl">
<div>Angular is counting: {{counter}}</div>
<button ng-click="processFiles()">processFiles</button>
<div>{{message}}</div>
</div>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope, $interval) {
var theTimer = $interval(function () {
$scope.counter++;
}, 1000);
$scope.counter = 0;
$scope.message = 'click button';
$scope.processFiles = function() {
alert('ok');
}
}
This is the default behaviour. Script execution stops when browser dialogues are open.
Workaround is to get the time difference between the alert and updating the counter with it, another one is to create your own modal

AngularJS - Can't cancel $interval after initiating it twice

I'm new to programming, and have recently been playing around with AngularJS.
To practice, i've decided try and create a simple stopwatch.
Starting with an initial 'time' value of 0, i'm using $interval to increment the 'time' by 0.01, every 10 milliseconds. I can start and stop the stopwatch without any issues, UNLESS i click 'Start' twice. After doing so, 'Stop' no longer works.
I'm sure this is an awful way to create a stopwatch, but regardless, the issue still remains.
My html contains the timer and 'Start' and 'Stop' buttons, like so:
<div class="row" style="margin-left: 20px" ng-controller="timerCtrl">
<b>{{time | number : 2}}</b>
<button ng-click="startTimer()">Start</button>
<button ng-click="stopTimer()">Stop</button>
</div>
And the js:
.controller('timerCtrl', ['$scope', '$timeout', '$interval',
function($scope, $timeout, $interval) {
$scope.time = 0;
$scope.startTimer = function() {
$scope.counter = $interval(function(){
$scope.time += 0.01;
}, 10)
}
$scope.stopTimer = function() {
$interval.cancel($scope.counter);
}
}
])
What's the best way to solve this? Any help will be much appreciated, thanks!
The problem is that $interval returns a promise, and each time you run $scope.startTimer you are creating a new promise. When you run it a second time, it doesn't cancel the previous promise, it just re-assigns $scope.counter to the new promise.
Check out the AngularJS $interval page to see their example and method of avoiding this problem.
A simple solution is to check to see whether the var you're assigning your promise to has already been defined when you are about to create a new promise. For example:
if ( angular.isDefined($scope.counter) ) return;
You can also use a boolean var to maintain the status as to whether it has been started or not if you prefer that.

angularJS $broadcast and $on

is there a way to make the $broadcast propagate the variable to the $on during initialization phase?
<div ng-app='test'>
<div ng-controller='testCtrl'> <span>{{testContent}}</span>
</div>
<div ng-controller="testCtrl2">
<input type='text' ng-change="updateContent()" ng-model="testContent2" />
</div>
</div>
var app = angular.module('test', []);
app.factory('sharedContent', function ($rootScope) {
var standardContent;
var resizeProportion;
return {
setStandardContent: function (newStandardContent) {
standardContent = newStandardContent;
$rootScope.$broadcast('updateContent');
console.log('broadcast');
},
getStandardContent: function () {
return standardContent;
},
setResizeProportion: function (newResizeProportion) {
$rootScope.$broadcast('updateResizeProportion');
},
}
});
app.run(function (sharedContent) {
sharedContent.setStandardContent('haha');
});
function testCtrl($scope, sharedContent) {
$scope.testContent;
$scope.$on('updateContent', function () {
console.log('receive');
$scope.testContent = sharedContent.getStandardContent();
});
}
function testCtrl2($scope, sharedContent) {
$scope.testContent2 = 'test';
$scope.updateContent = function () {
sharedContent.setStandardContent($scope.testContent2);
};
}
Sample fiddle : http://jsfiddle.net/jiaming/NsVPe/
The span will display the value as the input changes, which is due to the ng-change function.
However, at initialization phase, the value "haha" was not propagated to the $scope.testContent and thus, nothing was shown at first runtime. Is there a way to make the value "haha" appear at the first runtime?
Thank you.
Just provide a little delay using $timeout function. Just update the code in the factory it will start working.
Please refer the code below for the factory:
app.factory('sharedContent', function ($rootScope,$timeout) {
var standardContent;
var resizeProportion;
return {
setStandardContent: function (newStandardContent) {
standardContent = newStandardContent;
$timeout(function(){
$rootScope.$broadcast('updateContent');
},0)
console.log('broadcast');
},
getStandardContent: function () {
return standardContent;
},
setResizeProportion: function (newResizeProportion) {
$rootScope.$broadcast('updateResizeProportion');
},
}
});
The reason for this is that the ng-change triggers upon subsequent changes to the model identified by testContent2. When the controller initializes, the value "test" is assigned to it. ng-change then keeps a track of subsequent changes - the initial assignment does not qualify for this, only subsequent changes do.
http://jsfiddle.net/vZwy4/ - I updated the fiddle provided by you. Here you can see that the span tag is correctly populated with the data.
What you needed to do was instead of using ng-change, you should use the scope's $watch functionality. So remove the ng-change directive from the input box and remove the updateContent method. Instead, replace it with the following code wherein you watch the changes to the testContent2 model:
$scope.$watch('testContent2', function () {
if ($scope.testContent2 === undefined || $scope.testContent2 === null) {
return;
}
sharedContent.setStandardContent($scope.testContent2);
});
You can now see that the word "test" (I could not find anything to do with 'haha') appears the moment the page loads. Subsequent changes to the input are also updated in the span. Hope this is what you were looking for.
The thing that you are not taking into account that the run phase of the app gets executed before the controllers are initialized. Because broadcasted messages don't get buffered and are only served to the listeners that are listening in the moment the message is created, the haha value gets lost.
In your case, however, it's quite easy to make it work with a small change in your controller:
function testCtrl($scope, sharedContent) {
updateTestContent();
$scope.$on('updateContent', updateTestContent);
function updateTestContent(){
$scope.testContent = sharedContent.getStandardContent();
}
}
I forked your JSFiddle here http://jsfiddle.net/y3w5r01d/2/ where you can see on the console when each function (run and controllers) gets executed.

Databinding in angularJS not being updated - UI does not show change in $scope variable

I'm running into a problem with AngularJS where I use a callback on a custom service in order to pass data from one controller to another.
In particular, I'm trying to create a very simple logging service in which one controller registers a listener function with the service to receive updates when a message is logged, and the other controller logs a message when a button in the UI is clicked (triggering the listener in the first controller).
The listener function then updates a $scope.messages variable to show the newly logged message in the UI. However, while the newly arrived message arrives in the listener function, the UI is not updated.
I believe this is because AngularJS is not aware that $scope.messages is being updated.
However, trying to wrap the update of $scope.messages with $scope.$apply has brought no avail ($apply already in progress).
HTML Code:
<div ng-app="myApp">
<div ng-controller="ButtonCtrl">
<button type="button" ng-click="logMessage()">Button</button>
</div>
<div id="console" ng-controller="ConsoleCtrl">
<div ng-repeat="consoleItem in messages">{{consoleItem.message}}</div>
</div>
</div>
Javascript Code:
var module = angular.module('myApp', []);
module.factory('MyConsole', function($rootScope) {
var listeners = [];
var myConsoleService = {};
myConsoleService.log = function(messageObj) {
angular.forEach(listeners, function(listener){
listener(messageObj);
});
};
myConsoleService.registerListener = function(listener) {
listeners.push(listener);
};
return myConsoleService;
});
function ConsoleCtrl($scope, MyConsole){
$scope.messages = [{"message":"First message!"}];
MyConsole.registerListener( function(msgObj){
// while this console.log call below works as expected, the $scope.messages.unshift call does not update the view
$scope.messages.unshift(msgObj.message);
console.log($scope.messages);
});
}
function ButtonCtrl($scope, MyConsole) {
$scope.logMessage = function () {
MyConsole.log({message:"myLogmessage"});
};
}
Code can also be found for your convenience on JSFiddle: http://jsfiddle.net/G5LAY/3/
Any help would be greatly appreciated :-)
Thanks!
Your code is working, it's just that this line: $scope.messages.unshift(msgObj.message); only adds the message string to $scope.messages when your template expects $scope.messages to be a list of objects instead. So changing it to $scope.messages.unshift(msgObj); should do the trick.

Resources