In my angular directive, in a callback, I call $apply to:
Set $scope.model.something
Invoke $scope.onAction() which uses model.something.
I do this in one $apply call, but at the time onAction() is invoked, model.something is still undefined.
At the same time, after $apply, {{model.something}} has a correct value, so model.something is updated correctly.
I want model.something to be set, so I can use it in onAction(). How to fix the following code?
Here's the directive (I skipped not relevant code):
.directive(function () {
return {
scope: {
ngModel: '=',
onAction: '='
},
compile: function (element, attrs) {
return function (scope) {
// This is some callback which is invoked
// outside of digest cycle.
function callback() {
// Here I want to set model and call onAction callback
scope.$apply(function () {
scope.ngModel = 'something';
scope.onAction();
});
}
}
}
};
})
At the same time, my controller looks like:
var MyController = function ($scope) {
$scope.model = {};
$scope.onAction = function () {
// Here I want $scope.model.something to be set to "something"
// But it's undefined.
alert($scope.model.something);
};
}
Finally, HTML:
<div ng-controller="MyController">
{{ model.something }}
<my-directive ng-model="model.something" on-action="onAction"/>
</div>
One more thing, I know I could just call scope.onAction('something'), I'm looking for some other solution.
Here's the fiddle.
You can simply wrap each line into it's own $apply callback:
compile: function (element, attrs, transclude) {
return function (scope, element, attrs) {
setTimeout(function () {
var something = 'lorem ipsum';
scope.$apply(function () {
scope.ngModel = something;
});
scope.$apply(function () {
scope.onAction();
});
}, 200);
};
}
Fiddle
Use $timeout:
$timeout(function(){
scope.onAction(something);
});
Or use $watch:
scope.$watch("ngModel",function(){
scope.onAction(something);
});
Related
In this AngularJS code:
http://jsfiddle.net/edwardtanguay/xfbgjun5/8/
I have a scope function addCustomer() which will not update the scope variable score:
.controller('mainController', function ($scope) {
$scope.customers = ['First','Second','Third'];
$scope.score = 'sdfkj';
$scope.addCustomer = function() {
$scope.score = 'this does not work';
console.log('but it obviously gets here');
}
$scope.changeIt = function() {
$scope.score = 'this works';
}
...
})
I pass in the function addCustomer() as add:
return {
restrict: 'A',
scope: {
datasource: '=',
add: '&'
},
link: link
};
and then call it in my directive:
function addItem() {
scope.add();
items.push('new customer');
render();
}
Why doesn't the scope function addCustomer() update the scope variable score?
Looking at your fiddle your issue is here:
element.on('click', function(event) {
if(event.target.id === 'addItem') {
addItem();
event.preventDefault();
}
});
By doing an element.on you're wiring up your own event in angular. This won't work and you need to do this as well:
element.on('click', function(event) {
if(event.target.id === 'addItem') {
addItem();
event.preventDefault();
scope.$apply();
}
});
This is because the 'click' happenned outside of angular if you build it this way. You should also look into how to build a directive as this is not how you build it (with an html variable and then adding it in). There shouldn't be any html (expect in specific circumstances) in your directive code.
Basically, I created a directive that passes a promise to the link function from ng-click and and detects when the promise is done so that I can attach a class to it.
Example:
.directive('myDirective', function($parse) {
return {
restrict: 'A',
scope: {
ng-click: '&'
},
link: function(scope) {
var d = $parse(scope.ngClick);
element.on('click', function(event) {
d().then(function() {
element.addClass(attrs.myDirective);
});
});
}
};
});
<element ng-click="promise();" my-directive="class"></element>
//controller function
$scope.promise = function() {
return promise().then(function() {});
}
It is doing what I want except that the controller function is getting called three times. I would really like to just use require: '^ngClick' here but since the ngClick directive does not have any controllers, I can't do that. Can anyone point me in the right direction? Thanks!
Added event.preventDefault() to the event.on('click') function in the link of my directive:
element.on('click', function(event) {
event.preventDefault();
d().then(function() {
element.addClass(attrs.myDirective);
});
});
I try to implement a Angular.js directive which will disable the button until the promise is returned. I found some examples. But when I try to call the function inside directive link. It always tell me the newClick() function is undefined. Can someone tell me why? Thanks a lot.
angular.module('myApp.directives.newClick', [])
.directive('newClick', [function () {
return {
scope: {
newClick: '&'
},
link: function (scope, iElement, iAttrs) {
iElement.bind('click', function () {
iElement.prop('disabled', true);
scope.newClick().finally(function () {
iElement.prop('disabled', false);
});
});
}
};
}]);
This should work fine as long as:
The newClick directive is properly set on the element.
It references a function that returns a promise.
E.g.:
<button new-click="clickFunc()">Click me</button>
app.controller('myCtrl', function ($q, $scope, $timeout) {
$scope.clickFunc = function () {
var deferred = $q.defer();
$timeout(function () {
deferred.resolve('Hello, wolrd !');
}, 2000);
return deferred.promise;
};
});
See, also, this short demo.
I'm trying to make a directive which receives expression as a callback to bootstrap's 'hidden.bs.modal' event:
//the use
<div class="modal" modal-on-hide="currPane = 'firstPanel'">
//the implementation
app.directive('modalOnHide', function () {
return {
restrict: 'A',
link: function postLink(scope, element, attrs) {
element.on('hidden.bs.modal', function hideCallback () {
scope.$eval(attrs.modalOnHide);
});
}
};
});
currPane is an attr on the parent scope which is the same scope as the directive's, yet, when hideCallback executes, it's not being set to the new value. Idea?
Try this way:
element.on('hidden.bs.modal', function hideCallback () {
scope.$apply(function(){
scope.$eval(attrs.modalOnHide);
});
});
Or:
element.on('hidden.bs.modal', function hideCallback () {
scope.$apply(attrs.modalOnHide);
});
I just would like to show and hide an spinner every time that a user click on the link, in this case I'm using a promise to wrap the actual click method, when the promise get resolved I want hide the spinner.
for some reason it seems like the directive isn't binding the value that I have in the scope with the ng-class in the template
any ideas?
app.directive('toogleTextLink', function($compile,$q) {
return {
restrict: 'AE',
scope: { callback: "&targetMethod" },
template: '<div><a style="cursor: pointer" ><b>{{text}}</b></a> <div ng-class="{previewLoader: show}"></div></div>',
link: function (scope, element, attr) {
scope.value = attr.value;
scope.show = false;
scope.$watch('value', function () {
if (scope.value) {
scope.text = "yes";
} else {
scope.text = "no";
}
});
element.bind('click', function () {
scope.show = true;
scope.value = !scope.value;
scope.$apply();
if (scope.callback) {
var deferred = $q.defer();
deferred.resolve(scope.callback());
deferred.promise.then(function () {
scope.show = false;
console.log("then called");
});
}
});
}
};
});
take a look to the plunker
The problem seams the size of the spinner (CSS) also you are resolving the promise immediately, so you wont be able to see the spinner.
Check these changes
http://plnkr.co/edit/viwzCb?p=preview