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.
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);
}
};
});
So in order to have the ngDialogs centrilized into a specific place and not wandering about, I though about having a factory, let's call it modal.js, which would have a list of all the modals in a switch. Something like this:
.factory('Modal', [function() {
var modal = {};
return {
get : function(option) {
switch(option) {
case 'modal1':
modal = {
template: 'modal1.php',
controller: 'modalController',
resolve: {
$dep: function() {
return 'dep';
},
dip: function() {
return 'dup'
}
}
};
break;
case 'modal2':
modal = {
template: 'modal2.php',
controller: 'modalController2',
resolve: {
$dep2: function() {
return item.dep2;
},
dip2: function() {
return item.dip2;
}
}
};
break;
}
return modal;
}
}
}]);
And then my plan was to call it through a directive. Let's call it open-modal.js.
.directive("openModal", ['ngDialog', 'Modal', function(ngDialog, Modal) {
return {
restrict: "A",
link: function(scope, elem, attrs) {
return elem.bind('click', function() {
var modal = Modal.get(attrs.modalName) // Modal.get('modal1');
ngDialog.open(modal);
});
}
};
}]);
The problem that I have is basically with the modal2. I am not able to get the item from the resolve, obviously, because it isn't part of the controller. My question is: Is it possible to find a workaround in order to maintain it this way?
Thanks for the help guys!
So I figured it out. What I am doing is using the $broadcast in order to call the function that will be in each Controller, so the resolve will be able to work properly.
.factory('Modal', ['$rootScope', function($rootScope) {
var modal = {};
return {
get : function(option, params) {
switch(option) {
case 'modal1':
$rootScope.$broadcast('modal:controller1:modal1');
break;
case 'modal2':
$rootScope.$broadcast('modal:controller2:modal2');
break;
}
return modal;
}
}
}]);
In my angular app, I've to use different directives on a single field.
here are my directives
angular.module('app')
.directive('focusMe', function ($timeout) {
return {
scope: { trigger: '#focusMe' },
link: function (scope, element, attr) {
scope.$watch('trigger', function (value) {
if (value === "true") {
$timeout(function () {
element[0].focus();
});
}
});
}
};
});
and another one for datepicker
.directive('ngDatepicker', function() {
return {
restrict: 'A',
replace: true,
scope: {
ngOptions: '=',
ngModel: '='
},
template: "<div class=\"input-append date\">\n <input type=\"text\"><span class=\"add-on\"><i class=\"icon-th\"></i></span>\n</div>",
link: function(scope, element) {
scope.inputHasFocus = false;
element.datepicker(scope.ngOptions).on('changeDate', function(e) {
var defaultFormat, defaultLanguage, format, language;
defaultFormat = $.fn.datepicker.defaults.format;
format = scope.ngOptions.format || defaultFormat;
defaultLanguage = $.fn.datepicker.defaults.language;
language = scope.ngOptions.language || defaultLanguage;
return scope.$apply(function() {
return scope.ngModel = $.fn.datepicker.DPGlobal.formatDate(e.date, format, language);
});
});
element.find('input').on('focus', function() {
return scope.inputHasFocus = true;
}).on('blur', function() {
return scope.inputHasFocus = false;
});
return scope.$watch('ngModel', function(newValue) {
if (!scope.inputHasFocus) {
return element.datepicker('update', newValue);
}
});
}
};
});
it throws me an error
Multiple directives asking for new/isolated scope
after hours of searching and working on different solutions, i finally understand this one same like my problem and changed my first directive like this
angular.module('app')
.directive('focusMe', function ($timeout) {
return {
link: function (scope, element, attr) {
scope.$watch(attr.focusMe, function (value) {
if (value === "true") {
$timeout(function () {
element[0].focus();
});
}
});
}
};
})
But now its not working because it always gives me value = undefined and I don't know why it is happening. May be I'm missing something here or not doing it properly??
here is my html where I'm binding its value
<input type="text" focus-me="{{ DateOfBirthFocus }}" ng-datepicker>
Any kind of help will be appreciated.
I'm using intro.js in my angular app:
http://code.mendhak.com/angular-intro.js/example/index.html
and all was ok, untill yesterday...
my problem:
when i solve (or skip) tutorial:
and
after i change language and restart tutorial:
and i see same hints (in same language as before), but this text is changed:
what i do wrong?
i call intro.js so:
Start It!
and options:
$scope.IntroOptions = {
steps: [{
element: '.el1',
intro: $translate.instant('text1'),
position: 'bottom'
}, {
element: '.el2',
intro: $translate.instant('text2'),
position: 'bottom'
}],
showStepNumbers: false,
showProgress: false,
exitOnOverlayClick: false,
keyboardNavigation: false,
exitOnEsc: false,
prevLabel: '',
skipLabel: '<strong>skip</strong>',
doneLabel: '<strong>skip</strong>'
};
and whole angularjs directive of intro.js:
var ngIntroDirective = angular.module('angular-intro', []);
ngIntroDirective.directive('ngIntroOptions', ['$timeout', function ($timeout) {
return {
restrict: 'A',
scope: {
ngIntroMethod: "=",
ngIntroExitMethod: "=?",
ngIntroOptions: '=',
ngIntroOncomplete: '=',
ngIntroOnexit: '=',
ngIntroOnchange: '=',
ngIntroOnbeforechange: '=',
ngIntroOnafterchange: '=',
ngIntroAutostart: '&',
ngIntroAutorefresh: '='
},
link: function(scope, element, attrs) {
var intro;
scope.ngIntroMethod = function(step) {
var navigationWatch = scope.$on('$locationChangeStart', function(){
intro.exit();
});
if (typeof(step) === 'string') {
intro = introJs(step);
} else {
intro = introJs();
}
intro.setOptions(scope.ngIntroOptions);
if (scope.ngIntroAutorefresh) {
scope.$watch(function(){
intro.refresh();
});
}
if (scope.ngIntroOncomplete) {
intro.oncomplete(function() {
scope.ngIntroOncomplete.call(this, scope);
$timeout(function() {scope.$digest()});
navigationWatch();
});
}
if (scope.ngIntroOnexit) {
intro.onexit(function() {
scope.ngIntroOnexit.call(this, scope);
$timeout(function() {scope.$digest()});
navigationWatch();
});
}
if (scope.ngIntroOnchange) {
intro.onchange(function(targetElement){
scope.ngIntroOnchange.call(this, targetElement, scope);
$timeout(function() {scope.$digest()});
});
}
if (scope.ngIntroOnbeforechange) {
intro.onbeforechange(function(targetElement) {
scope.ngIntroOnbeforechange.call(this, targetElement, scope);
$timeout(function() {scope.$digest()});
});
}
if (scope.ngIntroOnafterchange) {
intro.onafterchange(function(targetElement){
scope.ngIntroOnafterchange.call(this, targetElement, scope);
$timeout(function() {scope.$digest()});
});
}
if (typeof(step) === 'number') {
intro.goToStep(step).start();
} else {
intro.start();
}
};
scope.ngIntroExitMethod = function (callback) {
intro.exit(); //TODO check callBack
};
if (scope.ngIntroAutostart()) {
$timeout(function() {
scope.ngIntroMethod();
});
}
}
};
}]);
what i do wrong? why my hints are not changing their language?
plunker you could check here:
http://plnkr.co/edit/RsJ29a49soZ4q33gxQhk?p=preview
what i do wrong with angular-translate?
You're using the synchronous $translate.instant() which means that your intro property will never update itself when changing language.
You need to manually reload the intro.js configuration (your steps) when the language change. For that you can use angular-translate events like $translateChangeSuccess:
$rootScope.$on('$translateChangeSuccess', function() {
// updating steps config with $translate.instant() function
$scope.IntroOptions.steps = [{
element: '.el1',
intro: $translate.instant('text1'),
position: 'bottom'
}, [...]];
});
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.