Angular pass function arguments from directive to controller - javascript

Im trying to implement directive pagination and i want to pass the number of the current page from the directive to a controller and then run a function from the controller with this argument but im getting undefined.
<account-pagination pagination-config="paginationConfig" on-change="pageChanged()">
</account-pagination>
controller:
$scope.pageChanged = function(page) {
console.log(page); // undefined
}
directive:
scope: {
paginationConfig: '=',
onChange: '&',
}
$scope.moveToPage = function(numPage) {
$scope.currentPage = numPage;
getPaginData(numPage);
}
function getPaginData(numPage) {
$scope.onChange({page: numPage});
}
template directive:
<li ng-repeat="num in numPages"><a ng-click="moveToPage(num)" ng-class="{pageActive: isActive(num)}">{{num}}</a></li>

OK i find the solution:
<account-pagination pagination-config="paginationConfig" on-change="pageChanged">
</account-pagination>
scope: {
paginationConfig: '=',
onChange: '=',
}
$scope.pageChanged = function(page) {
console.log(page);
}
$scope.moveToPage = function(numPage) {
$scope.currentPage = numPage;
$scope.onChange(numPage);
}

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);

Call function in Directive when Parent Scope Variable Changes

I need to call a function in my directive when the value of variable in the parent controller changes. I tried adding a watch (I'm obviously doing it wrong) because nothing happens when the value changes. Here is the directive:
angular.module('ssq.shared').directive('checkboxPicklist', function() {
return {
restrict: 'E',
templateUrl: '/Scripts/app/Shared/directives/checkboxPicklist.html',
replace: true,
scope: {
itemId: '=',
list: '=',
nameProp: '=',
title: '#',
searchPlaceholder: '#',
callbackFn: '&',
callMore: '&',
clear: '='
},
link: function (scope, element, attrs) {
scope.query = '';
var parent = scope.$parent;
var clear = parent.clear;
scope.$watch(clear, function () {
if (clear == true) {
this.clearAll();
}
})
var child = element.find('.dropdown-menu');
child.on({
'click': function (e) {
e.stopPropagation();
}
});
var selectedItemFn = function (item) {
return item.selected;
};
scope.getSelectedCount = function () {
return _.filter(scope.list, selectedItemFn).length;
};
scope.loadMore = function () {
scope.callMore();
};
scope.allSelected = function(list) {
var newValue = !scope.allNeedsMet(list);
_.each(list, function(item) {
item.selected = newValue;
scope.callbackFn({ object: item });
});
};
scope.allNeedsMet = function(list) {
var needsMet = _.reduce(list, function(memo, item) {
return memo + (item.selected ? 1 : 0);
}, 0);
if (!list) {
return (needsMet === 0);
}
return (needsMet === list.length);
};
function clearAll() {
_.each(list, function (item) {
item.selected = false;
})
}
}
};
});
Here is where I am trying to watch the variable:
var parent = scope.$parent;
var clear = parent.clear;
scope.$watch(clear, function () {
if (clear == true) {
this.clearAll();
}
})
Here is the function in my parent controller that changes the value of "clear"
$scope.clearFilters = function (clear) {
$scope.clear = true;
$scope.form.selected.services = [];
$scope.form.picked.areas = [];
$scope.form.certified.verifications = [];
$scope.form.subscribed.subscriptions = [];
$scope.form.OperatorBusinessUnitID = null;
$scope.form.OperatorBusinessUnitID = null;
};
I tried setting an attribute called "clearFilter" and assigning the variable to it, but the watch still doesn't trigger:
scope.$watch(attrs.clearFilter, function (value) {
if (value == true) {
this.clearAll();
}
});
<checkbox-picklist data-item-id="'servicesPicklist'"
data-search-placeholder="Search Services"
data-list="services"
data-title="Service(s)"
data-name-prop="'vchDescription'"
data-callback-fn="addService(object)"
call-more="loadMoreServices()"
clear-filter="clear">
</checkbox-picklist>
I'm not really sure if I am calling the function correctly. scope.$parent above does get the initial value of the variable from the parent scope, but once it changes, it never updates.
EDIT:What I have discovered is the normal scope.$watch('clear', function...) is not working it seems because the directive is in "ssq.shared" module which is injected in my my Main Module "myModule" (see below), so even though the page the directive is on uses my 'GeneralSearchCtrl', I cannot get the watch to work on the variable located in 'GeneralSearchCtrl'. If I use scope.$parent.clear I can see the value of the variable, but I cannot seem to set a watch on it.
My module injection code:
var app = angular.module('myModule', ['ui.bootstrap', 'checklist-model', 'ssq.shared', 'ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.pagination', 'ui.grid.selection', 'ui.grid.exporter', 'ui.grid.autoResize', 'ui.router', 'cgBusy', 'ui.mask', 'ngFileUpload', 'ngSanitize']);
The page where the directive lives uses:
<div ng-app="myModule" ng-controller="GeneralSearchCtrl">
I am unable to get a watch on the variable located in GeneralSearchCtrl.
Any assistance is greatly appreciated!!!!
Add a watch for the $scope value and call the function,
scope.$watch('clear', function(newValue, oldValue) {
if (newValue) {
this.clearAll();
}
});
scope.$watch(clear, function () {
if (clear == true) {
this.clearAll();
}
})
This.clearAll() doesn't exist in the scope of your $watch function. Simply calling clearAll() should work better.
The signature of the watch function is not correct.
scope.$watch('clear', function (new, old) {}
As it turns out, the problem was that the directive had scope:{...} in its definition which stopped the "normal" scope.$watch('clear', function...) from working. I had to add clear: '=' to the scope list like so:
replace: true,
scope: {
itemId: '=',
list: '=',
nameProp: '=',
title: '#',
searchPlaceholder: '#',
callbackFn: '&',
callMore: '&',
clear: '='
},
Then clear="clear" to the directive like so:
<checkbox-picklist data-item-id="'servicesPicklist'"
data-search-placeholder="Search Services"
data-list="services"
data-title="Service(s)"
data-name-prop="'vchDescription'"
data-callback-fn="addService(object)"
call-more="loadMoreServices()"
clear="clear">
</checkbox-picklist>
Then in the directive I had to add the watch like this for it work:
scope.$watch('$parent.clear', function (newValue, oldValue) {
if (newValue == true) {
clearAll();
alert('it works!');
}
})
I really hope this helps someone else as this was difficult for me to figure out. Happy coding!

Directive can't use data resolved by a controller

Consider the initial app main state:
$stateProvider.state('main', {
url: '',
views: {
'nav#': {
templateUrl: baseTemplatesUrl + 'nav.main.html',
controllerAs: 'vm',
controller: 'mainNavController',
resolve: {
timelinesService: 'timelinesService'
}
},
'content#': {
template: ''
}
}
});
Inside the nav.main.html template I'm trying to use a simple slider directive:
<ul>
<li ng-repeat="t in vm.timelines">
<!-- ui-sref=".timeline({yearFrom: t.yearFrom, yearTo: t.yearTo})" -->
<a class="button tiny" ui-sref-active="active">{{t.text}}</a>
</li>
</ul>
<div simple-slider items="vm.timelines" on-item-clicked="vm.timelineClicked"></div>
The directive is defined as:
function simpleSlider() {
return {
restrict: 'EA',
templateUrl: baseTemplatesUrl + 'directive.simpleSlider.html',
replace: true,
scope: {
items: '=',
onItemClicked: '&'
},
link: function(scope, el, attr, ctrl) {
scope.curIndex = 0;
scope.next = function() {
scope.curIndex < scope.items.length - 1 ? scope.curIndex++ : scope.curIndex = 0;
};
scope.prev = function() {
scope.curIndex > 0 ? scope.curIndex-- : scope.curIndex = scope.items.length - 1;
};
scope.$watch('curIndex', function() {
scope.items.forEach(function(item) {
item.active = false;
});
scope.items[scope.curIndex].active = true;
});
},
};
}
The problem
On a page load the directive's $watch throws an exception saying that scope.items[scope.curIndex] is undefined, whereas the ng-repeat="t in vm.timelines" inside the nav.main.html template renders successfully.
Why the watch fails / how to pass vm.timelines to directive's scope?
The fiddle
https://jsfiddle.net/challenger/Le5p9aup/
Update 1. Regarding #Lex answer
The fiddle is working. But my setup differs from the given fiddle. In my setup the timelinesService returns the $http promise:
var service = {
getTimelines: function() {
return $http.get('/api/timelines');
}
};
then inside the navMainController the vm.timelines array gets populated:
vm.timelines = [];
timelinesService.getTimelines().then(function(response) {
vm.timelines = response.data.timelines;
}).catch(function(error) {
console.log(error);
});
Then, unless you click the directive's prev/next button, the $watch will fail:
// scope.items[scope.curIndex] is undefined
scope.items[scope.curIndex].active = true;

calling controller function from link function

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.

Set Directive Angular after rendering view

I want to load my Angular Directive when my class change.
I have a directive who get the route and when this directive can get a route she set on a class in a div child.
But when the div child have the class, the directive don't run.
Route directive :
angular.module('myApp').directive('index', ['$rootScope','usersEntity', '$location', '$timeout',function($rootScope,usersEntity, $location, $timeout){
return {
restrict: 'E',
templateUrl: 'index',
replace: true,
controller: function ($scope, $element, $attrs) {
$scope.user = function () {
return usersEntity;
};
$scope.location = function () {
return $location.path().replace('/', '');
};
$rootScope.$on('$locationChangeStart', function (event) {
$scope.routeMap = {
'login': 'users-log'
};
if ($scope.location().length < 1) {
$scope.route = '';
} else {
$.each($scope.routeMap, function (index, value) {
if (index == $scope.location()) {
$scope.route = value;
}
});
}
});
},
link: function() {
}
}
}]);
Thanks and sorry for my perfect English ;)

Categories

Resources