Angular - detect changed to the same value - angularjs

I have a controller and a directive. In the directive there is array of data from the controller.
When a button from the directive is clicked I want the controller to update the data.
So the button is clicked, I show a busy indicator and when the controller finishes to update the data I want to hide the busy indicator. My problem is that some times the data stays the same so the "watchCollection" function is not triggered.
Bottom line, I'm looking for a solution that will allow the controller to tell the directive that he has finished the update.
** Another solution I thought of is adding a boolean for "updating". The directive will change it to true and the controller will change it to false when it is finished. But the problem again is that in some cases the value of "updating" is changed back to false before the digest cycle is started detects that is was changed to true.
The directive looks something like this:
Directive:
{
scope: {
arrayData: "=",
onChangeData: "&"
},
controller: function($scope){
$scope.buttonClicked : function(){
// show busy indicator
$scope.onChangeData();
}
}
link: function preLink(scope, element, attrs) {
scope.$watchCollection('arrayData', function (value) {
// hide busy indicator
});
}
}

One way you can achieve this is by returning a promise for onChangeData like
$scope.onChangeData=function() {
var defer=$q.defer();
//process data
//resolve defer
return defer.promise;
}
Then in your directive controller do
$scope.onChangeData().then(function() {
//stop animation
});

Related

Directive using timeout not waiting for animation to finish

This "focusMe" directive using the $timeout service does not wait for my modal to finish loading, so the animation is choppy. The input box should be automatically focused so the user does not have to click the box (or press tab) to enter their badge number.
I've attempted to do my own hacky fixes, and found two directives online that people said worked for them. I've included the best attempt I made in this plnkr
Here is the broken directive:
signout.directive('focusMe', ['$timeout', '$parse', function ($timeout, $parse) {
return {
// scope: true, // optionally create a child scope
link: function (scope, element, attrs) {
var model = $parse(attrs.focusMe);
scope.$watch(model, function (value) {
console.log('value=', value);
if (value === true) {
$timeout(function () {
element[0].focus();
});
}
});
}
};
}]);
Here's a visualization of this issue:
Choppy
Smooth
My desired result is the directive waiting for animation to finish, then immediately focus on the input field for easy entry.

prevent api call from execution repeatedly in directive

I am making a api call from a directive. There is a condition to show directive. I am handling it using ng-show. But when first time html loads api call is executed even if ng-show flag is false.
I want to make api call only at first time when ng-show flag is true. After change in this ng-show flag i do not want to make api call.
I have done something like this.
angular.module("module").directive("customDirective", function(){
return {
templateUrl : "cutomTemplate.html",
replace : true,
scope : {},
link : {
apicall();
}
};
});
<custom-directive ng-show="value === 1"></custom-directive>
Here is a solution i came up. This involves registering a watch on ngShow attribute in your directive scope and then de registering it once api call is made. See my fiddle here http://jsfiddle.net/cmyworld/u57dw8ws/
The directive now looks like:
angular.module("module").directive("customDirective", function () {
return {
scope: {
ngShow: '='
},
link: function (scope, element, attr) {
var observeFn = scope.$watch('ngShow', function (value) {
if (value) {
console.log('Making api call');
observeFn(); // degister after first callback,
}
});
}
};
});
ng-if may not work, if you show hide the element again and again.

angularjs - waiting for a value to be bound to a directive

I am experiencing a strange situation with angularjs. I have a value bound to a directive, and I need to be able to check on and manipulate that value from both the controller and a directive. I also have a method as a property of an object bound to the directive that I need to call from the controller. The method is expected to react accordingly to the bound value.
Here is some pseudo code to illustrate it:
.controller('ctrl', function(){
$scope.someAction = function(){
$scope.myValue = undefined;
$scope.someObject.myMethod();
};
});
.directive('myDirective', ...){
return {
...
scope: { myValue: '=', someObject: '=' },
link: function (scope) {
scope.someObject = {
myMethod: function(){
if (angular.isDefined(scope.myValue)){
// do something
}
else {
// do something else
}
}
};
}
}
}
and in controller template:
<my-directive my-value="myValue" some-object="someObject"></my-directive>
I would expect that when "someAction" is triggered, "myValue" set to undefined and the method "someObject.myMethod" is called from the controller, "myValue" in the directive is undefined, but it isn't that way. However, if I wrap the method call in a $timeout that waits for just 1 millisecond, I get the expected behaviour:
.controller('ctrl', function($timeout){
$scope.someAction = function(){
$scope.myValue = undefined;
$timeout(function() {
$scope.someObject.myMethod();
}, 1);
};
});
This hack has solved my problem, but I would prefer to understand what is going on and perhaps solve it (or avoid it) more elegantly...
Your controller's function $scope.someAction is wrapped by angular into an $apply call. So, probably, when you call $scope.someObject.myMethod(), the directive's isolated scope isn't updated because $apply didn't end. A solution would be to do something like you suggested:
$timeout(function() {
$scope.someObject.myMethod();
}, 0);
The timeout will delay to the execution of function to the next $apply call so your directive will see the updated value.

Directive trigger controller before model is updated

I created a custom directive that create two buttons. Each button use ng-click to call a method inside the directive to change a value of the model and then call the controller to update the value in the database. In this way the controller is called before the model is updated because it always use the old value.
If I apply inside the directive $scope.$apply() everything works fine, except an error is fired in the consolle because there are an $apply() executing yet. How properly wait the model update before to call the controller?
The directive is used into an ng-repeat that iterate an array.
Logic I used is:
User Click one of the Buttons of the switch (On or Off i.e.)
Button Trigger directive setModel(value) method
setModel(value) set value to the model and call controller passing index of the object
controller get the object by index and update in database
If i try to send the value edited to the controller, of course, it works, but i would play with models and not with each values. Why the model is updated before controller do it's $http calls?
This is the directive:
app.directive('ngSwitch', function () {
return {
scope: {
model: '=ngSwitch',
index: '=index', //Index in the array
switchValue1: '#switchValue1',
switchDesc1: '#switchDesc1',
switchValue2: '#switchValue2',
switchDesc2: '#switchDesc2',
callback: '=callback'
},
link: function (scope, elm, attr) {
scope.saving = false;
scope.setModel = function (value) {
scope.model = value == 1 ? scope.switchValue1 : scope.switchValue2;
scope.saving = true;
scope.$apply();//IF I DON'T USE $apply, IT DOESN'T WORK
scope.callback(scope.index)
}
},
templateUrl: 'components/direttive/ngSwitch.html'
};
});

Angular - receiving info from controller inside directive

I have a directive which is responsible to render audio and video items on a page:
myApp.directive('videoitem', function($compile){
return {
replace: true,
templateUrl: '/templates/directives/videoitem.html',
scope: {
videoChunkItem: "=",
videostartedCallback: "&videostarted",
videoendedCallback: "&videoended",
},
link: function($scope, $element, $attributes){
var startVideo = function(){
$element[0].load();
$element[0].play();
$scope.videostartedCallback();
};
$element.bind("ended", function(){
$scope.videoendedCallback();
$scope.$apply();
});
/**
* Here I am using on click event to start the video
* but the real case is that the trigger for video playing should come from controller.
* Any idea how to do it?
*/
$element.on('click', function(){
startVideo();
});
}
};
});
Is it possible from a route controller to send some event or something else to communicate from controller to directive to call startVideo method?
The flow of communication is from route controller to directive ... When in a controller occure some event I want to invoke directive's startVideo method.
Without knowing your exact use-case, my approach would be to define a new scope property, e.g. play : "=" (to set it to stop when the video ends), and $observe attrs.play or $scope.$watch "play" it. In your template you bind it to a $scope variable that is triggered by the controller.
<video-item play="videoControls.play"></video-item>
Here's a plunker where you use two-way binding, note how you don't have to do anything:
http://plnkr.co/edit/3OI801KcvYVoySDvcm8i?p=preview
If you want to allow expressions, you have to observe the attribute to get the interpolated value:
http://plnkr.co/edit/gi6FSPPlN599CtDjKBOb?p=preview

Resources