how to enable ionic multi-touch events - javascript

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>

Related

Confirm angular modal closing on dirty form

I have an Angular-UI modal with a form in it. When the user triggers the dismiss event I want to implement a confirmation based on $dirty. I have searched through numerous sources to find notions on Promise and can succesfully get e.g. an alert during the closing event. However, I can't find anywhere how to actually stop the modal from closing.
EDIT:
With the current code the confirmation alert often (surprisingly not always) pops up after the modal has already been dismissed.
var editResourceModalController = function($scope, $uibModalInstance) {
$uibModalInstance.result.catch(function() {
if ($scope.editForm.$dirty) {
window.confirm("close modal?");
}
$uibModalInstance.dismiss('cancel');
});
}
var uibModalInstance;
$scope.openEditModal = function() {
uibModalInstance = $uibModal.open({
animation: true,
templateUrl: "edit.html",
controller: editResourceModalController
});
}
Add the $scope.ok method and hook it to the editForm's submit button's ng-click
var editResourceModalController = function($scope, editItem, hierarchy, selectedFolder) {
$scope.form = {};
$scope.editItem = editItem;
$scope.editListItems = [];
$scope.listItems = 0;
$scope.getNumber = function(n) {
return new Array(n);
}
$scope.hierarchy = hierarchy;
$scope.selectedFolder = selectedFolder;
$scope.editModel = {
name: $scope.editItem.name,
description: $scope.editItem.description,
hierarchyId: $scope.selectedFolder
}
$scope.ok = function () {
editItem.close($scope.editForm.$dirty);
};
}
Inject the $scope.edeitForm.$dirty as isDirty and use the injected value as you like
$scope.openEditModal = function(editItem, hierarchy, selectedFolder) {
$scope.modalInstance = $uibModal.open({
animation: true,
templateUrl: "edit.html",
controller: ["$scope", "editItem", "hierarchy", "selectedFolder", editResourceModalController],
resolve: {
editItem: function() {
return editItem;
},
hierarchy: function() {
return hierarchy;
},
selectedFolder: function() {
return selectedFolder;
}
}
});
$scope.modalInstance.result.catch(function(isDirty) {
if (isDirty) {
// confirmation code here
}else{
// other logic
}
// dismiss the modal
editItem.dismiss('cancel');
});
}
Hope this helped you :D
I fixed it using $scope.$on, extensive example here
var editResourceModalController = function($scope, $uibModalInstance) {
$scope.close = function() {
$uibModalInstance.close();
}
$scope.$on('modal.closing', function(event) {
if ($scope.editForm.$dirty) {
if (!confirm("U sure bwah?")) {
event.preventDefault();
}
}
});
}
var uibModalInstance;
$scope.openEditModal = function(editItem, hierarchy, selectedFolder) {
uibModalInstance = $uibModal.open({
animation: true,
templateUrl: "edit.html",
controller: editResourceModalController
});
}
This solution works for me.
Esc, X button on top and Close button at the bottom.
function cancel() {
if (vm.modalForm.$dirty) {
var response = DevExpress.ui.dialog.confirm("You have unsaved changes. Would you like to discard them?");
response.done(function (result) {
if (result)
vm.dismiss({ $value: 'cancel' });
});
}
else
vm.dismiss({ $value: 'cancel' });
}
$scope.$on('modal.closing', function (event, reason) {
if (reason === 'escape key press') {
var message;
if (vm.modalForm.$dirty) {
message = "You have unsaved changes. Would you like to discard them?";
if (!confirm(message)) {
event.preventDefault();
}
}
}
});

how to get long press event in angular js?

I am trying to get long press event in angular js .I found the solution from here
https://gist.github.com/BobNisco/9885852
But I am not able to get log on console .here is my code.
http://goo.gl/ZpDeFz
could you please tell me where i am getting wrong ..
$scope.itemOnLongPress = function(id) {
console.log('Long press');
}
$scope.itemOnTouchEnd = function(id) {
console.log('Touch end');
}
It is a good implementation:
// pressableElement: pressable-element
.directive('pressableElement', function ($timeout) {
return {
restrict: 'A',
link: function ($scope, $elm, $attrs) {
$elm.bind('mousedown', function (evt) {
$scope.longPress = true;
$scope.click = true;
// onLongPress: on-long-press
$timeout(function () {
$scope.click = false;
if ($scope.longPress && $attrs.onLongPress) {
$scope.$apply(function () {
$scope.$eval($attrs.onLongPress, { $event: evt });
});
}
}, $attrs.timeOut || 600); // timeOut: time-out
// onTouch: on-touch
if ($attrs.onTouch) {
$scope.$apply(function () {
$scope.$eval($attrs.onTouch, { $event: evt });
});
}
});
$elm.bind('mouseup', function (evt) {
$scope.longPress = false;
// onTouchEnd: on-touch-end
if ($attrs.onTouchEnd) {
$scope.$apply(function () {
$scope.$eval($attrs.onTouchEnd, { $event: evt });
});
}
// onClick: on-click
if ($scope.click && $attrs.onClick) {
$scope.$apply(function () {
$scope.$eval($attrs.onClick, { $event: evt });
});
}
});
}
};
})
Usage example:
<div pressable-element
ng-repeat="item in list"
on-long-press="itemOnLongPress(item.id)"
on-touch="itemOnTouch(item.id)"
on-touch-end="itemOnTouchEnd(item.id)"
on-click="itemOnClick(item.id)"
time-out="600"
>{{item}}</div>
var app = angular.module('pressableTest', [])
.controller('MyCtrl', function($scope) {
$scope.result = '-';
$scope.list = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 }
];
$scope.itemOnLongPress = function (id) { $scope.result = 'itemOnLongPress: ' + id; };
$scope.itemOnTouch = function (id) { $scope.result = 'itemOnTouch: ' + id; };
$scope.itemOnTouchEnd = function (id) { $scope.result = 'itemOnTouchEnd: ' + id; };
$scope.itemOnClick = function (id) { $scope.result = 'itemOnClick: ' + id; };
})
.directive('pressableElement', function ($timeout) {
return {
restrict: 'C', // only matches class name
link: function ($scope, $elm, $attrs) {
$elm.bind('mousedown', function (evt) {
$scope.longPress = true;
$scope.click = true;
$scope._pressed = null;
// onLongPress: on-long-press
$scope._pressed = $timeout(function () {
$scope.click = false;
if ($scope.longPress && $attrs.onLongPress) {
$scope.$apply(function () {
$scope.$eval($attrs.onLongPress, { $event: evt });
});
}
}, $attrs.timeOut || 600); // timeOut: time-out
// onTouch: on-touch
if ($attrs.onTouch) {
$scope.$apply(function () {
$scope.$eval($attrs.onTouch, { $event: evt });
});
}
});
$elm.bind('mouseup', function (evt) {
$scope.longPress = false;
$timeout.cancel($scope._pressed);
// onTouchEnd: on-touch-end
if ($attrs.onTouchEnd) {
$scope.$apply(function () {
$scope.$eval($attrs.onTouchEnd, { $event: evt });
});
}
// onClick: on-click
if ($scope.click && $attrs.onClick) {
$scope.$apply(function () {
$scope.$eval($attrs.onClick, { $event: evt });
});
}
});
}
};
})
li {
cursor: pointer;
margin: 0 0 5px 0;
background: #FFAAAA;
}
.pressable-element {
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
}
<div ng-app="pressableTest">
<div ng-controller="MyCtrl">
<ul>
<li ng-repeat="item in list"
class="pressable-element"
on-long-press="itemOnLongPress(item.id)"
on-touch="itemOnTouch(item.id)"
on-touch-end="itemOnTouchEnd(item.id)"
on-click="itemOnClick(item.id)"
time-out="600"
>{{item.id}}</li>
</ul>
<h3>{{result}}</h3>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
JSFiddle: https://jsfiddle.net/reduardo7/u47ok38e/
Based on: https://gist.github.com/BobNisco/9885852
Your code is not working because the directive binds to the elements touchstart and touchend events which you're probably not using if you're testing in a browser.
When I changed them to mousedown and mouseup your script worked fine on my computer's browser.
app.directive('onLongPress', function($timeout) {
return {
restrict: 'A',
link: function($scope, $elm, $attrs) {
$elm.bind('mousedown', function(evt) { // <-- changed
/* ... */
});
$elm.bind('mouseup', function(evt) { // <-- changed
/* ... */
});
}
};
})
Go through the below URL for the angular directive and the implementation approaches,
Source code for long press Directive:
// Add this directive where you keep your directives
.directive('onLongPress', function($timeout) {
return {
restrict: 'A',
link: function($scope, $elm, $attrs) {
$elm.bind('touchstart', function(evt) {
// Locally scoped variable that will keep track of the long press
$scope.longPress = true;
// We'll set a timeout for 600 ms for a long press
$timeout(function() {
if ($scope.longPress) {
// If the touchend event hasn't fired,
// apply the function given in on the element's on-long-press attribute
$scope.$apply(function() {
$scope.$eval($attrs.onLongPress)
});
}
}, 600);
});
$elm.bind('touchend', function(evt) {
// Prevent the onLongPress event from firing
$scope.longPress = false;
// If there is an on-touch-end function attached to this element, apply it
if ($attrs.onTouchEnd) {
$scope.$apply(function() {
$scope.$eval($attrs.onTouchEnd)
});
}
});
}
};
})
Your HTML Should be like this:
<ion-item ng-repeat="item in list" on-long-press="itemOnLongPress(item.id)" on-touch-end="itemOnTouchEnd(item.id)">
{{ item }}
</ion-item>
Controller JS functions to make the definitions that you would prefer:
$scope.itemOnLongPress = function(id) {
console.log('Long press');
}
$scope.itemOnTouchEnd = function(id) {
console.log('Touch end');
}
https://gist.github.com/BobNisco/9885852

Strange behavior passing scope to directive

I have created a directive below:
html:
<div image-upload></div>
directive:
angular.module('app.directives.imageTools', [
"angularFileUpload"
])
.directive('imageUpload', function () {
// Directive used to display a badge.
return {
restrict: 'A',
replace: true,
templateUrl: "/static/html/partials/directives/imageToolsUpload.html",
controller: function ($scope) {
var resetScope = function () {
$scope.imageUpload = {};
$scope.imageUpload.error = false;
$scope.imageUpload['image_file'] = undefined;
$scope.$parent.imageUpload = $scope.imageUpload
};
$scope.onImageSelect = function ($files) {
resetScope();
$scope.imageUpload.image_file = $files[0];
var safe_file_types = ['image/jpeg', 'image/jpg']
if (safe_file_types.indexOf($scope.imageUpload.image_file.type) >= 0) {
$scope.$parent.imageUpload = $scope.imageUpload
}
else {
$scope.imageUpload.error = true
}
};
// Init function.
$scope.init = function () {
resetScope();
};
$scope.init();
}
}
});
This directive works fine and in my controller I access $scope.imageUpload as I required.
Next, I tried to pass into the directive a current image but when I do this $scope.imageUpload is undefined and things get weird...
html:
<div image-upload current="project.thumbnail_small"></div>
This is the updated code that gives the error, note the new current.
angular.module('app.directives.imageTools', [
"angularFileUpload"
])
.directive('imageUpload', function () {
// Directive used to display a badge.
return {
restrict: 'A',
replace: true,
scope: {
current: '='
},
templateUrl: "/static/html/partials/directives/imageToolsUpload.html",
controller: function ($scope) {
var resetScope = function () {
$scope.imageUpload = {};
$scope.imageUpload.error = false;
$scope.imageUpload['image_file'] = undefined;
$scope.$parent.imageUpload = $scope.imageUpload
if ($scope.current != undefined){
$scope.hasCurrentImage = true;
}
else {
$scope.hasCurrentImage = true;
}
};
$scope.onImageSelect = function ($files) {
resetScope();
$scope.imageUpload.image_file = $files[0];
var safe_file_types = ['image/jpeg', 'image/jpg']
if (safe_file_types.indexOf($scope.imageUpload.image_file.type) >= 0) {
$scope.$parent.imageUpload = $scope.imageUpload
}
else {
$scope.imageUpload.error = true
}
};
// Init function.
$scope.init = function () {
resetScope();
};
$scope.init();
}
}
});
What is going on here?
scope: {
current: '='
},
Everything works again but I don't get access to the current value.
Maybe I'm not using scope: { correctly.
in your updated code you use an isolated scope by defining scope: {current: '=' } so the controller in the directive will only see the isolated scope and not the original scope.
you can read more about this here: http://www.ng-newsletter.com/posts/directives.html in the scope section

AngularJS common $destroy for all scopes

I've created an Angular service which serves as a simple mechanism to handle success/warning/error/info alerts to the user in a common place throughout my app (code below). These alerts are bound to an Angular-UI alert element, listing all the alerts. My controller handles the plumbing.
So my question is how can I cause every controller in my app to call $alert.clear() upon the controller's destruction? I believe I can do this the hard way by calling something like this from every single controller:
$scope.$on('$destroy', function(){
$alerts.clear();
});
However, I don't really want that boilerplate stuff sprinkled everywhere. I'd like to be able to control that behavior common to ALL controllers in my app once and forget about it.
Thanks in advance for any gentle nudge or violent thwack in the right direction!
HTML snippet
<alert ng-repeat="alert in alerts" type="alert.type" close="closeAlert($index)">{{alert.msg}}</alert>
service.alert.js
app.factory('$alert', function() {
var alerts = [];
var clearAlerts = function() {
alerts = [];
};
var closeAlert = function(index, clearOthers) {
alerts.splice(index, 1);
};
var createAlert = function(type, message, clearOthers) {
if (clearOthers)
alerts = [];
alerts.push({type: type, msg: message});
};
var alertSuccess = function(message, clearOthers) {
clearOthers = clearOthers || true;
createAlert('success', message, clearOthers);
};
var alertInfo = function(message, clearOthers) {
clearOthers = clearOthers || true;
createAlert('info', message, clearOthers);
};
var alertWarning = function(message,clearOthers) {
clearOthers = clearOthers || true;
createAlert('warning', message, clearOthers);
};
var alertDanger = function(message, clearOthers) {
clearOthers = clearOthers || true;
createAlert('danger', message, clearOthers);
};
return {
$alerts: function() { return alerts; },
$success: function(message, clearOthers) { return alertSuccess(message, clearOthers); },
$info: function(message, clearOthers) { return alertInfo(message, clearOthers); },
$warning: function(message, clearOthers) { return alertWarning(message, clearOthers); },
$danger: function(message, clearOthers) { return alertDanger(message, clearOthers); },
$clear: function() { return clearAlerts(); },
$close: function(index) { return closeAlert(index); }
};
});
You can inherit a controller in all of your controllers.
It still involves you making all your controllers children of a parent controller but other than that it will work flawlessly.
The controller inheritence is done like this:
app.controller('ParentCtrl', function ($scope) {
"use strict";
$scope.$on("$destroy", function (event, val) {
alert("Controller Destoryed");
});
});
app.controller('ChildCtrl', ['$scope', '$controller', function ($scope, $controller) {
"use strict";
$controller('ParentCtrl', {$scope: $scope});
}]);
Here is a plunkr demo (notice that a $destory event is broadcast on the $scope so this example works exactly as if a $destroy event was broadcast)
This is may helps you
clearAlerts: function() {
for(var x in this.alerts) {
delete this.alerts[x];
}
}
Please take a look at this Demo

How to handle document click and notify other controllers using AngularJS?

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.

Categories

Resources