Angular $watch function after change variable - angularjs

I´m trying to build a delayed message box. I observe that apply is called when the app is started, as describe in the API docs. But when the observed value is changed, it isn´t called. The MessageCtrl is inner controller. Why isn´t watch called after changing message var?
angular.module('myApp',[]).controller('MessageCtrl', function($scope) {
$scope.getMessage = function() {
setTimeout(function() {
$scope.$parent.message = {text : ""};
$scope.$apply(function() {
console.log('message:' + $scope.$parent.message.text);
});
}, 2000);
}
$scope.getMessage();
})
.controller('MainCtrl',function($scope){
$scope.message={text:"oi"};
$scope.$watch("message", function(newValue, oldValue){
console.log("watch " + $scope.message.text);
});
});
The inner controller MessageCtrl will get the text message and show it for 2 seconds.
<body ng-app="myApp" ng-controller="MainCtrl">
<div ng-controller="MessageCtrl">
Message:{{message.text}}
</div>
</body>

As far as I can tell your code does work. You are however using $apply incorrectly. $apply lets you inform angular that you are changing state outside of the usual methods and that is should thus re-evaluate bindings etc. So you should be using
$scope.$apply(function() { $scope.$parent.message = {text: 'new message'}; });
angular.module('myApp', []).controller('MessageCtrl', function($scope) {
$scope.getMessage = function() {
setTimeout(function() {
$scope.$apply(function() {
$scope.$parent.message = {
text: "new message"
};
console.log('message:' + $scope.$parent.message.text);
});
}, 2000);
}
$scope.getMessage();
})
.controller('MainCtrl', function($scope) {
$scope.message = {
text: "oi"
};
$scope.$watch("message", function(newValue, oldValue) {
console.log("watch " + $scope.message.text);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MainCtrl">
<div ng-controller="MessageCtrl">
Message:{{message.text}}
</div>
</div>
One thing of note, you should really use the angular $timeout service which allows you to set timeouts in your app, but you don't have to handle calling $apply.
angular.module('myApp', []).controller('MessageCtrl', function($scope, $timeout) {
$scope.getMessage = function() {
$timeout(function() {
$scope.$parent.message = {
text: "new message"
};
console.log('message:' + $scope.$parent.message.text);
}, 2000);
}
$scope.getMessage();
})
.controller('MainCtrl', function($scope) {
$scope.message = {
text: "oi"
};
$scope.$watch("message", function(newValue, oldValue) {
console.log("watch " + $scope.message.text);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MainCtrl">
<div ng-controller="MessageCtrl">
Message:{{message.text}}
</div>
</div>

You need to use $scope.apply method because setTimeout function is not angular native function :
setTimeout(function() {
$scope.apply({
$scope.$parent.message = {text : ""}; $scope.$apply(function() { console.log('message:' + $scope.$parent.message.text); });}) ;}, 2000);
Alternatively you can also use $timeout inbuilt-service in angular like this :
angular.module('myApp',[]).controller('MessageCtrl', function($scope, , $timeout) { $scope.getMessage = function() { $, $timeout(function() { $scope.$parent.message = {text : ""}; $scope.$apply(function() { console.log('message:' + $scope.$parent.message.text); }); }, 2000); } $scope.getMessage(); })

use $timeout instead $apply that executes $apply implicity
angular.module('myApp',[]).controller('MessageCtrl', function($scope, $timeout) {
$scope.getMessage = function() {
$timeout(function() {
$scope.$parent.message = {text : ""};
$scope.$apply(function() {
console.log('message:' + $scope.$parent.message.text);
});
}, 2000);
}
$scope.getMessage();
})

Related

Using AngularJS component props

I'm new to angularJS, and now I'm trying to realize some parts.
The questions is: how do I get access to callback onFinish() which is passed to component "my-timer" and run it? this.onFinish() returns the error.
Here is my markup:
<div ng-app="app" ng-controller="MyCtrl as myCtrl">
<div>
Status: {{myCtrl.status ? myCtrl.status : 'Waiting...'}}
</div>
<div>
<button ng-click="myCtrl.addTimer(5)">Add timer</button>
</div>
<div ng-repeat="timer in myCtrl.timers">
<div>
<h3>Timer {{timer.id}}</h3>
<button ng-click="myCtrl.removeTimer($index)">X</button>
<my-timer id="{{timer.id}}" start-seconds="{{timer.seconds}}" on-finish="myCtrl.onFinish(endTime)"></my-timer>
</div>
</div>
</div>
And here is index.js
var app = angular.module('app', []);
app.controller('MyCtrl', class {
constructor($scope) {
this.status = null;
this.timerId = 0;
this.timers = [];
this.addTimer(10);
this.addTimer(3);
console.log($scope);
}
addTimer(seconds) {
this.timers.push({
id: this.timerId++,
seconds
});
}
removeTimer(index) {
this.timers.splice(index, 1);
}
onFinish(endTime){
this.status = `Timer finished at ${endTime}`;
console.log(endTime);
}
});
app.component('myTimer', {
bindings: {
id: '#',
startSeconds: '#',
onFinish: '&',
},
controller: function($interval, $scope) {
this.endTime = null;
this.$onInit = function() {
this.countDown();
};
this.countDown = function() {
$interval(() => {
this.startSeconds = ((this.startSeconds - 0.1) > 0) ? (this.startSeconds - 0.1).toFixed(2) : 0;
}, 100);
};
},
template: `<span>{{$ctrl.startSeconds}}</span>`,
});
And here is jsFiddle
this.$onInit = function() {
this.countDown();
};
this.onFinish('1');
The problem here is that you tried to execute this.onFinish right in controller's body. And that wont work that way. If you want this function to be called during initialization, move it to $onInit
this.$onInit = function() {
this.countDown();
this.onFinish('1');
};
Otherwise, call it from another component method. You can only declare variables and component methods in controller body, but not call functions.

angular $watch is not work with d3.js

look, click the button, $scope.optionis change, but $watch is not work, can`t console.log the option value , why?
I am sorry , it is my mistake , but it still is a problem in I use width d3.js
I use d3.js append a rect into page, and I want to when I click the rect can chagne the option value, but $watch is not work, why?
angular.module('myapp',[]).controller('myCtrl', function($scope){
$scope.option = '123';
$scope.d3TreeDraw = {
source : {
name: 'myTree'
},
updata: function(){
var _self = this;
var tree = d3.layout.tree().nodeSize([90, 60]);
var nodes = tree.nodes(_self.source).reverse();
nodes.forEach(function(d) { d.y = d.depth * 90; });
var svg = d3.select("body").append("svg")
.attr("width", 200)
.attr("height", 200)
var node = svg.selectAll("g.node")
.data(nodes)
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.style('cursor','pointer')
.on('click', function(d){
console.log('click'); // can console.log
$scope.option = '456';
console.log($scope.option) //is change
})
nodeEnter.append("rect")
.attr('width',150)
.attr('height', 30)
.style('fill', '#000')
}
}
$scope.d3TreeDraw.updata();
$scope.$watch('option', function(){
console.log('change:' + $scope.option); // when option is change,can not console.log
})
})
1) First You have taken myTree.onClick() and your function has onclick
So, the onClick() spelling mismatched.
Change button to <button ng-click="myTree.onclick()">456</button>
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<button ng-click="myTree.onclick()">{{data}}</button>
<br>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.firstName = "John";
$scope.lastName = "Doe";
$scope.data = '100';
$scope.myTree = {
onclick: function() {
$scope.data = '456';
}
}
$scope.$watch('data', function(){
console.log($scope.data);
alert($scope.data);
})
});
</script>
</body>
</html>
Here is a working DEMO.
EDIT:
By checking your edit, I saw that your scope assignment is outside of angular.
So, you need to $apply() the $scope
Change,
$scope.option = '456';
to,
$scope.$apply(function() {
$scope.option = '456'
});
The above method, runs the digest cycle manually, and apply the changes for the scope.
Performance:
If you write $scope.$apply() it will run complete digest cycle, which will affect the performance, so we sent a function into the $apply method and only ran the digest cycle of specific `scope.
Hence, you can $watch the scope whenever you want.
you have a upper case letter in the function name
change this
<button ng-click="myTree.onClick()">456</button>
to this
<button ng-click="myTree.onclick()">456</button>
Demo
angular.module("app",[])
.controller("ctrl",function($scope){
$scope.data = '123';
$scope.$watch('data', function(){
console.log($scope.data);
})
$scope.myTree = {
onclick: function() {
$scope.data = '456';
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<button ng-click="myTree.onclick()">456</button>
</div>

Uncaught TypeError: self.$apply is not a function in Angular1.6

I try to learn angular1.6 and in this example I don't know where I made a mistake.
It suppose, after 3 seconds, to print on the screen and in console log the same text from message variable, when I click on Get Message button.
(function() {
"use strict";
angular.module('myApp',[])
.component('myComponent', {
template: "<button ng-click='$ctrl.scheduleTask()'>Get Message</button><br><p>Message fetched: {{$ctrl.message}}</p>",
controller: function() {
self = this;
self.scheduleTask = function() {
setTimeout(function() {
self.$apply(function() {
self.message = 'Fetched after 3 seconds';
console.log('message = ' + self.message);
});
}, 3000);
};
}
})
})();
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<my-component></my-component>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
</html>
Try to use $scope instead:
(function() {
"use strict";
angular.module('myApp',[])
.component('myComponent', {
template: "<button ng-click='$ctrl.scheduleTask()'>Get Message</button><br><p>Message fetched: {{$ctrl.message}}</p>",
controller: myComponentController
});
myComponentController.$inject['$scope'];
function myComponentController($scope) {
self = this;
self.scheduleTask = function() {
setTimeout(function() {
$scope.$apply(function() {
self.message = 'Fetched after 3 seconds';
console.log('message = ' + self.message);
});
}, 3000);
};
}
})();
A more correct way would be to use $timeout:
$timeout
(function() {
"use strict";
angular.module('myApp',[])
.component('myComponent', {
template: "<button ng-click='$ctrl.scheduleTask()'>Get Message</button><br><p>Message fetched: {{$ctrl.message}}</p>",
controller: myComponentController
});
myComponentController.$inject['$timeout'];
function myComponentController($timeout) {
self = this;
self.scheduleTask = function() {
$timeout(function() {
self.message = 'Fetched after 3 seconds';
console.log('message = ' + self.message);
}, 3000, true);
};
}
})();

How to implement right click with images using angular JS

I am having an example of what i want to achieve given below in plunker.As 10 changes on right click,i want an image there that should convert to some another image on right click.Please help me out with this.
http://jsfiddle.net/bcaudan/vTZZ5/
app.directive('ngRightClick', function($parse) {
return function(scope, element, attrs) {
var fn = $parse(attrs.ngRightClick);
element.bind('contextmenu', function(event) {
scope.$apply(function() {
event.preventDefault();
fn(scope, {$event:event});
});
});
};
});
made this fiddle for you: JsFiddle
Are you looking for something like this??
JS should be like this:
var app = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.img1 = "http://4.bp.blogspot.com/-JOqxgp-ZWe0/U3BtyEQlEiI/AAAAAAAAOfg/Doq6Q2MwIKA/s1600/google-logo-874x288.png";
$scope.img2 = "http://www.socialtalent.co/wp-content/uploads/blog-content/so-logo.png";
$scope.selected = $scope.img1;
$scope.increment = function() {
$scope.selected = $scope.img1;
};
$scope.decrement = function() {
$scope.selected = $scope.img2;
};
};
app.directive('ngRightClick', function($parse) {
return function(scope, element, attrs) {
var fn = $parse(attrs.ngRightClick);
element.bind('contextmenu', function(event) {
scope.$apply(function() {
event.preventDefault();
fn(scope, {$event:event});
});
});
};
});
and HTML should be:
<div ng-app="myApp" ng-controller="MyCtrl">
<span class="action"
ng-click="increment()"
ng-right-click="decrement()"><img ng-src="{{selected}}"></span>
</div>
Please see demo below
var app = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.images = [
'http://upload.wikimedia.org/wikipedia/commons/a/a7/Tiffanie_at_cat_show.jpg',
'http://www.thetimes.co.uk/tto/multimedia/archive/00342/114240651_cat_342943c.jpg'
];
$scope.imageSrc = 1;
$scope.toggleImage = function() {
$scope.imageSrc == 1 ? $scope.imageSrc = 0 : $scope.imageSrc = 1;
};
};
app.directive('ngRightClick', function($parse) {
return function(scope, element, attrs) {
var fn = $parse(attrs.ngRightClick);
element.bind('contextmenu', function(event) {
scope.$apply(function() {
event.preventDefault();
fn(scope, {
$event: event
});
});
});
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<image ng-src="{{images[imageSrc]}}" class="action" ng-right-click="toggleImage()" width="150px" />
</div>

I have a trouble about using "$rootScope.$broadcast" and "$on"

I have a trobule about using $rootScope.$broadcast and $scope.$on
I have a one module and two controller(Controller1 & Controller2).
var app = angular.module("app",[]);
app.controller("Controller1",function ($scope,$rootScope){
$scope.$on("msgUpdated",function (event,data){
console.log(data.message);
})
app.controller("Controller2",function ($scope,$rootScope){
$scope.msg = "Hi!";
$rootScope.$broadcast("msgUpdated",{message:msg});
});
This above is my code.
The problem is that my Controller1's $scope.$on is not working.
Why? I don't get it.
and, How can I fix it to fire Controller1's $scope.$on ?
<body ng-app="app">
<div ng-controller="Controller1">
<h1>{{msg1}}</h1>
<input ng-model="test" ng-blur="sendMsg()"/>
</div>
<div ng-controller="Controller2">
<h1>{{msg2}}</h1>
<input ng-model="test" ng-blur="sendMsg()"/>
</div>
</body>
var app = angular.module('app',[])
.controller('Controller1',['$rootScope','$scope',function($rootScope,$scope){
$scope.msg1 = "Start";
$scope.sendMsg = function() {
$rootScope.$emit('msg',$scope.test);
};
var cleanup = $rootScope.$on('msg2', function (event,data) {
$scope.msg1 = data;
});
$scope.$on('$destroy', cleanup);
}])
.controller('Controller2',['$rootScope','$scope',function($rootScope,$scope){
$scope.msg2 = "Start2";
$scope.sendMsg = function() {
$rootScope.$emit('msg2',$scope.test);
};
var cleanup = $rootScope.$on('msg', function (event,data) {
$scope.msg2 = data;
});
$scope.$on('$destroy', cleanup);
}]);
Here is fiddler:
I always use $rootScope.$emit and clean up.
http://jsfiddle.net/hbqsbLyg/

Resources