How can I pass functions as params to an AngularJS Directive? - javascript

I can pass strings and objects, but for some reason when I add callback functions to my scope object that I'm using as configurable options, Angular strips them out.
HTML:
<div ng-controller="DemoCtrl">
scope.options = {{ options }}
<br><br>
<div my-directive="{{ options }}"></div>
</div>
JS:
var app = angular.module('demo2', []);
app.controller('DemoCtrl', function($scope) {
$scope.options = {
test_str: 'test',
init_cb: function() {
alert('custom init callback');
},
apply_cb: function() {
alert('custom apply callback');
}
};
});
app.directive('myDirective', ['$parse', function($parse) {
function link(scope, element, attrs) {
var options = attrs.myDirective;
scope.init(options);
}
return {
restrict: 'A',
transclude: true,
link: link,
controller: ['$scope', function($scope) {
$scope.defaults = {
init_cb: function() {
alert('default init callback');
},
apply_cb: function() {
alert('default apply callback');
}
};
$scope.settings = {};
$scope.init = function(options) {
$scope.settings = $.extend({}, $scope.defaults, $scope.options);
// init cb.
$scope.settings.init_cb();
// apply cb.
$scope.settings.apply_cb();
};
}]
};
}]);
Here is a fiddle: http://jsfiddle.net/revolunet/pHZNY/
Thanks for your help.

You are trying to use the attr, which can only be a string, when you need to just pass the actual variable into the scope:
var app = angular.module('demo2', []);
app.controller('DemoCtrl', function($scope) {
$scope.options = {
test_str: 'test',
init_cb: function() {
alert('custom init callback');
},
apply_cb: function() {
alert('custom apply callback');
}
};
});
app.directive('myDirective', ['$parse', function($parse) {
return {
restrict: 'A',
transclude: true,
scope: { //use scope to pass in variables
options: '=myDirective'
},
controller: ['$scope', function($scope) {
$scope.defaults = {
init_cb: function() {
alert('default init callback');
},
apply_cb: function() {
alert('default apply callback');
}
};
$scope.settings = {};
$scope.init = function(options) {
$scope.settings = angular.extend({}, $scope.defaults, $scope.options);
// init cb.
$scope.settings.init_cb();
// apply cb.
$scope.settings.apply_cb();
}();
}]
};
}]);

Related

injecting service into directive returns undefined

I am injecting a serivce into a directive and for some instance this service returns undefined can anyone explain what I am doing wrong?
Here is a plunker of the code below. https://plnkr.co/edit/H2x2z8ZW083NndFhiBvF?p=preview
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.players = ["A","B","C"];
});
app.factory('PlayerListS', [function() {
var playerList = [];
function getList() {
return playerList;
}
function addToList(name) {
playerList.push(name);
}
return {
addToList :addToList,
getList: getList
}
}]);
app.directive("player",['PlayerListS', function (PlayerListS) {
return {
restrict: 'E',
scope: {
person:'#person',
add:'&add'
},
replace: false,
templateUrl: "player.html",
controller: function($scope, $element, $compile) {
$scope.add = function(name) {
PlayerListS.addToList(name);
console.log(PlayListS.getList());
}
}
};
}]);
You have a typo in your console because of which the code is throwing an error. Change your directive the following way
app.directive("player",['PlayerListS', function (PlayerListS) {
return {
restrict: 'E',
scope: {
person:'#person',
add:'&add'
},
replace: false,
templateUrl: "player.html",
controller: function($scope, $element, $compile) {
$scope.add = function(name) {
debugger;
PlayerListS.addToList(name);
console.log(PlayerListS.getList());
}
}
};
}]);
Working Demo: https://plnkr.co/edit/HhmOYyoZAhm6vvXp3puC?p=preview

How to call another directive from directive angularjs

I want to call alertForm directive in loginForm directive. Where I want call 'alertForm' directive in 'loginForm' is highlighted as //i want to call here
alertForm directive
angular.module('myApp')
.directive('alertForm', function () {
return {
templateUrl: 'app/directives/alert/alertForm.html',
restrict: 'E',
scope: {
topic: '=topic',
description: '=description'
},
controller: function($scope) {
$scope.words = [];
this.showAlert = function() {
$scope.description.push("hello");
};
}
};
});
loginForm directive
angular.module('myApp')
.directive('loginForm', function() {
return {
templateUrl: 'app/directives/loginForm/loginForm.html',
restrict: 'E',
scope: {
successCallback: '&',
errorCallback: '&',
emailField: '='
},
link: function (scope, element, attrs) {
},
controller: function ($rootScope, $scope, authenticationService) {
$scope.loginFormData = {};
$scope.inProgress = false;
$scope.onLogin = function (form) {
if (form.$valid) {
$scope.inProgress = true;
authenticationService.loginUser('password', $scope.loginFormData).then(function () {
$scope.successCallback({formData: $scope.loginFormData});
}, function (err) {
$scope.inProgress = false;
if (err.message) {
**// i want to call here**
}
});
}
}
}
};
});
You can use require config of directive.
When a directive requires a controller, it receives that controller as
the fourth argument of its link function. Ref : Documentation
You can implement this in your code
angular.module(‘myApp')
.directive('loginForm', function() {
return {
templateUrl: 'app/directives/loginForm/loginForm.html',
restrict: 'E',
require:'alertForm',
scope: {
successCallback: '&',
errorCallback: '&',
emailField: '='
},
link: function (scope, element, attrs, alertFormCtrl) {
scope.alertFormCtrl = alertFormCtrl;
},
controller: function ($rootScope, $scope, authenticationService) {
$scope.loginFormData = {};
$scope.inProgress = false;
$scope.onLogin = function (form) {
if (form.$valid) {
$scope.inProgress = true;
authenticationService.loginUser('password', $scope.loginFormData).then(function () {
$scope.successCallback({formData: $scope.loginFormData});
}, function (err) {
$scope.inProgress = false;
if (err.message) {
// Calling showAlert function of alertFormCtrl
$scope.alertFormCtrl.showAlert();
}
});
}
}
}
};
});
Add the following line in the app/directives/loginForm/loginForm.html :
<alertForm topic="something" description = "something" ng-if="showAlert"></alertForm>
Now inside the loginForm directive's controller : // i want to call here
use
$scope.showAlert = true;
Note: you can use some variable to setup the topic and description as well inside the alertForm.

How to call controller function from directive in angular js ?

I am opening a dialog-box on click of button.I want to add endless scroll in that.
Problem:
When user scrolls at the end of dialog-box i want to call addMoreData() written in controller.
HTML of Dialog-box:
<modal-dialog show='modalShown' width='60%' height='325px' >
<div id='diaogContainer'>
<p>Modal Content Goes here<p>
</div>
</modal-dialog>
Controller:
sampleApp.controller('view3Controller', function($scope) {
$scope.modalShown = false;
$scope.toggleModal = function() {
$scope.modalShown = !$scope.modalShown;
}
**$scope.showMore = function(){
console.log('showMore');
}**
});
Directive of Dialog-box:
sampleApp.directive('modalDialog', function() {
return {
restrict: 'E',
scope: {
show: '='
},
replace: true, // Replace with the template below
transclude: true,
link: function(scope, element, attrs) {
scope.dialogStyle = {};
if (attrs.width)
scope.dialogStyle.width = attrs.width;
if (attrs.height)
scope.dialogStyle.height = attrs.height;
scope.hideModal = function() {
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay'ng-click='hideModal()'></div><div class='ng-modal-dialog' hello **scrolly='showMore()'** ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div></div></div>"
};
});
Directive to load more data:
sampleApp.directive('hello', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var raw = element[0];
element.bind('scroll', function () {
console.log(raw.scrollTop +'-------'+raw.offsetHeight+'----'+raw.scrollHeight);
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
// here is problem
// I am not able to call function through this attr
//
**scope.$apply(attrs.scrolly);**
}
});
}
};
});
You can't pass in a function to a directive through an attribute, you can however pass it through an isolate scope. Pass a reference to the function you wish to call to the directive:
sampleApp.directive('hello', function () {
return {
restrict: 'A',
scope:{
onScrollEnd:'&'
},
link: function (scope, element, attrs) {
var raw = element[0];
element.bind('scroll', function () {
console.log(raw.scrollTop +'-------'+raw.offsetHeight+'----'+raw.scrollHeight);
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.onScrollEnd();
}
});
}
};
});
Now assuming you have the addMoreData() function defined on your controller, you can pass it to the directive this this:
<div hello on-scroll-end='addMoreData()'></div>
EDIT
I think the problem is that the hello directive can't access functions on the parent controller since the modalDialog directive is using an isolated scope, therefore making everything o the parent controller invisible. Pass the function to through the isolate scope of the modalDialog Directive as well:
scope: {
show: '=',
onScrollEnd:'&'
},
you can try like this.
Directive part
var module = angular.module('direc');
module.directive("direcApp", ['$timeout', function ($timeout) {
return {
restrict: 'E',
templateUrl: "template/template.html",
compile: function (iel, iattr) {
return function (scope, el, attr) {
}
},
scope: {
type: "#",
items: '=',
onClick: '&',
val: "="
},
controller: function ($scope) {
$scope.selectItem = function (selectedItem) {
$scope.val = selectedItem;
if (angular.isFunction($scope.onClick)) {
$timeout($scope.onClick, 0);
}
};
}
};
}]);
Controler part
var app = angular.module('app', ['direc']);
app.controller("appCtrl", ['$scope', '$http', function ($scope, $http) {
var t = {
count: function () {
return $scope.$$watchersCount; // in angular version 4 get total page listener
},
val1: "",
onClick: function () {
console.log($scope.data.val1);
},
items: [{ text: 'Seçenek 1', value: '1' },
{ text: 'Seçenek 2', value: '2' },
{ text: 'Seçenek 3', value: '3' },
{ text: 'Seçenek 4', value: '4' },
{ text: 'Seçenek 5', value: '5' }]
};
angular.extend(this, t);
}]);
Html part
<div ng-controller="appCtrl as data">
<div><b>Watcher Count : {{data.count()}}</b></div>
<direc-app items="data.items"
val="data.val1"
on-click="data.onClick1()"
>
</selection-group>
</div>
Add data as parameter to directive: scope: { data: '='}, and in directive just data.push({name:'i am new object'})
Add function parameter to directive as suggested in previous answer.

Can an angular directive require its own controller?

This is a simple question but I can't seem to find any relevant documentation...
I'm trying to find out if an angular directive can both inherit a parent controller as well as its own. Consider the following examples:
Simple Inheritance From Self
app.directive('screen', function() {
return {
scope: true,
controller: function() {
this.doSomething = function() {
};
},
link: function($scope, el, attrs, ctrl) {
// ctrl now contains `doSomething`
}
}
});
Inheritance From Parent
app.directive('screen', function() {
return {
scope: true,
controller: function() {
this.doSomething = function() {
};
}
}
});
app.directive('widget', function() {
return {
scope: true,
require: '^screen',
link: function($scope, el, attrs, ctrl) {
// ctrl now contains `doSomething` -- inherited from the `screen` directive
}
}
});
There's even multiple inheritance...
app.directive('screen', function() {
return {
scope: true,
controller: function() {
this.doSomething = function() {
};
}
}
});
app.directive('widget', function() {
return {
scope: true,
require: ['^screen','^anotherParent'],
link: function($scope, el, attrs, ctrl) {
// ctrl[0] now contains `doSomething` -- inherited from the `screen` directive
// ctrl[1] now contains the controller inherited from `anotherParent`
}
}
});
What I can't figure out is how to make a directive inherit both a parent controller and its own. Like so:
app.directive('screen', function() {
return {
scope: true,
controller: function() {
this.doSomething = function() {
};
}
}
});
app.directive('widget', function() {
return {
scope: true,
require: '^screen',
controller: function($scope) {
// isolated widget controller
},
link: function($scope, el, attrs, ctrl) {
// I need the `screen` controller in ADDITION to the isolated widget controller accessible in the link
}
}
});
Is this possible/proper form (or is it some kind of anti-pattern I am unaware of)?
Well that turned out to be a lot more obvious than I thought... a little trial and error showed that a directive can actually require itself as well.
The proper way to inherit parent + local controllers seems to be:
app.directive('screen', function() {
return {
scope: true,
controller: function() {
this.doSomething = function() {
};
}
}
});
app.directive('widget', function() {
return {
scope: true,
require: ['^screen','widget'],
controller: function($scope) {
this.widgetDoSomething = function() {
};
},
link: function($scope, el, attrs, ctrl) {
// ctrl[0] contains the `screen` controller
// ctrl[1] contains the local `widget` controller
}
}
});

binding data to angularjs from external model

I've got directive and service in my app (declared in separate files):
Service:
(function(){
angular.module('core', [])
.factory('api', function() {
return {
serviceField: 100
};
})
})();
Directive:
(function(){
angular.module('ui', ['core'])
.directive('apiFieldWatcher', function (api) {
return {
restrict: 'E',
replace: true,
scope: true,
template: '<div>+{{apiField}}+</div>',
controller: function($scope) {
$scope.apiField = 0;
},
link: function (scope) {
scope.$watch(function(){return api.serviceField}, function(apiFld){
scope.apiField = apiFld;
});
}
}
});
})();
And in another separate file I have native model:
function Model() { this.fld = 0; }
Model.prototype.setFld = function(a) { this.fld = a; }
Model.prototype.getFld = function() { return this.fld; }
How can I bind (two way) my native this.fld field to value in my AngularJS service?
The solution is in using this code:
Model.prototype.setFld = function(a) {
this.fld = a;
injector.invoke(['$rootScope', 'api', function($rootScope, api){
api.setField(a);
$rootScope.$digest();
}]);
};
Model.prototype.getFldFromApi = function() {
var self = this;
injector.invoke(['api', function(api){
self.fld = api.getField();
}]);
};
http://plnkr.co/edit/nitAVuOtzGsdJ49H4uyl
i think it's bad idea to use $digest on $rootScope, so we can maybe use
var scope = angular.element( elementObject ).scope();
to get needed scope and call $digest for it

Categories

Resources