calling controller function from link function - javascript

I am trying to call a function in controller from link function. From controller function, I have to call a rest service call. (I tried calling rest service from link function , But I didn't get success. So I am trying to call controller function from link and from there I will call rest service).
My code is as below.
app.directive('collection', function() {
return {
restrict: "E",
replace: true,
scope: {
collection: '=',
articleData: '=',
articleContent: '='
},
template: "<ul><member ng-repeat='member in collection' member='member' article-data='articleData' article-content='articleContent'></member></ul>"
}
});
app.directive('member', function($compile,$http) {
return {
restrict: "A",
replace: true,
scope: {
member: '=',
articleData: '=',
articleContent: '='
},
template: "<div><li><a href='#' ng-click='getContent(member.itemId)'>{{member.title}}</a></li></div>",
link: function(scope, element, attrs) {
scope.getContent = function(itemId) {
scope.testFunction(itemId);
}
if (angular.isArray(scope.member.tocItem)) {
if (scope.member.hasChildren == "true") {
for (var i = 0; i < scope.member.tocItem.length; i++) {
if (scope.member.tocItem.title) {
scope.member.tocItem.title.hide = true;
}
}
}
element.append("<collection collection='member.tocItem'></collection>");
$compile(element.contents())(scope)
}
}
}
});
app.controller('apdController', function($scope, getTocService,$location) {
var bookId = $location.search().id;
var sampdata = getTocService.getToc(bookId);
$scope.tasks =sampdata;
//$scope.tasks = data;
var artData = getTocService.getArtData('PH1234');
$scope.articleContent = artData;
$scope.testFunction = function(itemId){
alert("called.....");
}
});
Here, I am trying to call testFunction from link.From testFunction, I am planning to call a rest service. But getting undefined is not a function error.
Can someone please help? Also, please let me know is this a right approach (from link function to controller and from controller to rest call . Since my time line is less, I couldn't think of some other approach)

What does testFunction do? You have different options. If testFunction calls a rest service then you should definitely move it to a service.
app.factory('restService', function($http) {
function testFunction(itemId){
// Whatever you need
return $http.get('myUrl');
}
return {
testFunction: testFunction
}
});
Then inject it in your directive
app.directive('member', function($compile, $http, restService) {
...
link: function(scope, element, attrs) {
scope.getContent = function(itemId) {
restService.testFunction(itemId);
}
....
}
});
This way, you can also call it in your controller if you ever need it.
If instead you have to communicate something to the controller then you can use signals.

Related

How can i update a directive $scope from other directive contoller in Angular.js?

I have a diretive with a list of events loading from my service service:
.directive('appointments', [function () {
return {
restrict: 'CE',
scope: {
ngTemplate: '=',
},
controller: ['$scope','calendarService', function ($scope, calendarService) {
var vm = this;
vm.events = calendarService.getEvents();
}],
controllerAS:'vm',
link: function (scope, elem, attrs) {
scope.getTemplateUrl = function () {
if (angular.isDefined(scope.ngTemplate))
return scope.ngTemplate;
else
return "/list.directive.html";
}
},
template: '<div ng-include="getTemplateUrl()"></div>'
}
}])
Now in another directive i am updating this list, how can i update the list in the first controller?
.directive('appointmentsUpdate', [function () {
return {
restrict: 'CE',
scope: {
ngEventId: '=',
},
controller: ['$scope','calendarService', function ($scope, calendarService) {
var vm = this;
vm.update = calendarService.updateEvent(scope.ngEventId).then(function(res){
// Here is where the newly created item, should be added to the List (vm.events) from first directive
)
});
}],
controllerAS:'vm',
link: function (scope, elem, attrs) {
scope.getTemplateUrl = function () {
if (angular.isDefined(scope.ngTemplate))
return scope.ngTemplate;
else
return "/list.directive.html";
}
},
template: '<div ng-include="getTemplateUrl()"></div>'
}
}])
you can use angular broadcast service for this:
in first directive use this:
$rootScope.$broadcast('greeting', data_needs_to_be_send);
in other directive listen the event to update its scope:
$scope.$on('greeting', listenGreeting)
function listenGreeting($event, message){
alert(['Message received',message].join(' : '));
}
We use require property to make communication between directives.
Something like this
return {
restrict: 'AE',
require: '^ParentDirective or ^SameLevelDirective'
}
Here is the clear explanation of Driectives That Communicate by ToddMotto
Services are singletons, so if you update the list from one place (with your calendarService.updateEvent()), then if you retrieve the data from the service in the other directive, it should be the updated list.
You could use a watch to check when the list is updated:
$scope.$watch(() => calendarService.getEvents(), (newValue, oldValue) => {
// update your scope with the new list
}, true);

Decorating directive with unnamed link function

How can one derocate directive if link function is not named. More specifically I want to decorate select-ui directive. This is how their directive is defined:
.directive('uiSelect', function() {
return {
restrict: 'EA',
scope: true,
compile: function(tElement, tAttrs) {
//some code
return function(scope, element, attrs, ctrls){
//some code here
//how do I extend this?
}
}
}
});
This is how I tried, but it's giving me error Cannot read property 'apply' of undefined, because link function is not named in directive :
.decorator('uiSelectDirective', function($delegate, Restangular){
var directive;
directive = $delegate[0];
console.log(directive, $delegate);
directive.scope = {
endpoint: '=',
items: '=',
parameters: '='
};
directive.compile = function() {
return function($scope, element, attrs){
directive.link.apply(this, arguments);
// custom code here
}
};
return $delegate;
});
EDIT
I tried the suggested approach, but i'm running into issues.
.decorator('uiSelectDirective', function($delegate, Restangular){
var directive;
directive = $delegate[0];
directive.scope = {
endpoint: '=',
items: '=',
parameters: '='
};
directive.compile = function(originalCompile) {
var args = arguments;
return function($scope, element, attrs){
console.log($scope); // this here is returning element instead of scope?
return originalCompile.apply(this, args);
}
}(directive.compile);
return $delegate;
});
Instead of $scope I'm getting element variable ([div.ui-select-container.ui-select-bootstrap.dropdown]). I also have error saying that tAttrs is not defined, which is the parameter in main compule function. I'm guessing that apply isn't passing arguments into function somehow...
Do something this like this to extend compile function:
directive.compile = function(originalCompile) {
return function() {
var oldLink = originalCompile.apply(this, arguments);
return function($scope, element, attrs) {
// custom code for link here
return oldLink.apply(this, arguments)
}
}
}(directive.compile);

Returning Callback from Angular, Link or Controller Directives?

I want to create a table directive, I wanted to know how to pass a callback to for example: "Calling a delete function that is not in the Controller", I was wanting to pass as a callback using parameters, but for some time I am hitting my head with it. When I can return the callback through the "link" I can not return the values, and the controller can not.
Imagem when I use link , but don't back values of callback
Image when I use Controller
View DashBoard
<div crud-table options='vm.options' callback='vm.myCallBack(response)'></div>
Controller DashBoard
(function() {
'use strict';
angular
.module('senhalivre')
.controller('DashboardMikrotikCtrl', DashboardMikrotikCtrl);
DashboardMikrotikCtrl.$inject = [];
function DashboardMikrotikCtrl() {
var vm = this;
vm.myCallBack = myCallBack;
vm.options = {
enabled : true,
msg : 'DashBoard',
// Configs etc.....
}
//////////////////
function myCallBack(response){
console.log('Callback !!! ');
console.log(response);
}
}
})();
Directive
(function() {
'use strict';
angular
.module('senhalivre')
.directive('crudTable', crudTable);
crudTable.$inject = [];
function crudTable() {
var directive = {
bindToController: true,
controller: crudTableCtrl,
templateUrl : './views/crud/crudTable.html',
controllerAs: 'vm',
link: link,
restrict: 'EA',
scope: {
options : '=',
callback : '&callback'
}
};
return directive;
////////////////
function link(scope, element, attrs, ctrl) {
scope.vm.callback({test : 'something'});
// Work
}
}
crudTableCtrl.$inject = [];
function crudTableCtrl() {
var vm = this;
vm.callback({test : 'something'});
// :(
}
})();
You need to provide the property 'response' in the parameter-object when calling the callback:
function link(scope, element, attrs, ctrl) {
scope.vm.callback({response: 'some response text'});
}
or
function crudTableCtrl() {
this.callback({response: 'something'});
}

Angular js : How to make make ajax call for directives to create options

I created directives for form controls.
There are some controls those will have options from ajax[API] call.
I am stuck here how to make ajax call over directives.
function selectControlDir()
{
return {
transclude: true,
restrict: 'E',
scope: {
data: '=data'
},
template: "<div ng-transclude></div><label>{{data._text}} </label><select type='text' name='{{data._attributeName}}' id='{{data._attributeName}}' >\n\
<option ng-repeat='ans in data._answerOptions'>{{ans._promptText}}</option></select>"
,
link: function (scope, element, attrs)
{
//console.log("scope.data.QuestionData", scope.data.QuestionData);
}
};
}
Plunker for complete code
http://plnkr.co/edit/Op1QDwUBECAosPUC7r3N?p=preview
Here I want to make api call for Year (on page load).
on year change update options of Make.
What you can do for this is better to have a ng-change event directive on the year element and in the controller you can use to have an array which holds all the make happened in that year:
var app = angular.module('yourApp', []);
app.controller('yourController', ['$scope', '$http',
function($scope, $http) {
$scope.o = {
makes: [{make:'Make1'}, {make:'Make2'}], // <---just for demo
getMake: function(year) {
$http.post(url, {
year: year // <----pass the year to backend as year like : { year:1990 }
})
.then(function success(response) {
$scope.o.makes = response.data; // <---put actual data here
},
function error(e) {
console.error(e);
});
}
};
}
]);
<div ng-app='yourApp' ng-controller='yourController'>
<select ng-model='year' ng-change='o.getMake(year)'>
<option>1990</option>
<option>1991</option>
</select>
<select ng-model='make' ng-options='make as make.make for make in o.makes track by make.make'>
<option>Make...</option>
</select>
</div>
You should get the options by angular service. The angular service will send the AJAX call and return the result (and even some default data in case of failure).
How to do it? Here is an example:
var app = angular.module('app',[]);
//Service
app.factory('myService', function(){
function getOption() {
return 'getOption';
}
return {
getOption: getOption
}
});
app.directive('myDirective', ['myService',function(myService){
return {
template: '<h1>{{name}}</h1>'
link: function (scope, element, attrs) {
scope.name = myService.getOption();
}
}
}]);
Use can use $http to make ajax call. You need to inject it to use it. Better solution is to move the service calls to factory/service.
function selectControlDir($http)
{
return {
transclude: true,
restrict: 'E',
scope: {
data: '=data'
},
template: "<div ng-transclude></div><label>{{data._text}} </label
> ><select type='text' name='{{data._attributeName}}' id='{{data._attributeName}}' >\n\ <option ng-repeat='ans in
> data._answerOptions'>{{ans._promptText}}</option></select>"
,
link: function (scope, element, attrs)
{
// $http.post(url, options); => make your call here
//console.log("scope.data.QuestionData", scope.data.QuestionData);
}
};
}
The best way is to create a service or a factory and inject it into your directive.
app.service("serviceName", ["$http", function($http) {
return {
getData: function(configObject) {
// $http.get(..., post... etc
}
}
}]);
You can use it in your link or in controller statement.
selectControlDir(serviceName) {
return{
...
link: function() {
selectControlDir.getData({ url: ..., getParam1: 123})
}
... or ...
controller: function() {
selectControlDir.getData({ url: ..., getParam1: 123})
}
}
}

How to access a require'd controller from another controller?

I have an Angular 1.3 module that looks something like this (directive that requires the presence of a parent directive, using controllerAs):
angular.module('fooModule', [])
.controller('FooController', function ($scope) {
this.doSomething = function () {
// Accessing parentDirectiveCtrl via $scope
$scope.parentDirectiveCtrl();
};
})
.directive('fooDirective', function () {
return {
// Passing in parentDirectiveCtrl into $scope here
link: function link(scope, element, attrs, parentDirectiveCtrl) {
scope.parentDirectiveCtrl = parentDirectiveCtrl;
},
controller: 'FooController',
controllerAs: 'controller',
bindToController: true,
require: '^parentDirective'
};
});
Here I'm just using $scope to pass through parentDirectiveCtrl, which seems a little clunky.
Is there another way to access the require-ed controller from the directive's controller without the linking function?
You must use the link function to acquire the require-ed controllers, but you don't need to use the scope to pass the reference of the controller to your own. Instead, pass it directly to your own controller:
.directive('fooDirective', function () {
return {
require: ["fooDirective", "^parentDirective"],
link: function link(scope, element, attrs, ctrls) {
var me = ctrls[0],
parent = ctrls[1];
me.parent = parent;
},
controller: function(){...},
};
});
Be careful, though, since the controller runs prior to link, so within the controller this.parent is undefined, until after the link function runs. If you need to know exactly when that happens, you can always use a controller function to pass the parentDirective controller to:
link: function link(scope, element, attrs, ctrls) {
//...
me.registerParent(parent);
},
controller: function(){
this.registerParent = function(parent){
//...
}
}
There is a way to avoid using $scope to access parent controller, but you have to use link function.
Angular's documentation says:
Require
Require another directive and inject its controller as the fourth
argument to the linking function...
Option 1
Since controllerAs creates namespace in scope of your controller, you can access this namespace inside your link function and put required controller directly on controller of childDirective instead of using $scope. Then the code will look like this.
angular.module('app', []).
controller('parentController', function() {
this.doSomething = function() {
alert('parent');
};
}).
controller('childController', function() {
this.click = function() {
this.parentDirectiveCtrl.doSomething();
}
}).
directive('parentDirective', function() {
return {
controller: 'parentController'
}
}).
directive('childDirective', function() {
return {
template: '<button ng-click="controller.click()">Click me</button>',
link: function link(scope, element, attrs, parentDirectiveCtrl) {
scope.controller.parentDirectiveCtrl = parentDirectiveCtrl;
},
controller: 'childController',
controllerAs: 'controller',
bindToController: true,
require: '^parentDirective'
}
});
Plunker:
http://plnkr.co/edit/YwakJATaeuvUV2RBDTGr?p=preview
Option 2
I usually don't use controllers in my directives at all and share functionality via services. If you don't need to mess with isolated scopes of parent and child directives, simply inject the same service to both of them and put all functionality to service.
angular.module('app', []).
service('srv', function() {
this.value = '';
this.doSomething = function(source) {
this.value = source;
}
}).
directive('parentDirective', ['srv', function(srv) {
return {
template: '<div>' +
'<span ng-click="srv.doSomething(\'parent\')">Parent {{srv.value}}</span>' +
'<span ng-transclude></span>' +
'</div>',
transclude: true,
link: function(scope) { scope.srv = srv; }
};
}]).
directive('childDirective', ['srv', function(srv) {
return {
template: '<button ng-click="srv.doSomething(\'child\')">Click me</button>',
link: function link(scope) { scope.srv = srv; }
}
}]);
Plunker
http://plnkr.co/edit/R4zrXz2DBzyOuhugRU5U?p=preview
Good question! Angular lets you pass "parent" controller. You already have it as a parameter on your link function. It is the fourth parameter. I named it ctrl for simplicity. You do not need the scope.parentDirectiveCtrl=parentDirectiveCtrl line that you have.
.directive('fooDirective', function () {
return {
// Passing in parentDirectiveCtrl into $scope here
link: function link(scope, element, attrs, ctrl) {
// What you had here is not required.
},
controller: 'FooController',
controllerAs: 'controller',
bindToController: true,
require: '^parentDirective'};});
Now on your parent controller you have
this.doSomething=function().
You can access this doSomething as
ctrl.doSomething().

Categories

Resources