I currently trying to set focus on a textarea on ipad which isn't touched by the user.
The reason for this is that I want to another action on a single tap than on a double tap.
I tried to make a plnkr work but I couldn't. For some reasons plnkr don't want to use my eventhandling. Thatswhy I only could show you my code.
Plunker
This part works very well on Andriod, Windows Mobile, MacOS and PC but not on iOs:
angular.module('App').direktive('setFocusOnInput', ['$timeout', function($timeout) {
return {
scope: {
trigger: '#setFocusOnInput',
elementId: '#'
},
link: function($scope, $elm, $attr, $ctrl) {
$scope.$watch('trigger', function(value) {
if(value === $scope.elementId) {
$timeout(function() {
$elm.focus();
});
} else {
angular.element(document.activeElement).blur();
}
});
$elm.on('blur', function() {
$scope.trigger = null;
});
}
}
}]);
I found a solution which worked for me:
.directive('setFocusOnInput', ['$timeout', function($timeout) {
return {
scope: {
trigger: '#setFocusOnInput',
elementId: '#'
},
link: function($scope, $elm, $attr, $ctrl) {
$scope.$watch('trigger', function(value) {
if(value === $scope.elementId) {
if(window.navigator.userAgent.indexOf('iPad') > -1 || window.navigator.userAgent.indexOf('iPod') > -1) {
$elm.focus();
$timeout(function() {
document.activeElement.focus();
},750);
} else {
$timeout(function() {
$elm.focus();
});
}
} else {
angular.element(document.activeElement).blur();
}
});
$elm.on('blur', function() {
$scope.trigger = null;
});
}
}
}])
iOS doesn't accept the timeout to set the Dom-element as activeElement. But if it is set, you have to set another focus on the activeElement. It's a cheese solution but maybe somebody will have a simular problem and could use this solution.
I have an html page with question and ability to add multiple options with ability to add or remove an option.
How wanted to set focus to the newly added option text box which was added using add button/link
Here's the link for Plnkr
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.question = { question: 'what is your name',
options: [{option:'Red', code: '1'}, {option:'Blue', code: '2'},
{option:'Green', code: '3'},{option:'Black', code: '4'}]};
});
app.directive('myDirective', function(){
return {
link: function(scope, element, attrs, ctrls) {
function Option() {
this.option = '';
this.code = '';
}
scope.addOption = function(index) {
var existingOption = scope.question.options;
if (existingOption && existingOption.length) {
var newOption = new Option(index + 1);
scope.question.options.splice(index + 1, 0, newOption);
}
}
scope.removeOption = function (index) {
if (index > -1) {
scope.question.options.splice(index, 1);
}
}
},
restrict: 'AE',
scope: {
question: "="
},
templateUrl: 'question.html'
};
});
You can create an autofocus directive that will give focus to an element when it is first created:
.directive('autofocus', function($timeout) {
return {
link: function(scope, element, attrs) {
$timeout(function() {
element.focus();
});
}
}
});
Then in question.html add the autofocus attribute to the input:
<input type="text" autofocus class="form-control... />
http://plnkr.co/edit/xW17yIgEaKS0oX4wVj23?p=preview
I am trying call up to do focus of a element in directive of modal.
After the directive is called the focus-me directive is called only first time.
I tried adding the focus-me element in dialog directive as shown in the Plunker.
Below are things which I am trying in the directive.
Show the Modal dialog box [working]
After the dialog box is shown i am trying to focus a specfic element in the dialog box [For which i added watch element in focus-me directive]
The Focus is happening only for the first time.
Plunker
Please help with,how to trigger the watch element on the directive for the above Plunker. Thanks a lot.
Below is the directive code for the focus and dialog which i am currently using.`
app.directive('focusMe', function($timeout, $parse) {
return {
link: function(scope, element, attrs) {
var model = $parse(attrs.focusMe);
scope.$watch(model, function(value) {
console.log('value=',value);
if(value === true) {
$timeout(function() {
element[0].select();
element[0].focus();
});
}
});
element.bind('blur', function() {
console.log('blur')
scope.$apply(model.assign(scope, false));
})
}
};
}).directive('modalDialog', function(){
return {
restrict: 'AE',
scope: {
show: '=',
focusDesign:'=focusDesign'
},
replace: true,
transclude: true,
link: function(scope, element, attrs) {
scope.dialogStyle = {};
if (attrs.width)
scope.dialogStyle.width = attrs.width;
if (attrs.height)
scope.dialogStyle.height = attrs.height;
if (attrs.focusDesigniso)
scope.focusDesigniso=attrs.focusDesigniso;
scope.hideModal = function(focusDesign) {
scope.show = false;
focusDesigniso=false;
//$scope.focusDesign=false;
};
scope.hideModalC = function() {
alert("I m in hide");
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay' ng-click='hideModal(focusDesign)' ng-model='focusDesign'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal(focusDesign)' ng-model='focusDesign'>X</div><div class='ng-modal-dialog-content' ng-transclude></div></div></div>"
};
});
I got solution for the problem in the directive.
The $scope.apply i was applying it model instead i should i used for the element in focus.
The updated the directive of focus i have copied below with working Plunker
app.directive('focusMe', function($timeout, $parse) {
return {
link: function(scope, element, attrs) {
var model = $parse(attrs.focusMe);
scope.$watch(model, function(value) {
console.log('value=',value);
if(value === true) {
$timeout(function() {
element[0].select();
element[0].focus();
});
}
});
element.bind('blur', function() {
scope.$apply(function()
{
attrs.focusMe=!attrs.focusMe;
})
})
}
};
});
Working Plunker
I have a custom directive to confirm if a user that clicks an element really wants to perform an action:
.directive('ngReallyClick', ['$modal',
function ($modal) {
return {
restrict: 'A',
scope: {
ngReallyClick: "&",
},
link: function (scope, element, attrs) {
var isDeleteDisabled = scope.ngReallyDisabled;
element.bind('click', function () {
if (isDeleteDisabled != true) {
var message = attrs.ngReallyMessage || "Are you sure ?";
...
var modalInstance = $modal.open({
template: modalHtml,
controller: ModalInstanceCtrl
});
modalInstance.result.then(function () {
scope.ngReallyClick({ ngReallyItem: scope.ngReallyItem }); //raise an error : $digest already in progress
}, function () {
//Modal dismissed
return false;
});
};
});
}
}
}]);
It is used e.g:
<a ng-really-message="Are you sure you want to save and close?" ng-really-click="saveAndCloseGlobal(456)"
where saveAndCloseGlobal is called when the user confirms their choice. But, if I try and pass $event to this function, to get the original click event, it ends up undefined. If I use a plain ng-click=saveAndCloseGlobal($event) then I get the correct event object in saveAndCloseGlobal.
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.