I've got a directive that needs to do something every now and then, let's say it has to count something.
If I use the basic syntax with $scope to bind the count function, it works just fine. but when we switch to the controller as syntax it doesn't bind the function.
Here is a working plunker: https://plnkr.co/edit/C2wVaeOm63SLnXBG?open=lib%2Fscript.js&deferRun=1&preview
JS
angular
.module('plunker', [])
.controller('MainCtrl', function ($scope) {
var vm = this;
vm.name = 'Plunker';
setInterval(function () {
$scope.count();
}, 1000);
setInterval(function () {
$scope.count2();
}, 1000);
setInterval(function () {
$scope.count3();
}, 1000);
})
.directive('test', function () {
return {
scope: {
count: '=',
},
controller: function ($scope) {
$scope.i = 0;
$scope.count = function () {
$scope.i++;
console.log($scope.i);
};
},
};
})
//with bindToController
.directive('test2', function () {
return {
scope: {
count: '=',
},
bindToController: true,
controller: function ($scope) {
var vm = this;
vm.i = 0;
vm.count = function () {
vm.i++;
console.log(vm.i);
};
},
};
})
//with bindToController - the new way
.directive('test3', function () {
return {
scope: true,
bindToController: {
count: '=',
},
controller: function ($scope) {
var vm = this;
vm.i = 0;
vm.count = function () {
vm.i++;
console.log(vm.i);
};
},
};
});
HTML
<body ng-app="plunker" ng-cloak>
<div ng-controller="MainCtrl as vm">
<h1>Hello {{vm.name}}</h1>
<test count="count"></test>
<test2 count="count2"></test>
<test3 count="count3"></test>
</div>
</body>
If you are using bindToController syntax, then you should declare your directive count function in directive link function, because binding is happening after directive controller initialisation.
Your modified example here:
//with bindToController
.directive('test2', function () {
return {
scope: {
count: '=',
},
bindToController: true,
controller: function ($scope) {
var vm = this;
vm.i = 0;
},
link:function($scope,$element,$attr,ctrl){
ctrl.count = function () {
ctrl.i++;
console.log('test2',ctrl.i);
};
}
};
})
Or you can check my modified plunker here:
https://plnkr.co/edit/TKTtObbDTcFFC9SS?open=lib%2Fscript.js&deferRun=1
angular
.module('plunker', [])
.controller('MainCtrl', function ($scope, $interval) {
var vm = this;
vm.name = 'Plunker';
̶s̶e̶t̶I̶n̶t̶e̶r̶v̶a̶l̶(̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶(̶)̶ ̶{̶
$interval(function () {
$scope.count();
}, 1000);
Use the $interval service.
AngularJS modifies the normal JavaScript flow by providing its own event processing loop. This splits the JavaScript into classical and AngularJS execution context. Only operations which are applied in the AngularJS execution context will benefit from AngularJS data-binding, exception handling, property watching, etc.
For more information, see
AngularJS $interval Service API Reference
Related
I tried many ways but i am not sure what i am missing I am getting controller name is not registered error in console.
I tried to even change name of controller but still no luck
Please suggest
AngularController.js
(function () {
'use strict';
angular
.module('mymod')
.controller('controller', controller);
controller.$inject = ['$location'];
(function ($) {
$.fn.menumaker = function (options) {
var cssmenu = $(this), settings = $.extend({
format: "dropdown",
sticky: false
}, options);
return this.each(function () {
$(this).find(".button").on('click', function () {
$(this).toggleClass('menu-opened');
var mainmenu = $(this).next('ul');
if (mainmenu.hasClass('open')) {
mainmenu.slideToggle().removeClass('open');
}
else {
mainmenu.slideToggle().addClass('open');
if (settings.format === "dropdown") {
mainmenu.find('ul').show();
}
}
});
cssmenu.find('li ul').parent().addClass('has-sub');
multiTg = function () {
cssmenu.find(".has-sub").prepend('<span class="submenu-button"></span>');
cssmenu.find('.submenu-button').on('click', function () {
$(this).toggleClass('submenu-opened');
if ($(this).siblings('ul').hasClass('open')) {
$(this).siblings('ul').removeClass('open').slideToggle();
}
else {
$(this).siblings('ul').addClass('open').slideToggle();
}
});
};
if (settings.format === 'multitoggle') multiTg();
else cssmenu.addClass('dropdown');
if (settings.sticky === true) cssmenu.css('position', 'fixed');
resizeFix = function () {
var mediasize = 1000;
if ($(window).width() > mediasize) {
cssmenu.find('ul').show();
}
if ($(window).width() <= mediasize) {
cssmenu.find('ul').hide().removeClass('open');
}
};
resizeFix();
return $(window).on('resize', resizeFix);
});
};
})(jQuery);
(function ($) {
$(document).ready(function () {
$("#cssmenu").menumaker({
format: "multitoggle"
});
});
})(jQuery);
})();
Directive.js
This is directive created to call on basepage
(function () {
'use strict';
angular.module('mymod')
.directive('myDirective', function () {
return {
restrict: 'E', //E = element, A = attribute, C = class, M = comment
scope: true,
//templateUrl: 'Views/Directives/Header/mytemplate.html',
templateUrl: '../Views/Directives/MainHeader/mytemplate.html',
//template: '<h1>Hello Directive: {{vm.title}}</h1>',
controller: 'controller',
controllerAs: 'vm'
}
});
})();
Below image shown is error message information
Update your code by having dependency array along with module initialization. Update your code to:
(function () {
'use strict';
angular
.module('mymod', [])
.controller('controller', controller);
controller.$inject = ['$scope', '$location'];
function controller($scope, $location) {
//Write controller code here
}
Then update your directive code to:
(function () {
'use strict';
angular.module('mymod')
.directive('myDirective', function () {
return {
restrict: 'E', //E = element, A = attribute, C = class, M = comment
scope: true,
//templateUrl: 'Views/Directives/Header/mytemplate.html',
templateUrl: '../Views/Directives/MainHeader/mytemplate.html',
//template: '<h1>Hello Directive: {{vm.title}}</h1>',
controller: controller,
controllerAs: 'vm'
}
});
})();
And if you want to use jquery in angularjs app make sure you inject it (script tag on index.html) before angularjs lib.
Demo Example
I am trying to bind a callback to a component through a template. The template contains instance of another directive. This just doesn't seems to work. I'm invoking the directive from a modal, not sure if this can cause a problem. I tried many of the solution suggested in previous questions and still no luck. I ran it with a debugger, and the '$ctrl.onSelectionChanged' is defined to be as it should:
function (locals) { return parentGet(scope, locals); }
My code:
my-component.js:
The inner-directive as no reference to the callback, should it have?
angular.module('myModule')
.component('myComponent', {
template: '<div class="container-fluid"> <inner-directive><button class="btn btn-default center-block" ng-click="$ctrl.onSelectionChange({items_list: $ctrl.selectedItems})">Button</button> </inner-directive> </div>',
bindings: {
$router: '<',
onSelectionChange: '&'
},
controller: MyComponentController
});
/* #ngInject */
function MyComponentController(MyService, $filter, $log, $q) {
var $ctrl = this;
$ctrl.$routerOnActivate = function () {
};
$ctrl.selectedItems = [];
}
calling-component-controller.js:
function CallingComponentCtrl(toastr, $scope, $uibModal, $log) {
var $ctrl = this;
$ctrl.loadDone = false;
$ctrl.grid = {
enableSorting: true,
data: [],
columnDefs: [
{name: 'id'},
{name: 'name'},
{name: 'description'}
],
enableRowSelection: true,
enableRowHeaderSelection: false,
multiSelect: false,
noUnselect: true,
onRegisterApi: function (gridApi) {
$ctrl.gridApi = gridApi;
}
};
this.$onInit = function () {
if (angular.isUndefined($ctrl.abc)) {
return;
}
syncData();
$ctrl.loadDone = true;
};
this.$onChanges = function () {
// TODO
};
function syncData(){
$ctrl.grid.data = $ctrl.abc;
}
$ctrl.myFoo = function(items_list) {
alert("This is never happening");
};
$ctrl.onPress = function (event) {
var modalInstance = $uibModal.open({
template: '<my-component on-selection-change="$ctrl.myFoo(items_list)"></my-component>',
windowClass: 'modal-window'
});
};
}
Any thoughts?
Use the $compile service
link: function(scope, element) {
var template = $compile('<div class="container-fluid"> <inner-directive><button class="btn btn-default center-block" ng-click="$ctrl.onSelectionChange({items_list: $ctrl.selectedItems})">Button</button> </inner-directive> </div>')(scope);
element.append(template);
}
Remember to inject the compile service to the directive function
Trying changing your child component to this:
.component('myComponent', {
template: '<div class="container-fluid"> <inner-directive><button class="btn btn-default center-block" ng-click="$ctrl.onSelectionChange({items_list: $ctrl.selectedItems})">Button</button> </inner-directive> </div>',
bindings: {
$router: '<'
},
require: {
parent: '^^CallingComponent'
},
controller: MyComponentController
});
With require you inherit the parent controller.
Then in the init function you can make the call:
function MyComponentController(MyService, $filter, $log, $q) {
this.$onInit = function() {
this.parent.myFoo(items_list);
}
var $ctrl = this;
$ctrl.$routerOnActivate = function () {};
$ctrl.selectedItems = [];
}
--Old answer
Try changing the template to:
<my-component on-selection-change="$ctrl.myFoo(items_list)"></my-component>
You're calling it from the $scope when it's declared as a controller function.
Well, I found the problem. While invoking the modal, I used a template used a component in this template. To the component, I passed a callback that is defined in the '$ctrl'. The problem was that the modal defined its own scope and couldn't reached this $ctrl. So I defined a controller to the modal, and called through it the function I needed. This is my solution, I highlighted the changes and adds:
calling-component-controller.js:
function CallingComponentCtrl(toastr, $scope, $uibModal, $log) {
var $ctrl = this;
....
$ctrl.myFoo = function(items_list) {
alert("This is never happening");
};
$ctrl.onPress = function (event) {
var modalInstance = $uibModal.open({
template: '<my-component on-selection-change="$ctrl.myNewFoo(items_list)"></my-component>',
**controllerAs: '$ctrl',**
windowClass: 'modal-window',
**controller: function($uibModalInstance){
var $ctrl = this;
$ctrl.myNewFoo= function(items_list) {
$uibModalInstance.close(items_list);
};
}**
});
**modalInstance.result.then(function(items_list) {
$ctrl.myFoo(items_list);
});**
};
}
This is a function I have added in localcacheService.
I want to add this function in directive.
LocalCacheServiceClear.prototype.isAvailable = function() {
this.cache.clear();
}
My directive is this:
(function() {
var MainApp = angular.module('MainApp');
MainApp.directive('Logout', function () {
return {
'restrict': 'E',
'templateUrl': 'directives/panels/Logout.html',
'controller': ["$scope","LocalCacheService", function($scope,LocalCacheService) {
console.log("Logout Controller called....");
$scope.Logout = function() {
window.sessionStorage.clear();
console.log('Log out');
}
}]
};
});
}());
How to add this function to this directive?
via Angular Service:
MainAppUserConvMod.service('LocalCacheServiceClear', LocalCacheServiceClear);
and then inject into your directive:
MainAppUserConvMod.directive('Logout', function (LocalCacheServiceClear) {
and use it
LocalCacheServiceClear.isAvailable()
I am creating a directive with angular and in that i am using kendo-window control. Now i want to open that kendo window on demand from controller. In simple words i want to call a method of directive from controller on button click.
Here is my code sample
sample.directive('workorderwindow', [initworkorderwindow]);
function initworkorderwindow() {
return {
link: function (scope, elements, attrs) {
},
restrict: 'E',
template: "<div data-kendo-window='window.control' data-k-options='window.config'> HELLOW RORLD </div>",
scope: {
},
controller: function ($scope) {
$scope.window =
{
control: null,
config: { title: 'HELLO WORLD', visible: false }
}
$scope.open = function () {
$scope.window.control.center().open();
}
}
}
}
HTML
<workorderwindow></workorderwindow>
Now i want to call that directive open method from my controller.
sample.controller('datacontroller', ['$scope', 'datafac', initcontroller]);
function initcontroller($scope, datafac) {
$scope.onsampleclick = function () {
//FROM HERE
}
It's probably a bad practice to directly call a function of your directive from a controller. What you can do is create a Service, call it from your controller and injecting this service in your directive. With a $watch you will be able to trigger your directive function.
The service between Controller and Directive
app.factory('myWindowService', function () {
return {
windowIsOpen : null,
openWindow: function () {
this.windowIsOpen = true;
},
closeWindow: function () {
this.windowIsOpen = false;
}
};
Your directive :
app.directive('workorderwindow', function () {
return {
restrict: 'E',
template: "<div>test</div>",
controller: function ($scope, myWindowService) {
$scope.windowService = myWindowService;
$scope.$watch("windowService.windowIsOpen", function (display) {
if (display) {
console.log("in directive");
//$scope.window.control.center().open();
}
// You can close it here too
});
}
};
})
And to call it from your controller
app.controller('datacontroller', function ($scope, myWindowService) {
$scope.open = function () {
myWindowService.openWindow();
}
// $scope.close = ...
});
Here a working Fiddle http://jsfiddle.net/maxdow/ZgpqY/4/
Well, I'm having some problems updating a progress bar (which is in a directive) from a controller.
here are some code snippets:
my directive:
angular.module('TestApp').directive('orderProgress', ['$window', OrderProgress]);
function OrderProgress($window) {
var directive = {
link: link,
restrict: 'A',
templateUrl: 'OrderProgress.html',
controller: 'ProgressController',
replace: true
};
return directive;
function link(scope, element, attrs) {}
}
controller for directive:
function ProgressController($scope, progressNumberService) {
$scope.progress = progressNumberService.getProgress();
}
progressNumberService just hides the detail for the amount of "progress":
var progress = 20;
var progressServiceInstance = {
incProgress: function() {
progress += 20;
},
decProgress: function() {
progress -= 20;
},
getProgress: function() {
return progress;
}
};
App.value('progressNumberService', progressServiceInstance);
of course the controller:
function Controller($scope, progressNumberService) {
$scope.nextStep = function() {
progressNumberService.incProgress();
};
$scope.prevStep = function() {
progressNumberService.decProgress();
};
}
I've created an example:
http://plnkr.co/edit/LtY4ZUG591Kd3mUKEmEF?p=catalogue
So why doesn't the directive get the update from the 'Controller', when the Next/Prev buttons are pressed?
So your issue is that the value is being updated in your .value module, but your directive controller is never calling getProgress once the values are updated. I would suggest using $broadcast and $on to send a message saying that the progress was updated. I tested this and it seemed to do the trick.
Controller:
angular.module('TestApp').controller(controllerId2, ['$scope', '$rootScope', 'progressNumberService', ProgressController]);
function ProgressController($scope, $rootScope, progressNumberService) {
$scope.progress = progressNumberService.getProgress();
$rootScope.$on("event:progress-change", function() {
$scope.progress = progressNumberService.getProgress();
});
}
And change your .value to a factory so you can use $rootScope to broadcast
App.factory('progressNumberService', function($rootScope) {
return {
incProgress: function() {
progress += 20;
$rootScope.$broadcast("event:progress-change");
},
decProgress: function() {
progress -= 20;
$rootScope.$broadcast("event:progress-change");
},
getProgress: function() {
return progress;
}
}
});
Here is the updated Plunker DEMO