I am using the following code to get the "auto tab" working with AngularJS (automatically tabs the user to the "Title" textbox after the max length is met for the "Name" textbox):
var app = angular.module('plunker', []);
app.directive('autoTabTo', [function () {
return {
restrict: "A",
link: function (scope, el, attrs) {
el.bind('keyup', function(e) {
if (this.value.length === this.maxLength) {
var element = document.getElementById(attrs.autoTabTo);
if (element)
element.focus();
}
});
}
}
}]);
Here is my HTML (with my custom directives):
<customtextfield autoTabTo="Variant.Specs.Title" ng-maxlength="20" customfield="Variant.Specs.Name"></customtextfield>
<customtextfield ng-maxlength="25" customfield="Variant.Specs.Title"></customtextfield>
Would you happen to know what I am doing wrong?
This piece of code should do it. Let's keep it as simple as possible. :)
HTML:
<div ng-app="autofocus">
<label>Name:</label>
<input ng-model="maxLengthReach"></input>
<br/><br/>
<label>Title:</label>
<input autofocus-when></input>
</div>
Javascript:
var app = angular.module('autofocus', []);
app.directive('autofocusWhen', function () {
return function (scope, element, attrs) {
scope.$watch('maxLengthReach', function(newValue){
if (newValue.length >= 5 ) {
element[0].focus();
}
});
}
});
jsFiddle: http://jsfiddle.net/gctvyfcz/1/
Your first error is your markup is wrong for using the directive. Should be
<input auto-tab-to="Variant.Specs.Title" ng-maxlength="4" customfield="Variant.Specs.Name"></input>
Directive should be changed to:
app.directive('autoTabTo', [function () {
return {
restrict: "A",
link: function (scope, el, attrs) {
el.bind('keyup', function(e) {
if (this.value.length === parseInt(attrs.ngMaxlength)) {
var element = document.getElementById(attrs.autoTabTo);
if (element)
element.focus();
}
});
}
}
}]);
Also, you don't have an ID on your second element so it won't find it:
<input ng-maxlength="4" customfield="Variant.Specs.Title" id="Variant.Specs.Title"></input>
working plunker
Related
I am trying to use ckeditor with angularjs I have added a directive for the same. It is working fine. The problem is when I try to get the instances list of the ckeditor.
// directive
app.directive('ckeditor', function () {
return {
require: '?ngModel',
link: function (scope, element, attr, ngModel) {
var ck = CKEDITOR.replace(element[0]);
if(!ngModel)return;
ck.on('pasteState', function () {
scope.$apply(function () {
ngModel.$setViewValue(ck.getData());
});
});
ngModel.$render = function (value) {
ck.setData(ngModel.$viewValue);
};
}
};
});
// ng-repeat
<div ng-repeat="key in []| range:0:(vm.listCount-1)">
<textarea ckeditor id="content_{{key + 1}}"
ng-model="vm.contentList[key].content">
</textarea>
</div>
In controller I am trying to get instances list. There instead of
content_0,content_1 etc. I am getting content_{{key + 1}} only one instance
console.log(CKEDITOR.instances);
I want to get the proper instance of the ckeditor but I am getting only one value that is content_{{key + 1}} Please someone suggest.
My guess is that the directive needs to set the id attribute before invoking CKEDITOR.replace:
app.directive('ckeditor', function () {
return {
require: '?ngModel',
link: function (scope, element, attr, ngModel) {
//COMPUTE id attribute
if (attr.key) {
var keyValue = scope.$eval(attr.key);
element[0].id += "_"+keyValue;
};
var ck = CKEDITOR.replace(element[0]);
if(!ngModel)return;
ck.on('pasteState', function () {
scope.$apply(function () {
ngModel.$setViewValue(ck.getData());
});
});
ngModel.$render = function (value) {
ck.setData(ngModel.$viewValue);
};
}
};
});
Usage:
<div ng-repeat="key in [0,1]">
<textarea ckeditor key="$index+1" id="content"
ng-model="vm.contentList[key].content">
</textarea>
</div>
The CKEDITOR is likely instantiating the editor before the AngularJS framework computes id="content_{{key + 1}}".
So I'm beginner to angularjs and firebase and I'm trying to develop an app which adds values(numerical) on an input. So far I have this:
app.js:
var app = angular.module("app", ['firebase']);
app.directive('addOne', function() {
return {
link: function(scope,element) {
element.bind('click', function() {
console.log(element.parent().find('input'))
element.parent().find('input')[1].value++;
});
}
}
});
and my view:
<section class="form-group">
<label for="">$</label> <input type="button" value="+" add-one>
<input ng-model="user.level" type="text" class="form-control" />
</section>
and my controller:
app.controller('mController', ['$scope', 'User',
function($scope, backHome, User, adicionar){
$scope.user = User(1);
User(1).$bindTo($scope, "user");
}
]);
the thing is that after I click the button with the directive add-one the value of the input changes but the $bindTo is not working...
So why does the bindTo doesn't work when I make a change directly in the DOM?
AngularJS doesn't care what the value of an input is set to, it only cares about what's in the ng-model. Try this...
app.directive('addOne', function() {
return {
link: function(scope,element) {
element.on('click', function() {
scope.$apply(function(){
scope.user.level++
});
});
}
}
});
As pointed out by #PankajParkar, you also need to use scope.$apply when you want to update a binding from event.
angular.module('demo', [])
.controller('DemoController', function($scope){
$scope.user={level: 1};
})
.directive('addOne', function() {
return {
link: function(scope, element, attrs) {
element.on('click', function() {
scope.$apply(scope.user.level++);
});
}
}
})
.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "=",
txt: '#buttonText'
},
template: '<input type="text" ng-model="model" /><button>{{txt}}</button>',
link: function(scope, element, attrs) {
if(angular.isDefined(attrs.initialVal)) {
scope.model = attrs.initialVal;
}
element.on('click', function() {
if (attrs.direction === 'decrement') {
scope.$apply(scope.model--);
} else {
scope.$apply(scope.model++);
}
});
}
};
});
<script src="https://code.angularjs.org/1.3.15/angular.min.js"></script>
<div ng-app="demo" ng-controller="DemoController">
<input type="text" ng-model="user.level">
<input type="button" value="+" add-one>
<hr>
<unary-input button-text="Add one" model="user.level" direction="increment"></unary-input>
<unary-input button-text="-" model="user.level" direction="decrement"></unary-input>
<hr>
<unary-input button-text="-" model="user.val" direction="decrement" initial-val="10"></unary-input>
</div>
In AngularJS, you want to change the view by changing the model that it's based on, versus doing it imperatively like you might with a traditional jQuery approach for example (traversing the DOM and incrementing the value).
UPDATE
Okay, so here's a nice reusable version of the (please check the snippet to see it in action).
The template includes both the button and the input. It accepts 4 values that you set as attributes:
button-text: The text you want to show on the button.
model: The model value for the input.
initial-val: The initial value for the input if you don't want to initialize on your controller.
direction: Whether to increment or decrement the values. This one currently accepts a string "decrement" to subtract. If you have no direction set or any other value set in the attribute, it will increment.
So, you would use it like this:
<unary-input button-text="Subtract One" model="user.val" direction="decrement" initial-val="10"></unary-input>
And the directive itself looks like this:
.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "=",
txt: '#buttonText'
},
template: '<input type="text" ng-model="model" /><button>{{txt}}</button>',
link: function(scope, element, attrs) {
if(angular.isDefined(attrs.initialVal)) {
scope.model = attrs.initialVal;
}
element.on('click', function() {
if (attrs.direction === 'decrement') {
scope.$apply(scope.model--);
} else {
scope.$apply(scope.model++);
}
});
}
};
});
Browsing around I could find a solution doing the way you said in the comments (two buttons one incrementing and another decrementing) thanks a lot for the help! and here's the final version.
app.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "="
},
template: '<input type="text" ng-model="model" /><button ng-click="decrement()">-</button><button ng-click="increment()">+</button>',
link: function(scope, element) {
scope.increment = function() {
scope.model++;
}
scope.decrement = function() {
scope.model--;
}
}
};
});
I have been trying to create a very simple directive that checks if the attribute my-minlength is a string (not null) and if it is; add the attribute ng-minlength to the input element. I feel like this should work, but it is simply not working.
My Directive
var app = angular.module("fooApplication", [])
app.directive('myMinlength', function() {
return {
link: function(scope, element, attrs) {
if(element == "") {
attrs.$set('ng-minlength', attrs.myMinlength);
}
},
}
});
My HTML
<input type="text" name="foo" my-minlength="5" required/>
Edit the suggestion completely stripped from possible errors - still does not work.
.directive('myMinlength', ['$compile', function($compile) {
return {
link: function(scope, element, attrs) {
if(true) {
attrs.$set('ng-minlength', "5");
$compile(element)(scope);
}
},
}
}]);
You can use simpler approach:
<input type="text" name="foo" ng-minlength="myvar || 0" required/>
if scope.myvar is undefined then minlength will be 0
You need to recompile your directive in order to add the ng-minlength attribute to your directive. Try this :
.directive('myMinlength', ['$compile', function($compile) {
return {
link: function(scope, element, attrs) {
if(element == "") {
attrs.$set('ng-minlength', attrs.myMinlength);
$compile(element)(scope);
}
},
}
}]);
you should use compile
.directive('myMinlength', function() {
var directive = {};
directive.restrict = 'A'; /* restrict this directive to attributess */
directive.compile = function(element, attributes) {
var linkFunction = function($scope, element, attributes) {
attributes.$set('ng-minlength', attrs.myMinlength);
}
return linkFunction;
}
return directive;
})
I have places in my code where I have this:
<input data-ng-disabled="SOME_SCOPE_VARIABLE" />
I would like to be able to use it like this too:
<input data-ng-autofocus="SOME_SCOPE_VARIABLE" />
Or even better, mimicking how ng-style is done:
<input data-ng-attribute="{autofocus: SOME_SCOPE_VARIABLE}" />
Does this exist in the current version of AngularJS? I noticed in the code there's a BOOLEAN_ATTR which gets all the attr's that AngularJS supports. I don't want to modify that in fear of changing versions and forgetting to update.
Update: AngularJS now has an ngFocus directive that evaluates an expression on focus, but I mention it here for the sake of completeness.
The current version of AngularJS doesn't have a focus directive, but it's in the roadmap. Coincidentally, we were talking about this on the mailing list yesterday, and I came up with this:
angular.module('ng').directive('ngFocus', function($timeout) {
return {
link: function ( scope, element, attrs ) {
scope.$watch( attrs.ngFocus, function ( val ) {
if ( angular.isDefined( val ) && val ) {
$timeout( function () { element[0].focus(); } );
}
}, true);
element.bind('blur', function () {
if ( angular.isDefined( attrs.ngFocusLost ) ) {
scope.$apply( attrs.ngFocusLost );
}
});
}
};
});
Which works off a scope variable as you requested:
<input type="text" ng-focus="isFocused" ng-focus-lost="loseFocus()">
Here's a fiddle: http://jsfiddle.net/ANfJZ/39/
You can do this with the built-in ngAttr attribute bindings.
<input ng-attr-autofocus="{{SOME_SCOPE_VARIABLE}}">
The autofocus attribute will be added if SOME_SCOPE_VARIABLE is defined (even if it's false), and will be removed if it's undefined. So I force falsy values to be undefined.
$scope.SOME_SCOPE_VARIABLE = someVar || undefined;
This directive should do the trick:
angular.module('utils.autofocus', [])
.directive('autofocus', ['$timeout', function($timeout) {
return {
restrict: 'A',
scope: {'autofocus':'='}
link : function($scope, $element) {
$scope.$watch 'autofocus', function(focus){
if(focus){
$timeout(function() {
$element[0].focus();
});
}
}
}
}
}]);
Taken from here: https://gist.github.com/mlynch/dd407b93ed288d499778
scope.doFocus = function () {
$timeout(function () {
document.getElementById('you_input_id').focus();
});
};
Create a directive like this
.directive('autoFocus', ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function ($scope, $element) {
$timeout(function () {
$element[0].focus();
});
}
}
<input type="text" auto-focus class="form-control msd-elastic" placeholder="">
What I did is using regular autofocus on my inputs: <input autofocus>
And then I set the focus on the first visible input with autofocus when angular is ready:
angular.element(document).ready(function() {
$('input[autofocus]:visible:first').focus();
});
Hope this helps.
I did it with two custom directives, something like this:
(function(angular) {
'use strict';
/* #ngInject */
function myAutoFocus($timeout) {
return {
restrict: 'A',
link: function(scope, element) {
$timeout(function() {
element[0].focus();
}, 300);
}
};
}
function myFocusable() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var focusMethodName = attrs.myFocusable;
scope[focusMethodName] = function() {
element[0].focus();
};
}
};
}
angular
.module('myFocusUtils', [])
.directive('myAutoFocus', myAutoFocus)
.directive('myFocusable', myFocusable);
}(angular));
If you add attribute my-auto-focus to an element, it will receive focus after 300ms. I set the value to 300 instead of 0 to let other async components to load before setting the focus.
The attribute my-focusable will create a function in the current scope. This function will set focus to the element when called. As it creates something in the scope, be cautious to avoid overriding something.
This way you don't need to add something to Angular's digest cycle (watch) and can do it entirely in the view:
<input my-focusable="focusOnInput"></input>
<button ng-click="focusOnInput()">Click to focus</button>
I created a JSFiddle to show the myFocusable directive: http://jsfiddle.net/8shLj3jc/
For some reason I don't know, the myAutoFocus directive does not work in JSFiddle, but it works in my page.
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="namesCtrl">
<div ng-repeat="x in names">
<input ng-attr-focus={{$first}} value="{{x.name + ', ' + x.country }}" />
</div>
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('namesCtrl', function($scope) {
$scope.names = [
{name:'x1',country:'y1'},
{name:'x2',country:'y2'},
{name:'x3',country:'y3'}
];
});
myApp.directive("focus", function(){
return {
restrict: "A",
link: function link(scope, element, attrs) {
if(JSON.parse(attrs.focus)){
element[0].focus();
}
}
};
});
</script>
</body>
</html>
had created above custom directive for one of my use case.
always focusses on first input element.
works for ajax data, browser back/forward buttons.
Tested on chrome and firefox(default autofocus is not supported here)
JSON.parse is used to parse string "true" returned from html to boolean true in JS.
another way to use attrs.focus === "true" for if condition.
so without $timeout you can also use auto focus like this -
<input type="text" ng-show="{{condition}}" class='input-class'></input>
angular.element(document).ready(function(){
angular.element('.input-class')[0].focus();
});
Combining whar others mentioned above:
JS Code:
myApp.directive('ngAutofocus', ['$timeout', function ($timeout) {
var linker = function ($scope, element, attrs) {
$scope.$watch('pageLoaded', function (pageLoaded) {
if (pageLoaded) {
$timeout(function () {
element[0].focus();
});
}
});
};
return {
restrict: 'A',
link: linker
};
}]);
HTML:
<input type="text" ng-model="myField" class="input-block-level edit-item" ng-autofocus>
Set pageLoaded to true from your initial load method of the page get:
var loadData = function () {
..
return $http.get(url).then(function (requestResponse) {
$scope.pageLoaded = true;
......
}
Is there a cleaner way of delegating focus to an element when a checkbox is clicked. Here's the dirty version I hacked:
HTML
<div ng-controller="MyCtrl">
<input type="checkbox" ng-change="toggled()">
<input id="name">
</div>
JavaScript
var myApp = angular.module('myApp',[]);
function MyCtrl($scope, $timeout) {
$scope.value = "Something";
$scope.toggled = function() {
console.debug('toggled');
$timeout(function() {
$('#name').focus();
}, 100);
}
}
JSFiddle: http://jsfiddle.net/U4jvE/8/
how about this one ? plunker
$scope.$watch('isChecked', function(newV){
newV && $('#name').focus();
},true);
#asgoth and #Mark Rajcok are correct. We should use directive. I was just lazy.
Here is the directive version. plunker I think one good reason to make it as directive is you can reuse this thing.
so in your html you can just assign different modals to different sets
<input type="checkbox" ng-model="isCheckedN">
<input xng-focus='isCheckedN'>
directive('xngFocus', function() {
return function(scope, element, attrs) {
scope.$watch(attrs.xngFocus,
function (newValue) {
newValue && element.focus();
},true);
};
});
Another directive implementation (that does not require jQuery), and borrowing some of #maxisam's code:
myApp.directive('focus', function() {
return function(scope, element) {
scope.$watch('focusCheckbox',
function (newValue) {
newValue && element[0].focus()
})
}
});
HTML:
<input type="checkbox" ng-model="focusCheckbox">
<input ng-model="name" focus>
Fiddle.
Since this directive doesn't create an isolate scope (or a child scope), the directive assumes the scope has a focusCheckbox property defined.
If you want to make it more interesting, and support for any expression to be evaluated (not only variables), you can do this:
app.directive('autofocusWhen', function ($timeout) {
return {
link: function(scope, element, attrs) {
scope.$watch(attrs.autofocusWhen, function(newValue){
if ( newValue ) {
$timeout(function(){
element.focus();
});
}
});
}
};
});
And your html can be a little more decoupled, like that:
<input type="checkbox" ng-model="product.selected" />
{{product.description}}
<input type="text" autofocus-when="product.selected" />
A cleaner way is to use a directive to perform the toggle:
app.directive('toggle', function() {
return {
restrict: 'A',
scope: {
selector: '='
},
link: function(scope, element, attrs) {
element.on('change', function() {
$(scope.selector).focus();
scope.$apply();
});
}
}:
});
Your html would be sth like:
<input type='checkbox' toggle selector='#name'>