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.
Related
I'm trying to create a directive that is going to go back a page to my search document form and call my 'documentSearch' method. I'm not sure what is going wrong here as browser complains
Uncaught TypeError: Cannot read property 'dsSvc' of undefined
APPCONFIG.dsSvc stores information where the web service is stored. It works absolutely fine from search page but in this directive I'm creating doesn't. Any idea why? I'm stuck on this for long enough. I'm not entirely sure if it's going to work the way I do it (go back a screen and call the web service).
app.directive('previousPage', ['$window', function($window) {
return {
restrict: 'A',
link: function (scope, elem, attrs, $scope,GenericServiceSvc,APPCONFIG) {
elem.bind('click', function () {
$window.history.back();
var paramsBack = JSON.parse(window.localStorage.getItem('srchParams'));
var svcData = {
invoke: 'documentSearch',
app: APPCONFIG.dsSvc,
params: paramsBack
};
GenericServiceSvc.callService(svcData).then(
function (response) {
if (response) {
console.log(response);
} else {
//no results
}
}, function () {
console.log(svcData.invoke + '- Fail');
}
);
});
}
};
}]);
The link function has the following signature:
function link(scope, element, attrs, controller, transcludeFn) {...}
It looks like you're trying to inject APPCONFIG (is it a service/factory/constant?) into the link function, so what you're really asking is for AngularJS to get dsSvc from what it expects to be the transcludeFn argument.
Move your injections to the directive function:
app.directive('previousPage', ['$window', 'GenericServiceSvc', 'APPCONFIG', function($window, GenericServiceSvc, APPCONFIG) {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
elem.bind('click', function () {
$window.history.back();
var paramsBack = JSON.parse(window.localStorage.getItem('srchParams'));
var svcData = {
invoke: 'documentSearch',
app: APPCONFIG.dsSvc,
params: paramsBack
};
GenericServiceSvc.callService(svcData).then(
function (response) {
if (response) {
console.log(response);
} else {
//no results
}
}, function () {
console.log(svcData.invoke + '- Fail');
}
);
});
}
};
}]);
Here is the code I'm using. I'm trying to display the html contents which comes under this tag after 4 seconds
Utils.directive('ieUtilsError', function() {
var directive = {};
directive.link = function(scope, element, attrs) {
element.attr("style","display:none")
function show() {
element.attr("style","display:inline")
}
$timeout(function() {
show();
}, 4000);
}
});
$timeout is a service which should be injected. Change the below line
Utils.directive('ieUtilsError', function() {
//your code
});
to
Utils.directive('ieUtilsError', function($timeout) {
//your code
});
Include the $timeout dependency in your directive, like this:
Utils.directive('ieUtilsError', function($timeout) {
//your code...
});
You didn't inject the $timeout service in your directive, so it's undefined:
Utils.directive('ieUtilsError', function($timeout) {
You also forgot to return the directive from the function:
return directive;
Otherwise, it works fine: http://plnkr.co/edit/z0P6ENRyXvyYjNr7KqzV?p=preview
Note that is couls be reduced to
Utils.directive('ieUtilsError', function($timeout) {
return {
link: function(scope, element, attrs) {
element.attr("style","display:none")
$timeout(function() {
element.attr("style","display:inline");
}, 4000);
}
};
});
You need to inject $timeout into your directive
Utils.directive('ieUtilsError', ['$timeout',ieUtilsError]);
function ieUtilsError($timeout){
return {
$timeout(function() {
element.attr("style","display:inline");
}, 4000);
};
}
You need to inject dependencies to the directive like in other modules:
Utils.directive('ieUtilsError', ['$timeout', function($timeout) {
return = {
link: function($scope, element){
element.attr("style","display:none")
//use $timeout
$timeout(function() {
element.attr("style","display:inline");
}, 4000);
}
};
}]);
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);
});
});
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);
});
I'm trying to use prettyprint with some ajax data. The problem is that when the directive is invoked, the data arem't ready, so I get undefined variable output.
Plunkr: http://plnkr.co/edit/fdRi2nIvVzT3Rcy2YIlK?p=preview
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,$http) {
$scope.result = $http.get('data.json').success(function(result){
return result.data.dom
})
});
app.directive('prettyprint', function() {
return {
restrict: 'C',
link: function postLink(scope, element, attrs) {
element.html(prettyPrintOne(scope.result))
}
};
});
Use scope's $watch method:
scope.$watch("result" , function(n,o){
element.html(prettyPrintOne(scope.result));
})
And instead of this:
$scope.result = $http.get('data.json').success(function(result){
return result.data.dom
})
Use this:
$http.get('data.json').success(function(result){
$scope.result = result.dom;
})
Plunk: http://plnkr.co/edit/Autg0V