Angularjs destroy is not working - angularjs

http://plnkr.co/edit/UfQJU661pQR0DMY3c61t?p=preview
I got above code from AngularJs site and only thing I have added a button to delete a Div where we have controller but after delete no destroy method called as I have put alert in Directive and Controller.
element.on('$destroy', function() {
alert('destroy directive interval');
$interval.cancel(stopTime);
});
and
$scope.$on('$destroy', function() {
alert('destroy controller interval');
// Make sure that the interval is destroyed too
$scope.stopFight();
});
please suggest.
Thanks

The main thing to be noticed
When element.remove() is executed that element and all of its children will be removed from the DOM together will all event handlers attached via for example element.on.
It will not destroy the $scope associated with the element.
So you need to manually trigger scope.$destroy();
First get the scope of element:-
var scope = angular.element(document.getElementById("mainDiv")).scope();
Second remove the element from dom:-
$('#mainDiv').remove();
Third destroy scope manually:-
scope.$destroy();
Plunker

You're doing it outside of angular's context.
<button id="btn" onclick="DeleteMainDiv()">DeleteDiv</button>
So in your DeleteMainDiv() function
function DeleteMainDiv() {
alert('Controller div going to remove');
//debugger;
var scope = angular.element(document.getElementById("mainDiv")).scope();
$('#mainDiv').remove();
scope.$destroy();
}
This will trigger the destroy functionality.
But I don't see a need of it. Angular will automatically run the $destroy event handler when the route changes or directive no longer required.
DEMO

Related

Changing $scope from inside $rootScope is not getting reflected

I am trying to show one button as in this Plunker
<div ng-show="showbtn"><button class="fix btn btn-success" ng-click="top()">To the top</button></div>
On scroll event, I have made $rootScope.$emit call and it is getting triggered too, but not sure why the $scope value is not getting changed inside the mainCtrl controller $scope. Is $scope inside $rootScope is different ?
The event handler (the function passed to $rootScope.$on) runs outside of Angular's normal digest cycle so you need to tell the parent scope that something has changed. You can use $apply to do so:
$rootScope.$on('scrolled',function(event,data){
$scope.$apply(function () {
$scope.showbtn = data.message;
});
});
Here's an updated Plunker.

$watch on clientWidth isn't working

Inside of my directive I have this and it works only in the beginning, and whenever I resize later it never fires.
scope.$watch ->
cw: element[0].clientWidth
,(newValue, oldValue)->
if newValue.cw isnt oldValue.cw
console.log "changed"
,true
Your function looks right to me, but it's important to note that it won't fire on resize unless you manually trigger a digest on the resize event.
Without this, angular doesn't realise there's been an event that should trigger a digest, and so none happen.
Try adding this (remember to inject $window):
angular.element($window).bind('resize', ()->
scope.$apply();
)
Just like Jason Goemaat said, its because Watches are only checked on a $digest cycle
Depending on how the way your element is resized maybe this example may help you.
Here is a menu that expands it self when pressed on the arrow.
<div class="menu" ng-class="{'expanded' : main.expanded}">
<div class="menu-handler" ng-click="main.expanded = !main.expanded">
</div>
</div>
The menu is expanded by a ng-click that makes a change on the scope, in this case its a boolean var that acts as a flag. As the opening of the menu is made throught the change in the scope it calls the $digest cycle iterating through all $watch.
scope.$watch(function() {
return element[0].clientWidth;
}, function(oldValue, newValue) {
console.log(oldValue, newValue);
});
You can view the full example in this JSfiddle
https://jsfiddle.net/owenbcal/zf17s0mL/
Hope i was helpfull.
the way I know about watching this kind of values is making a function that is fired once the value changes so then I watch that function :
in the service:
this.getDataOut = function () {
return self.dataOut;
};
in the controller:
$scope.$watch(dataService.getDataOut, function() {
$scope.dataOut = dataService.dataOut;
});

Why isn't $destroy triggered when I call element.remove?

I can't figure out why the $destroy event is not triggered in the following example. Can someone explain why it is not triggered, and in what scenarios it will be triggered?
Here's the plunkr: http://plnkr.co/edit/3Fz50aNeuculWKJ22iAX?p=preview
JS
angular.module('testMod', [])
.controller('testCtrl', function($scope){
$scope.removeElem = function(id) {
var elem = document.getElementById(id);
angular.element(elem).remove();
}
}).directive('testDir',[function() {
return {
scope:true,
link: function(scope) {
console.log('in directive');
scope.$on('$destroy', function(){
alert('destroyed');
})
}
}
}]);
HTML
<body ng-controller='testCtrl'>
<div testDir id='test'>I will be removed.</div>
<button ng-click='removeElem('test')'>remove</button>
</body>
The problem is your listening for the $destroy event on the scope, but $destroy is being triggered on the element.
From angular.js source (I'm sure it's documentated on the website somewhere, but I didn't look):
$destroy - AngularJS intercepts all jqLite/jQuery's DOM destruction
apis and fires this event on all DOM nodes being removed. This can
be used to clean up any 3rd party bindings to the DOM element before
it is removed.
Your directive should be (note that I added scope,element, and attrs as link arguments): Also, here is a plunker.
directive('testDir',[function() {
return {
scope:true,
link: function(scope,element,attrs) {
console.log('in directive');
element.on('$destroy', function(){
alert('destroyed');
})
}
};
}]);
I am puzzled on why the $destroy event is not triggered on remove() method.
As per the docs, the $destroy event is triggered in two cases.
Just before a scope is destroyed
Just before an element is removed from the DOM
The purpose being "cleanup". You can listen on the $destroy event and perform necessary cleanups before letting a scope or element be destroyed. ngIf, ngSwitch, ngRepeat and other in-built directives/methods use the $destroy event to perform cleanups.
A best example would be the ngRepeat directive
https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js
On line 339 you can notice the $destroy event being triggered. You can listen on the event and perform any action just before an item is removed from the list used by ngRepeat.
ngRepeat $destroy Example Plunk -- http://goo.gl/mkozCY

How to append a directive to another directive which calls a function from controller.

I'm trying to append a directive which occurs when an event is fired via the $watch function in angular. The first directive updater would insert a custom element <confirmation /> into my view. confirmation should have it's respective angular bindings.
after it's inserted I want the function startChange() which resides in my MainCtrl to fire inside the new confirmation directive.
I've made a plnkr http://plnkr.co/edit/BEozKyRZ0rJMz2e0jyp0?p=preview
Any help would be hugely appreciated.
First off...
There shouldnt be a '.' between the (el) and the ($scope)
var ngEl = $compile(el).($scope);
remove the dot and it will fix the app not being defined in the console.
var ngEl = $compile(el)($scope);
Next to get the error in the console of the startChange function not being defined, name space it on the scope of the directive and be sure to pass it into the isolate scope as well.
HTML:
<confirmation message="Please Select something" state="state" startChange="startChange()" class="background-state-{{ state }}">someText</confirmation>
Directive:
scope : {
message : '#',
state: '=',
startChange: '&'
}
In the linker:
scope.startChange();
I'm still not sure if this is the situation you would like as the other directive update isn't being used anywhere that I can see...
Here is your plunker with my changes: http://plnkr.co/edit/8VoJSj7bHUkaEjUFrn3i?p=preview

Adding ng-change to child elements from linking function of directive

I created a directive that should add a ng-change directive dynamically to all child input tags:
myApp.directive('autosave', function ($compile) {
return {
compile: function compile(tElement, tAttrs) {
return function postLink(scope, iElement, iAttrs) {
var shouldRun = scope.$eval(iAttrs.autosave);
if (shouldRun) {
iElement.find(':input[ng-model]').each(function () {
$(this).attr("ng-change", iAttrs.ngSubmit);
});
$compile(iElement.contents())(scope);
console.log("Done");
}
}; //end linking fn
}
};
});
The problem that I have is that the ng-change directive isn't running. I can see it that its added to the DOM element BUT not executing when value changes.
The strange thing is that if I try with ng-click, it does work.
Dont know if this is a bug on ng-change or if I did somehting wrong.
Fiddle is with ng-click (click on the input) http://jsfiddle.net/dimirc/fq52V/
Fiddle is with ng-change (should fire on change) http://jsfiddle.net/dimirc/6E3Sk/
BTW, I can make this work if I move all to compile function, but I need to be able to evaluate the attribute of the directive and I dont have access to directive from compile fn.
Thanks
You make your life harder than it is. you do'nt need to do all the angular compile/eval/etc stuff - at the end angular is javascript : see your modified (and now working) example here :
if (shouldRun) {
iElement.find(':input[ng-model]').on( 'change', function () {
this.form.submit();
});
console.log("Done");
}
http://jsfiddle.net/lgersman/WuW8B/1/
a few notes to your approach :
ng-change maps directly to the javascript change event. so your submit handler will never be called if somebody uses cut/copy/paste on the INPUT elements. the better solution would be to use the "input" event (which catches all modification cases).
native events like change/input etc will be bubbled up to the parent dom elements in the browser. so it would have exactly the same to attach the change listener to the form instead of each input.
if you want to autosave EVERY edit that you will have an unbelievable mass of calls to your submit handler. a better approach would be to slow-down/throttle the submit event delegation (see http://orangevolt.blogspot.de/2013/08/debounced-throttled-model-updates-for.html ).
if you want to autosave EVERY edit you skip your change handler stuff completely and suimply watch the scope for changes (which will happen during angular model updates caused by edits) and everything will be fine :
scope.watch( function() {
eElement[0].submit();
});

Resources