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/
Related
my directive:
(function () {
var directive = function ($compile, $http, $rootScope, $translate) {
return {
restrict: 'E',
scope: {
baseUrl: '#rmsUrl',
},
link: function (scope, element, attrs) {
$rootScope.languageSwitcher = {
toggle: true,
changeLanguage: function () {
if ($rootScope.languageSwitcher.toggle) {
$translate.use('ENG');
} else {
$translate.use('FRE');
}
}
}
$rootScope.$on('oauth2:authSuccess', function (data) {
var html2 = 'French <label class="switch">'
html2 += '<input type="checkbox" ng-model="languageSwitcher.toggle" ng-change="languageSwitcher.changeLanguage()" /><div></div>'
html2 += '</label>English'
element.html(html2);
$compile(element.contents())(scope);
});
}
};
};
angular.module('testingApp')
.directive('rmsLanguageToggler', directive);
}());
my index file contain the directive:
<rms-language-toggler rms-url='blah blah blah'></rms-language-toggler>
my question:
The html render correctly, however changing/clicking the checkbox doesn't trigger the function: $rootScope.languageSwitcher.changeLanguage()
I found out why now. Turns out the ng-change and ng-model inside the directive is still referencing to the inner isolated scope. I have to use $parent. to access the outer model, i.e. $parent.languageSwitcher.toggle
I'm trying to build a header directive that:
If logged in, should display the username and a Log Out button
If logged out, should hide the above-mentioned things
I'm using a custom login service that captures this information, and broadcasts the events login and logout. I'm successfully listening to these events in both the header's controller and directive.
How can I reload the directive on these events?
loginService.js:
angular.module("app")
.service("loginService", ["$http", "$rootScope", function ($http, $rootScope) {
var loggedIn = false,
_username = "";
this.logIn = function (username, password) {
// do some validation...
loggedIn = ...validation was successful;
_username = username;
if (loggedIn) {
$rootScope.$broadcast("login");
}
};
this.getUsername = function () {
return _username;
};
this.isLoggedIn = function () {
return loggedIn;
};
this.logOut = function () {
loggedIn = false;
$rootScope.$broadcast("logout");
};
}]);
headerController.js
angular.module("app")
.controller("headerController", ["loginService", "$rootScope", "$location", function (loginService, $rootScope, $location) {
this.isLoggedIn = loginService.isLoggedIn();
this.username = "";
$rootScope.$on("login", function (event) {
this.isLoggedIn = loginService.isLoggedIn();
this.username = loginService.getUsername();
});
this.logOut = function () {
loginService.logOut();
this.isLoggedIn = loginService.isLoggedIn();
this.username = "";
$location.path("/login"); // redirecting
};
}]);
header.html:
<header ng-controller="headerController as header">
<span ng-if="header.isLoggedIn">{{header.username}} <button ng-click="header.logOut()">Log Out</button></span>
</header>
headerDirective.js
angular.module("app")
.directive("header", function () {
return {
restrict: "A",
transclude: false,
templateUrl: "app/header/header.html",
controller: "headerController",
link: function (scope, element, attrs) {
scope.$on("login", function (event) {
// show the ng-if in header.html??
});
scope.$on("logout", function (event) {
// hide the ng-if in header.html??
});
}
};
});
I'm using this as <div header></div>.
It looks like there are some fundamental issues with the directive that will not allow this to work:
1) Declared as an Attribute Directive:
You've create a header attribute directive: restrict: "A", but you are using it as an element directive: <header ng-controller...</header>. restrict property should be restrict: "E". Or you haven't used the directive as others have commented.
2) Transclude is false
You have set transclude to false but you are attempting to use the directive with contents so transclude should be true.
To solve your issue I would suggest this as a solution:
1. Declare your header directive in its parent container view as just this.
<ian-header></ian-header>
ianHeader.html
<header>
<span ng-if="header.isLoggedIn">{{header.username}} <button ng-click="header.logOut()">Log Out</button></span>
</header>
ianHeader.js
angular.module("app")
.directive("ianHeader", function () {
return {
restrict: "E",
templateUrl: "app/header/ianHeader.html",
link: function (scope, element, attrs) {
scope.header = {isLoggedIn: false};
scope.$on("login", function (event) {
// show the ng-if in header.html??
scope.header.isLoggedIn = true;
});
scope.$on("logout", function (event) {
// hide the ng-if in header.html??
scope.header.isLoggedIn = false;
});
}
};
});
If you would provide a JS snippet would be easier but anyway one approach might be:
angular.module("app")
.directive("header", function () {
return {
restrict: "A",
transclude: false,
templateUrl: "app/header/header.html",
controller: "headerController",
link: function (scope, element, attrs) {
scope.$on("login", function (event)
{
//header should be the controllerAs you declared
//If you would provide JS snippet would be easier to debbug
scope.$parent.header.isLoggedIn= true;
// show the ng-if in header.html??
});
scope.$on("logout", function (event)
{
scope.$parent.header.isLoggedIn = false;
// hide the ng-if in header.html??
});
}
};
});
Your solution is not great, usage of event broadcasting is always prone to errors and generally difficult to test and debug. What you need to do is to create a service which stores the current profile object and store a reference to it in the header directive (and other services which may use/modify the current user).
The Service
'use strict';
(function() {
function AuthService() {
var Auth = {
User: User //call some API for user authentication if using sessions
};
return Auth;
}
angular.module('app')
.factory('Auth', AuthService);
})();
Your header directive
'use strict';
//do not use "header" as the name of directive
angular.module('app')
.directive('navbar', () => ({
templateUrl: 'components/navbar.html',
restrict: 'E',
controller: function(Auth){
this.user = Auth.User;
},
controllerAs: 'navbar'
}));
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 have read this post. However, in that example he calls the controller function after listening on a click event of the element.
How can I achieve calling a controller function when clicking children of the directive element?
<div ng-controller="MyCtrl">
<abc method1="outerMethod('c')" method2="outerMethod2('g')"></abc>
</div>
Directive:
var myApp = angular.module('myApp',[]);
myApp.directive('abc', function() {
return {
restrict: "EA",
replace: true,
template: "<div><p ng-click='clickedP()'>p</p><div ng-click='clickedDiv()'>div</div></div>",
controller: function($scope) {
// how can I call outerMethod if clickedP is executed???
// how can I call outerMethod2 if clickedDiv is executed???
},
controllerAs: "vm",
link: function(scope, element, attrs, vm) {
}
}
});
function MyCtrl($scope) {
$scope.outerMethod = function( a ) {
alert( "you did it" );
}
$scope.outerMethod2 = function( a ) {
alert( "you did it again" );
}
}
Fiddle: http://jsfiddle.net/j93ba7a2/5/
The scope can be used directly without passing attributes. Also, using "controllerAs" on a directive with the same value as the parent controller is a bad idea, since it will overwrite it.
Solution:
var myApp = angular.module('myApp', []);
myApp.directive('abc', function () {
return {
restrict: "EA",
replace: true,
template: "<div><p ng-click='clickedP()'>p</p><div ng-click='clickedDiv()'>div</div></div>",
controller: function ($scope) {
// how can I call outerMethod if clickedP is executed???
$scope.clickedP = function () {
$scope.outerMethod(); // you just call it!
}
// how can I call outerMethod2 if clickedDiv is executed???
$scope.clickedDiv = function () {
$scope.outerMethod2(); //Same way!
}
},
controllerAs: "vm",
link: function (scope, element, attrs, vm) {
/* It would have been better to handle clickedP and
clickedDiv here instead of in the controller, but I'm
trying to avoid confusion by changing as little as
possible of your code. */
}
}
});
function MyCtrl($scope) {
$scope.outerMethod = function (a) {
alert("you did it");
}
$scope.outerMethod2 = function (a) {
alert("you did it again");
}
app.directive('mainCtrl', function () {
return {
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
link: function (scope, lElement, attrs, mainCtrl) {
mainCtrl.funcA()
}
};
});
I don't want to use the link method but the controller method.
Is there a way to get the mainCtrl in the controller method of the directive addProduct.
something like:
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
controller: function (scope, mainCtrl) {
mainCtrl.funcA()
}
};
});
You'd still need to use the link function because the controllers are injected there. What you could, however, is request your directive's own controller and then set the other required controller as its property:
app.directive('addProduct', function () {
return {
restrict: 'E',
require: ['addProduct','^mainCtrl'],
controller: function ($scope) {
// this.mainCtrl is still not set here
// this.mainCtrl.funcA(); // this will cause an error
// but typically it is invoked in response to some event or function call
$scope.doFuncA = function(){
this.mainCtrl.funcA();
}
},
link: function(scope, element, attrs, ctrls){
var me = ctrls[0], mainCtrl = ctrls[1];
me.mainCtrl = mainCtrl;
}
};
});
Since AngularJS 1.5, you can use the $onInit lifecycle hook of the controller. As written in the documentation of require, when defining require as an object and setting bindToController to true, the required controllers are added to the controller as properties after the controller has been constructed, but before the $onInit method is run. So the code would look like this:
app.directive('mainCtrl', function () {
return {
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: {
myParentController: '^mainCtrl'
},
bindToController: true,
controller: function ($scope) {
this.$onInit = function() {
this.myParentController.funcA();
};
}
};
});
Here is my solution:
app.directive('mainCtrl', function () {
return {
controllerAs: 'main',
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
controller: function ($scope) {
$scope.main.funcA();
}
};
});
Pass the controller to the scope on the link function then accessing the scope on controller. Like this:
app.directive('mainCtrl', function () {
return {
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
link: function (scope, lElement, attrs, mainCtrl) {
scope.ctrl=mainCtrl;
},controller:function($scope){
$scope.ctrl.funcA();
}
};
});