I have created a horizontal drop down menu using AngularJS.
The menu section is managed by an angular controller called menuController. Standard menu behavior is implemented, so that on hover main menu item gets highlighted unless it is disabled. On clicking the main menu item, the sub menu toggles. If Sub menu is in a open state, I want it to go away when user clicks anywhere else on the document. I tried to create a directive to listen for document click event but not sure on how to notify menu-controller about it. How should I implement this scenario in a AngularJS way?
Partially working Original Plunk without document click handling mechanism.
UPDATE:
Based on answered suggestion, I went with Brodcast approach and updated the script to reflect my latest changes. It is working as per my expectation. I made the globalController $broadcast a message and menuController subscribe to that message.
UPDATE 2: Modified code to inject global events definition data.
var eventDefs = (function() {
return {
common_changenotification_on_document_click: 'common.changenotification.on.document.click'
};
}());
var changeNotificationApp = angular.module('changeNotificationApp', []);
changeNotificationApp.value('appEvents', eventDefs);
changeNotificationApp.directive("onGlobalClick", ['$document', '$parse',
function($document, $parse) {
return {
restrict: 'A',
link: function($scope, $element, $attributes) {
var scopeExpression = $attributes.onGlobalClick;
var invoker = $parse(scopeExpression);
$document.on("click",
function(event) {
$scope.$apply(function() {
invoker($scope, {
$event: event
});
});
}
);
}
};
}
]);
changeNotificationApp.controller("globalController", ['$scope', 'appEvents',
function($scope, appEvents) {
$scope.handleClick = function(event) {
$scope.$broadcast(appEvents.common_changenotification_on_document_click, {
target: event.target
});
};
}
]);
//menu-controller.js
changeNotificationApp.controller('menuController', ['$scope', '$window', 'appEvents',
function($scope, $window, appEvents) {
$scope.IsLocalMenuClicked = false;
$scope.menu = [{
Name: "INTEGRATION",
Tag: "integration",
IsDisabled: false,
IsSelected: false,
SubMenu: [{
Name: "SRC Messages",
Tag: "ncs-notifications",
IsDisabled: false,
AspNetMvcController: "SearchSRCMessages"
}, {
Name: "Target Messages",
Tag: "advisor-notifications",
IsDisabled: false,
AspNetMvcController: "SearchTaregtMessages"
}]
}, {
Name: "AUDITING",
Tag: "auditing",
IsDisabled: true,
IsSelected: false,
SubMenu: []
}];
$scope.appInfo = {
Version: "1.0.0.0",
User: "VB",
Server: "azzcvy0623401v",
IsSelected: false
};
var resetMenu = function() {
angular.forEach($scope.menu, function(item) {
item.IsSelected = false;
});
$scope.appInfo.IsSelected = false;
};
$scope.toggleDropDownMenu = function(menuItem) {
var currentDropDownState = menuItem.IsSelected;
resetMenu($scope.menu, $scope.appInfo);
menuItem.IsSelected = !currentDropDownState;
$scope.IsLocalMenuClicked = true;
};
$scope.loadPage = function(menuItem) {
if (menuItem.AspNetMvcController)
$window.location.href = menuItem.AspNetMvcController;
};
$scope.$on(appEvents.common_changenotification_on_document_click,
function(event, data) {
if (!$scope.IsLocalMenuClicked)
resetMenu($scope.menu, $scope.appInfo);
$scope.IsLocalMenuClicked = false;
});
}
]);
UPDATE 3: Modified code in previous implementation to fix a bug where document click fires multiple times. Almost similar approach, but this time, if any one clicks again anywhere on the menu, the click is ignored. Please refer to the New Working Plunk for full code example
changeNotificationApp.directive("onGlobalClick", ['$document', '$parse',
function ($document, $parse) {
return {
restrict: 'A',
link: function ($scope, $element, $attributes) {
var scopeExpression = $attributes.onGlobalClick;
var invoker = $parse(scopeExpression);
$document.on("click",
function (event) {
var isClickedElementIsChildOfThisElement = $element.find(event.target).length > 0;
if (isClickedElementIsChildOfThisElement) return;
$scope.$apply(function () {
invoker($scope, {
$event: event
});
});
}
);
}
};
}
]);
UPDATE 4: Implemented another alternate option. Please refer to the Option 2 Plunk for full code example
var eventDefs = (function () {
return {
on_click_anywhere: 'common.changenotification.on.document.click'
};
}());
var changeNotificationApp = angular.module('changeNotificationApp', []);
changeNotificationApp.value('appEvents', eventDefs);
changeNotificationApp.directive("onClickAnywhere", ['$window', 'appEvents',
function($window, appEvents) {
return {
link: function($scope, $element) {
angular.element($window).on('click', function(e) {
// Namespacing events with name of directive + event to avoid collisions
$scope.$broadcast(appEvents.on_click_anywhere, e.target);
});
}
};
}
]);
//menu-controller.js
changeNotificationApp.controller('menuController', ['$scope', '$window', 'appEvents', '$element',
function ($scope, $window, appEvents, $element) {
$scope.menu = [
{
Name: "INTEGRATION",
Tag: "integration",
IsDisabled: false,
IsSelected: false,
SubMenu: [
{
Name: "SRC Messages",
Tag: "ncs-notifications",
IsDisabled: false,
AspNetMvcController: "SearchSRCMessages"
},
{
Name: "Target Messages",
Tag: "advisor-notifications",
IsDisabled: false,
AspNetMvcController: "SearchTaregtMessages"
}
]
},
{
Name: "AUDITING",
Tag: "auditing",
IsDisabled: true,
IsSelected: false,
SubMenu: []
}
];
$scope.appInfo = {
Version: "1.0.0.0",
User: "VB",
Server: "azzcvy0623401v",
IsSelected: false
};
var resetMenu = function () {
angular.forEach($scope.menu, function (item) {
item.IsSelected = false;
});
$scope.appInfo.IsSelected = false;
};
$scope.toggleDropDownMenu = function (menuItem) {
var currentDropDownState = menuItem.IsSelected;
resetMenu($scope.menu, $scope.appInfo);
menuItem.IsSelected = !currentDropDownState;
};
$scope.loadPage = function (menuItem) {
if (menuItem.AspNetMvcController)
$window.location.href = menuItem.AspNetMvcController;
};
$scope.$on(appEvents.on_click_anywhere, function(event, targetElement) {
var isClickedElementIsChildOfThisElement = $element.find(targetElement).length > 0;
if (isClickedElementIsChildOfThisElement) return;
$scope.$apply(function(){
resetMenu($scope.menu, $scope.appInfo);
});
});
}
]);
You can simplify the directive into something like this:
changeNotificationApp.directive('onDocumentClick', ['$document',
function($document) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var onClick = function() {
scope.$apply(function() {
scope.$eval(attrs.onDocumentClick);
});
};
$document.on('click', onClick);
scope.$on('$destroy', function() {
$document.off('click', onClick);
});
}
};
}
]);
And then pass a function from the menuController to it:
<section class="local-nav" ng-controller="menuController" on-document-click="someFunction()">
No need for the globalController this way.
If you want to keep the globalController and handle it from there, you can:
1.) Make the menu into a service and then inject it into all controllers that need to be able to control it.
2.) Broadcast an event from globalController and listen for it in menuController.
Specific alternative solution: You can turn the directive into a 'on-outside-element-click' and use it like this:
<ul on-outside-element-click="closeMenus()">
The directive looks like this and will only call closeMenus() if you click outside the ul:
changeNotificationApp.directive('onOutsideElementClick', ['$document',
function($document) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.on('click', function(e) {
e.stopPropagation();
});
var onClick = function() {
scope.$apply(function() {
scope.$eval(attrs.onOutsideElementClick);
});
};
$document.on('click', onClick);
scope.$on('$destroy', function() {
$document.off('click', onClick);
});
}
};
}
]);
Working Plunker: http://plnkr.co/edit/zVo0fL2wOCQb3eAUx44U?p=preview
Well you have done things well. If you apply the same directive over the menuController
<section class="local-nav" ng-controller="menuController" on-global-click="handleClick($event)>
and have the click handler defined in your menuController you are all set to go.
I don't think there is any harm in having multiple handlers for the event on document. So where ever you define this directive that element can respond to the global document click event.
Update: As i tested this, it leads to another problem where this method get called, where ever you click on the page. You need a mechanism to differentiate now.
Related
I am using ionic popup in my code in which i have created a directive to make the automatic cursor to focus the input box.It is working fine when the popup is opened for the first time but the cursor is not getting focused to the input box again when i close the popup and open it again can anyone tell me why it occurs.
Code:
$scope.data = {};
var myPopup = $ionicPopup.show({
template: '<input focus-me type="text" ng-model="data.expensetype" limit-char limit="15">',
title: $translate.instant('{{"pentercoconuttype_message" | translate}}'),
scope: $scope,
buttons: [
{ text: $translate.instant('{{"pcancel_message" | translate}}') },
{
text: $translate.instant('{{"psave_message" | translate}}'),
type: 'button-positive',
onTap: function (e) {
if (!$scope.data.expensetype) {
//don't allow the user to close unless he enters producttype
e.preventDefault();
} else {
addExpenseCategory();
return $scope.data.expensetype;
}
}
},
]
});
myPopup.then(function (res) {
$log.log('Tapped!', res);
myPopup.close();
});
Directive:
.directive('focusMe', function($timeout) {
return {
link: function(scope, element, attrs) {
$timeout(function() {
element[0].focus();
});
}
};
});
Just add 150ms to the $timeout to make sure the view is completely rendered.
.directive('focusMe', function($timeout) {
return {
link: function(scope, element, attrs) {
$timeout(function() {
element[0].focus();
}, 150);
}
};
});
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!
I currently have an issue when I call ui-tinymce directive in a custom directive. The custom directive is used to load dynamically links from backend for tinymce advlink plugin (+ load tinymce options object associated with a key passed as an attribute to the directive).
Here is my controller :
module.controller('Ctrl', function ($scope) {
$scope.test = {
val: "gfsgfdgh"
};
});
Here is how I call the directive in HTML:
<tinymce-custom type="minimal" ng-model="test.val"></tinymce-custom>`
And here is my directive :
module.directive('tinymceCustom', function($location, TinyService, Module, GenerateurPage) {
return {
restrict: 'E',
replace: true,
require:"ngModel",
scope: {
ngModel: '='
},
link: function(scope, element, attrs, ngModel){
scope.loaded = {
modules: false,
pages: false,
tinymce: false
};
scope.tinyOptions = {};
var link_list = [];
var modules = [];
var pages = [];
Module.findByOrganisme({}, function (data) {
data.forEach(function(module) {
modules.push({title: module.libelle, value: "/modules/"+module.id});
});
link_list.push({title: "Modules", menu: modules});
scope.loaded.modules = true;
initTiny();
});
GenerateurPage.findByOrganisme({}, function(data) {
data.forEach(function(page) {
pages.push({title: page.titre, value: "/#/generateurPage/afficherPage?id=/"+page.id});
});
link_list.push({title: "Pages", menu: pages});
scope.loaded.pages = true;
initTiny();
});
function initTiny() {
if (!scope.loaded.modules || !scope.loaded.pages) {
return false;
}
scope.tinyOptions = TinyService.options(attrs.type);
console.log(scope);
scope.tinyOptions.link_list = link_list;
scope.loaded.tinymce = true;
}
},
template: '<div ng-if="loaded.tinymce"><textarea ui-tinymce="tinyOptions" ng-model="ngModel"></textarea></div>'
};
});
The problem is that the model passed to ui-tinymce directive is not updated when changing the text with the editor, and the text in the editor is not updated when the model from the controller is changed... BUT, the initial ngModel value is passed to ui-tinymce directive, so I think that is the data binding that is broken. Tried to watch it with $watch but nothing happens.
I can't figure how to fix it so I'm now looking for some help...
Thx
Finaly fixed it changing the approach :
<textarea tinymce-custom="minimal" ng-model="myVar"></textarea >
The final directive :
module.directive('tinymceCustom', function($location, $compile, $q, TinyService, Module, GenerateurPage) {
return {
restrict: 'A',
priority:999,
terminal:true, // prevent lower priority directives to compile after it
scope: true,
require: ['?ngModel'],
link: function(scope, el, attrs) {
// default is basic template
var type = attrs.tinymceCustom ? attrs.tinymceCustom : 'basic';
function loadTinyOptions(name) {
var loaded = {
modules: false,
pages: false,
tinymce: false
};
var link_list = [];
var deferred = $q.defer();
var initTiny = function() {
if (!loaded.modules || !loaded.pages) {
return false;
}
var tinyOptions = TinyService.options(name);
tinyOptions.link_list = link_list;
deferred.resolve(tinyOptions);
};
Module.findByOrganisme({}, function (data) {
var modules = [];
data.forEach(function(module) {
modules.push({title: module.libelle, value: "/modules/"+module.id});
});
link_list.push({title: "Modules", menu: modules});
loaded.modules = true;
initTiny();
});
GenerateurPage.findByOrganisme({}, function(data) {
var pages = [];
data.forEach(function(page) {
pages.push({title: page.titre, value: "/#/generateurPage/afficherPage?id=/"+page.id});
});
link_list.push({title: "Pages", menu: pages});
loaded.pages = true;
initTiny();
});
return deferred.promise;
}
loadTinyOptions(type).then(function(data) {
scope._tinyOptions = data;
el.removeAttr('tinymce-custom'); // necessary to avoid infinite compile loop
el.attr('ui-tinymce', '{{_tinyOptions}}');
$compile(el)(scope);
});
}
};
Hope this can help.
I 'm developing a simple ionic app, and part of the app requires you to press two buttons at once. I've built this logic like so:
<!--yT stands for yourThumb, pT stands for partnersThumb -->
<a class="icon ion-qr-scanner lg-txt" on-hold="Global.thumbHoldManager('yT',true)" on-release="Global.thumbHoldManager('yT',false, true)"></a>
<a class="icon ion-qr-scanner lg-txt" on-hold="Global.thumbHoldManager('pT',true)" on-release="Global.thumbHoldManager('pT',false, true)"></a>
I have a method on my controller which handles this event using a service I 've created
var globalCtrl = function (clickHandler, $timeout) {
var self = this;
this.clickHandler = clickHandler;
this.timeout = $timeout;
this.readyState = clickHandler.ready;
this.showInstruction = false;
clickHandler.watchForReady();
};
globalCtrl.prototype.thumbHoldManager = function(which, what, up) {
this.clickHandler.setClickState(which, what);
var self = this;
if (up) {
this.clickHandler.stopWatching();
}
if (!this.readyState) {
this.instruction = "Hold both thumbs in place to scan"
if (!this.showInstruction) {
this.showInstruction = true;
self.timeout(function() {
self.showInstruction = false;
}, 5000)
}
}
};
globalCtrl.$inject = ['clickHandler', '$timeout'];
The service clickHandler exposes an api to a private object whose job it is to track when a button is pressed, and when both buttons are pressed to navigate to a new url.
.factory('clickHandler', [
'$interval',
'$rootScope',
'$location',
function($interval, $rootScope, $location) {
// Service logic
// ...
var clickState = {
yT: false,
pT: false,
ready: false,
watching: false,
watcher: false
};
// Public API here
return {
setClickState: function(which, what) {
clickState[which] = what;
},
getClickState: function(which) {
return clickState[which]
},
getReadyState: function() {
return ((clickState.yT) && (clickState.pT));
},
watchForReady: function() {
var self = this;
clickState.watching = $interval(function() {
clickState.ready = self.getReadyState();
},50);
clickState.watcher = $rootScope.$watch(function() {
return clickState.ready
}, function redirect(newValue) {
if (newValue) {
self.stopWatching();
$location.path('/scan');
}
})
},
stopWatching: function() {
if (clickState.watching) {
$interval.cancel(clickState.watching);
clickState.watcher();
clickState.watching = false;
clickState.watcher = false;
}
}
};
}
])
I don't get any errors with this code, everything works as it should, the watcher gets registered on the hold event and unregistered on the release event. But no matter what I do, I cannot seem to get my phone to detect a press on both buttons. It's always one or the other and I don't know why. I can't test this in the browser or the emulator since multi-touch is not supported and I don't have a multi-touch trackpad if it were.
Here's how I implemented my own directive and service to do this:
.factory('clickHandler', ['$interval', '$rootScope', '$location', '$document', function ($interval, $rootScope, $location, $document) {
// Service logic
// ...
$document = $document[0];
var
touchStart,
touchEnd;
touchStart = ('ontouchstart' in $document.documentElement) ? 'touchstart' : 'mousedown';
touchEnd = ('ontouchend' in $document.documentElement) ? 'touchend' : 'mouseup';
var clickState = {
yT: false,
pT: false,
ready: false,
watching: false,
watcher: false,
startEvent: touchStart,
endEvent: touchEnd
};
// Public API here
return {
setClickState: function (which, what) {
clickState[which] = what;
},
getClickState: function (which) {
return clickState[which]
},
getReadyState: function () {
return ( (clickState.yT) && (clickState.pT) );
},
watchForReady: function () {
var self = this;
//prevent multiple redundant watchers
if (clickState.watching) {
return;
}
clickState.watching = $interval(function () {
clickState.ready = self.getReadyState();
}, 50);
clickState.watcher = $rootScope.$watch(function () {
return clickState.ready
}, function redirect(newValue) {
if (newValue) {
self.stopWatching();
$location.path('/scan');
}
})
},
stopWatching: function () {
if (clickState.watching) {
$interval.cancel(clickState.watching);
clickState.watcher();
clickState.watching = false;
clickState.watcher = false;
}
},
getTouchEvents: function () {
return {
start: clickState.startEvent,
end: clickState.endEvent
}
}
};
}])
.directive('simultaneousTouch', ['clickHandler', '$document', function (clickHandler) {
return {
restrict: 'A',
link: function (scope, elem, attr) {
var touchEvents = clickHandler.getTouchEvents();
elem.on(touchEvents.start, function () {
clickHandler.watchForReady();
clickHandler.setClickState(attr.simultaneousTouch, true);
});
elem.on(touchEvents.end, function () {
clickHandler.stopWatching();
clickHandler.setClickState(attr.simultaneousTouch, false);
})
}
}
}]);
Crossposting stankugo's answer from the ionic forums for the sake of reference. The simple solution below is entirely his idea, I've just done a little cleanup.
angular.module('xxxx').directive('multitouch', function () {
return function(scope, element, attr) {
element.on('touchstart', function() {
scope.$apply(function() {
scope.$eval(attr.multitouch);
});
});
};
});
Use like:
<div multitouch="handler()"></div>
I wanted to use a directive to have some click-to-edit functionality in my front end.
This is the directive I am using for that: http://icelab.com.au/articles/levelling-up-with-angularjs-building-a-reusable-click-to-edit-directive/
'use strict';
angular.module('jayMapApp')
.directive('clickToEdit', function () {
return {
templateUrl: 'directives/clickToEdit/clickToEdit.html',
restrict: 'A',
replace: true,
scope: {
value: '=clickToEdit',
method: '&onSave'
},
controller: function($scope, $attrs) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
$scope.method();
};
}
};
});
I added a second attribute to the directive to call a method after when the user changed the value and then update the database etc. The method (´$onSave´ here) is called fine, but it seems the parent scope is not yet updated when I call the method at the end of the directive.
Is there a way to call the method but have the parent scope updated for sure?
Thanks in advance,
Michael
I believe you are supposed to create the functions to attach inside the linking function:
Take a look at this code:
http://plnkr.co/edit/ZTx0xrOoQF3i93buJ279?p=preview
app.directive('clickToEdit', function () {
return {
templateUrl: 'clickToEdit.html',
restrict: 'A',
replace: true,
scope: {
value: '=clickToEdit',
method: '&onSave'
},
link: function(scope, element, attrs){
scope.save = function(){
console.log('save in link fired');
}
},
controller: function($scope, $attrs) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
console.log('save in controller fired');
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
$scope.method();
};
}
};
});
I haven't declared the functions inside the controller before, but I don't see why it wouldn't work.
Though this question/answer explain it Link vs compile vs controller
From my understanding:
The controller is used to share data between directive instances, not to "link" functions which would be run as callbacks.
The method is being called but angular doesn't realise it needs to run the digest cycle to update the controller scope. Luckily you can still trigger the digest from inside your isolate scope just wrap the call to the method:
$scope.$apply($scope.method());