AngularJs: How to check for changes in file input fields? - angularjs

I am new to angular. I am trying to read the uploaded file path from HTML 'file' field whenever a 'change' happens on this field. If i use 'onChange' it works but when i use it angular way using 'ng-change' it doesn't work.
<script>
var DemoModule = angular.module("Demo",[]);
DemoModule .controller("form-cntlr",function($scope){
$scope.selectFile = function()
{
$("#file").click();
}
$scope.fileNameChaged = function()
{
alert("select file");
}
});
</script>
<div ng-controller="form-cntlr">
<form>
<button ng-click="selectFile()">Upload Your File</button>
<input type="file" style="display:none"
id="file" name='file' ng-Change="fileNameChaged()"/>
</form>
</div>
fileNameChaged() is never calling. Firebug also doesn't show any error.

I made a small directive to listen for file input changes.
View JSFiddle
view.html:
<input type="file" custom-on-change="uploadFile">
controller.js:
app.controller('myCtrl', function($scope){
$scope.uploadFile = function(event){
var files = event.target.files;
};
});
directive.js:
app.directive('customOnChange', function() {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var onChangeHandler = scope.$eval(attrs.customOnChange);
element.on('change', onChangeHandler);
element.on('$destroy', function() {
element.off();
});
}
};
});

No binding support for File Upload control
https://github.com/angular/angular.js/issues/1375
<div ng-controller="form-cntlr">
<form>
<button ng-click="selectFile()">Upload Your File</button>
<input type="file" style="display:none"
id="file" name='file' onchange="angular.element(this).scope().fileNameChanged(this)" />
</form>
</div>
instead of
<input type="file" style="display:none"
id="file" name='file' ng-Change="fileNameChanged()" />
can you try
<input type="file" style="display:none"
id="file" name='file' onchange="angular.element(this).scope().fileNameChanged()" />
Note: this requires the angular application to always be in debug mode. This will not work in production code if debug mode is disabled.
and in your function changes
instead of
$scope.fileNameChanged = function() {
alert("select file");
}
can you try
$scope.fileNameChanged = function() {
console.log("select file");
}
Below is one working example of file upload with drag drop file upload may be helpful
http://jsfiddle.net/danielzen/utp7j/
Angular File Upload Information
URL for AngularJS File Upload in ASP.Net
https://github.com/geersch/AngularJSFileUpload
AngularJs native multi-file upload with progress with NodeJS
http://jasonturim.wordpress.com/2013/09/12/angularjs-native-multi-file-upload-with-progress/
ngUpload - An AngularJS Service for uploading files using iframe
http://ngmodules.org/modules/ngUpload

This is a refinement of some of the other ones around, the data will end up in an ng-model, which is normally what you want.
Markup (just make an attribute data-file so the directive can find it)
<input
data-file
id="id_image" name="image"
ng-model="my_image_model" type="file">
JS
app.directive('file', function() {
return {
require:"ngModel",
restrict: 'A',
link: function($scope, el, attrs, ngModel){
el.bind('change', function(event){
var files = event.target.files;
var file = files[0];
ngModel.$setViewValue(file);
$scope.$apply();
});
}
};
});

The clean way is to write your own directive to bind to "change" event.
Just to let you know IE9 does not support FormData so you cannot really get the file object from the change event.
You can use ng-file-upload library which already supports IE with FileAPI polyfill and simplify the posting the file to the server. It uses a directive to achieve this.
<script src="angular.min.js"></script>
<script src="ng-file-upload.js"></script>
<div ng-controller="MyCtrl">
<input type="file" ngf-select="onFileSelect($files)" multiple>
</div>
JS:
//inject angular file upload directive.
angular.module('myApp', ['ngFileUpload']);
var MyCtrl = [ '$scope', 'Upload', function($scope, Upload) {
$scope.onFileSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var $file = $files[i];
Upload.upload({
url: 'my/upload/url',
data: {file: $file}
}).then(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
});
}
}
}];

I've expanded on #Stuart Axon's idea to add two-way binding for the file input (i.e. allow resetting the input by resetting the model value back to null):
app.directive('bindFile', [function () {
return {
require: "ngModel",
restrict: 'A',
link: function ($scope, el, attrs, ngModel) {
el.bind('change', function (event) {
ngModel.$setViewValue(event.target.files[0]);
$scope.$apply();
});
$scope.$watch(function () {
return ngModel.$viewValue;
}, function (value) {
if (!value) {
el.val("");
}
});
}
};
}]);
Demo

Similar to some of the other good answers here, I wrote a directive to solve this problem, but this implementation more closely mirrors the angular way of attaching events.
You can use the directive like this:
HTML
<input type="file" file-change="yourHandler($event, files)" />
As you can see, you can inject the files selected into your event handler, as you would inject an $event object into any ng event handler.
Javascript
angular
.module('yourModule')
.directive('fileChange', ['$parse', function($parse) {
return {
require: 'ngModel',
restrict: 'A',
link: function ($scope, element, attrs, ngModel) {
// Get the function provided in the file-change attribute.
// Note the attribute has become an angular expression,
// which is what we are parsing. The provided handler is
// wrapped up in an outer function (attrHandler) - we'll
// call the provided event handler inside the handler()
// function below.
var attrHandler = $parse(attrs['fileChange']);
// This is a wrapper handler which will be attached to the
// HTML change event.
var handler = function (e) {
$scope.$apply(function () {
// Execute the provided handler in the directive's scope.
// The files variable will be available for consumption
// by the event handler.
attrHandler($scope, { $event: e, files: e.target.files });
});
};
// Attach the handler to the HTML change event
element[0].addEventListener('change', handler, false);
}
};
}]);

This directive pass the selected files as well:
/**
*File Input - custom call when the file has changed
*/
.directive('onFileChange', function() {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var onChangeHandler = scope.$eval(attrs.onFileChange);
element.bind('change', function() {
scope.$apply(function() {
var files = element[0].files;
if (files) {
onChangeHandler(files);
}
});
});
}
};
});
The HTML, how to use it:
<input type="file" ng-model="file" on-file-change="onFilesSelected">
In my controller:
$scope.onFilesSelected = function(files) {
console.log("files - " + files);
};

I recommend to create a directive
<input type="file" custom-on-change handler="functionToBeCalled(params)">
app.directive('customOnChange', [function() {
'use strict';
return {
restrict: "A",
scope: {
handler: '&'
},
link: function(scope, element){
element.change(function(event){
scope.$apply(function(){
var params = {event: event, el: element};
scope.handler({params: params});
});
});
}
};
}]);
this directive can be used many times, it uses its own scope and doesn't depend on parent scope. You can also give some params to handler function. Handler function will be called with scope object, that was active when you changed the input.
$apply updates your model each time the change event is called

The simplest Angular jqLite version.
JS:
.directive('cOnChange', function() {
'use strict';
return {
restrict: "A",
scope : {
cOnChange: '&'
},
link: function (scope, element) {
element.on('change', function () {
scope.cOnChange();
});
}
};
});
HTML:
<input type="file" data-c-on-change="your.functionName()">

Working Demo of "files-input" Directive that Works with ng-change1
To make an <input type=file> element work the ng-change directive, it needs a custom directive that works with the ng-model directive.
<input type="file" files-input ng-model="fileList"
ng-change="onInputChange()" multiple />
The DEMO
angular.module("app",[])
.directive("filesInput", function() {
return {
require: "ngModel",
link: function postLink(scope,elem,attrs,ngModel) {
elem.on("change", function(e) {
var files = elem[0].files;
ngModel.$setViewValue(files);
})
}
}
})
.controller("ctrl", function($scope) {
$scope.onInputChange = function() {
console.log("input change");
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app" ng-controller="ctrl">
<h1>AngularJS Input `type=file` Demo</h1>
<input type="file" files-input ng-model="fileList"
ng-change="onInputChange()" multiple />
<h2>Files</h2>
<div ng-repeat="file in fileList">
{{file.name}}
</div>
</body>

Too complete solution base on:
`onchange="angular.element(this).scope().UpLoadFile(this.files)"`
A simple way to hide the input field and replace it with a image, here after a solution, that also require a hack on angular but that do the job [TriggerEvent does not work as expected]
The solution:
place the input-field in display:none [the input field exist in the DOM but is not visible]
place your image right after
On the image use nb-click() to activate a method
When the image is clicked simulate a DOM action 'click' on the input field. Et voilà!
var tmpl = '<input type="file" id="{{name}}-filein"' +
'onchange="angular.element(this).scope().UpLoadFile(this.files)"' +
' multiple accept="{{mime}}/*" style="display:none" placeholder="{{placeholder}}">'+
' <img id="{{name}}-img" src="{{icon}}" ng-click="clicked()">' +
'';
// Image was clicked let's simulate an input (file) click
scope.inputElem = elem.find('input'); // find input in directive
scope.clicked = function () {
console.log ('Image clicked');
scope.inputElem[0].click(); // Warning Angular TriggerEvent does not work!!!
};

Another interesting way to listen to file input changes is with a watch over the ng-model attribute of the input file. Of course, FileModel is a custom directive.
Like this:
HTML -> <input type="file" file-model="change.fnEvidence">
JS Code ->
$scope.$watch('change.fnEvidence', function() {
alert("has changed");
});
Hope it can help someone.

I have done it like this;
<!-- HTML -->
<button id="uploadFileButton" class="btn btn-info" ng-click="vm.upload()">
<span class="fa fa-paperclip"></span></button>
<input type="file" id="txtUploadFile" name="fileInput" style="display: none;" />
// self is the instance of $scope or this
self.upload = function () {
var ctrl = angular.element("#txtUploadFile");
ctrl.on('change', fileNameChanged);
ctrl.click();
}
function fileNameChanged(e) {
console.log(self.currentItem);
alert("select file");
}

Angular elements (such as the root element of a directive) are jQuery [Lite] objects. This means we can register the event listener like so:
link($scope, $el) {
const fileInputSelector = '.my-file-input'
function setFile() {
// access file via $el.find(fileInputSelector).get(0).files[0]
}
$el.on('change', fileInputSelector, setFile)
}
This is jQuery event delegation. Here, the listener is attached to the root element of the directive. When the event is triggered, it will bubble up to the registered element and jQuery will determine if the event originated on an inner element matching the defined selector. If it does, the handler will fire.
Benefits of this method are:
the handler is bound to the $element which will be automatically cleaned up when the directive scope is destroyed.
no code in the template
will work even if the target delegate (input) has not yet been rendered when you register the event handler (such as when using ng-if or ng-switch)
http://api.jquery.com/on/

You can simply add the below code in onchange and it will detect change. you can write a function on X click or something to remove file data..
document.getElementById(id).value = "";

Related

AngularJS 1.5 scope in directive ng-include

Here's some code http://jsfiddle.net/miuosh/n6yppypj/ with uploadfile directive. The problem is that I use this
<input type="file" file-model="myFile" />
<button ng-click="uploadFile()">upload me</button>
in ng-include="'views/uploadFileForm.html'".
In directive I add "myFile" to scope. It turns out that Angular create new scope with myFile. To add "myFile" to "rootScope" I need to use
modelSetter(scope.$parent.$parent.$parent[..],element[0].files[0])
which is inconvenient because I need to know how many parent scope I have.
I have faced similar problem dealing with file input with angular.
you can create a directive which will listen to file change and call its controller function with file. Here jsFiddle for it.
var app = angular.module('app', []);
app.controller('yourCtrl', function() {
$scope.image;
$scope.imageChanged= function (files) {
$scope.image = files;
};
});
app.directive('customOnChange', function() {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var onChangeFunc = scope.$eval(attrs.customOnChange);
element.bind('change', function(event){
var files = event.target.files;
onChangeFunc(files);
});
element.bind('click', function(){
element.val('');
});
}
};
})
<input type="file" id="imgInput" custom-on-change="imageChanged"/>

Using custom directive with isolated scope, in a modal

I use a custom directive to get places from Google API. This directive works like a charm in a controller. But when I want to use it inside a modal, it doesn't work any more. It's a question of scope, but I can't figure out what's exactly happened. Any idea ?
My directive :
'use strict';
angular.module('app').directive('googleplace', function() {
return {
require: 'ngModel',
scope: {
ngModel: '=',
details: '=?'
},
link: function(scope, element, attrs, model) {
var options;
options = {
types: ['address'],
componentRestrictions: {}
};
scope.gPlace = new google.maps.places.Autocomplete(element[0], options);
google.maps.event.addListener(scope.gPlace, 'place_changed', function() {
scope.$apply(function() {
scope.details = scope.gPlace.getPlace();
if (scope.details.name) {
element.val(scope.details.name);
model.$setViewValue(scope.details.name);
element.bind('blur', function(value) {
if (value.currentTarget.value !== '') {
element.val(scope.details.name);
}
});
}
});
});
}
};
});
My modal controller :
modalInstance = $modal.open
templateUrl: "modal.html"
controller: ($scope, $modalInstance) ->
$scope.$watch 'placeDetails', ->
_.forEach $scope.placeDetails.address_components, (val, key) ->
$scope.myaddress = val.short_name + ' ' if val.types[0] is 'street_number'
return
And finally, my html :
<div class="modal-body">
<div>
<input type="text" placeholder="Start typing" ng-model="address" details="placeDetails" googleplace />
</div>
<div>
<input type="text" ng-model="myaddress">
</div>
</div>
I should have the ng-model="address" populated with the result of the call to Google Place API, and the ng-model="myaddress" populated by the $watch, but nothing happens.
Here is my plunkr http://plnkr.co/edit/iEAooKgfUUfxoBWm8mgw?p=preview
Click on "Open modal" causes the error : Cannot read property 'address_components' of undefined
working demo
According to how to create modal in angularjs
Extra things that i added :
1 : New Controller for modal
2 : Blur function that fires on property change Instead of $watch

Angular.js: Set CSS when Input is on Focus

Does someone knows how to get the following working:
If an user clicks inside "name" - Set CSS Class to XYZ on DIV ?
<div ng-class="???">Enter your Name here</div>
<input type="text" ng-model="user.name" required id="name"/>
Version: AngularJS v1.0.8
If you're using Angular 1.2.x, see ng-focus and ng-blur:
<div ng-class="{xyz: focused}">Enter your name here</div>
<input type="text" ng-model="user.name" ng-init="focused = false" ng-focus="focused = true" ng-blur="focused = false" id="name" required>
If you're using a 1.0.x version, nothing is stopping you from defining your own focus and blur directives based on Angular 1.2.x's:
/*
* A directive that allows creation of custom onclick handlers that are defined as angular
* expressions and are compiled and executed within the current scope.
*
* Events that are handled via these handler are always configured not to propagate further.
*/
var ngEventDirectives = {};
forEach(
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
function(name) {
var directiveName = directiveNormalize('ng-' + name);
ngEventDirectives[directiveName] = ['$parse', function($parse) {
return function(scope, element, attr) {
var fn = $parse(attr[directiveName]);
element.on(lowercase(name), function(event) {
scope.$apply(function() {
fn(scope, {$event:event});
});
});
};
}];
}
);
Just use this directive:
app.directive('ngFocusClass', function () {
return ({
restrict: 'A',
link: function(scope, element) {
element.focus(function () {
element.addClass('focus');
});
element.blur(function () {
element.removeClass('focus');
});
}
});
});
Working example for pre-1.2.xxx versions: http://jsfiddle.net/atXAC/
In this example, the ng-customblur directive will fire a function() in your controller.
HTML:
<div ng-controller="MyCtrl">
<div ng-class="{'active':hasFocus==true,'inactive':hasFocus==false}">Enter your Name here</div>
<input type="text" ng-model="user.name" ng-click="hasFocus=true" ng-customblur="onBlur()" required id="name"/>
</div>
JS:
myApp.directive('ngCustomblur', ['$parse', function($parse) {
return function(scope, element, attr) {
var fn = $parse(attr['ngCustomblur']);
element.bind('blur', function(event) {
scope.$apply(function() {
fn(scope, {$event:event});
});
});
}
}]);
function MyCtrl($scope) {
$scope.onBlur = function(){
$scope.hasFocus = false;
}
}

How to clear a file input from Angular JS

In AngularJS, I'm using the approach described here to handle input type=file.
https://groups.google.com/forum/?fromgroups=#!topic/angular/-OpgmLjFR_U
http://jsfiddle.net/marcenuc/ADukg/89/
Markup:
<div ng-controller="MyCtrl">
<input type="file" onchange="angular.element(this).scope().setFile(this)">
{{theFile.name}}
</div>
Controller:
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function($scope) {
$scope.setFile = function(element) {
$scope.$apply(function($scope) {
$scope.theFile = element.files[0];
});
};
});
As mentioned it's a bit of a hack, but it mostly works for my purposes. What I need however is a way to clear the file input after the upload has finished - ie: from the controller.
I could completely hack it and use jQuery or something to find the input element and clear it, but was hoping for something a little more elegant.
Upon a successful upload, I clear up the input type file elements explicitly from my controller, like so:
angular.forEach(
angular.element("input[type='file']"),
function(inputElem) {
angular.element(inputElem).val(null);
});
The input[type='file'] selector requires jQuery, but everything else is plain Angular.
I would definitely use directive for this kind of task.
http://plnkr.co/edit/xLM9VX
app.directive('fileSelect', function() {
var template = '<input type="file" name="files"/>';
return function( scope, elem, attrs ) {
var selector = $( template );
elem.append(selector);
selector.bind('change', function( event ) {
scope.$apply(function() {
scope[ attrs.fileSelect ] = event.originalEvent.target.files;
});
});
scope.$watch(attrs.fileSelect, function(file) {
selector.val(file);
});
};
});
note: it is using jquery for element creation.
my solution without using $scope.
app.directive('fileChange',['UploadService',function (UploadService) {
var linker = function (element, attrs) {
element.bind('change', function (event) {
var files = event.target.files;
UploadService.upload({'name':attrs['name'],'file':files[0]});
element.val(null); // clear input
});
};
return {
restrict: 'A',
link: linker
};
}]);
It might help you!!
HTML code sample
<input type="file" id="fileMobile" file-model="myFile">
<button type="button" class="btn btn-danger" id="i-agree" ng-click="uploadFile()"> Upload </button>
AngularJs code sample
$scope.uploadFile = function () {
var file = $scope.myFile;
mobileService.uploadBulkFile(file).then(function (resp) {
if (resp !== undefined) {
$('#fileMobile').val('');
}
});
};
You can use ID to reset file field.
<div class="col-md-8">
<label for="files">Select File</label>
<input type="file" id="file_upload" class="form-control">
</div>
After uploading clear it.
var fileElement = angular.element('#file_upload');
angular.element(fileElement).val(null);
Above example working good for me. Will work for you too.
In my case, I broadcast events when a file upload succeeds.
So my directive watches for the broadcast, and clears the selection.
app.directive("fileInput", function( APP_EVENTS ){
return{
require: "ngModel",
link: function postLink( scope, elem, attrs, ngModel ){
elem.on("change", function( e ){
var files=elem[0].files;
ngModel.$setViewValue( files );
});
scope.$on( APP_EVENTS.FILE_UPLOAD_SUCCESS, function( event ){
elem.val( null );
});
}
}
});
It's used like so:
<input type="file" name="myFieldName" ng-model="myModel" file-input/>

How to set focus on input field?

What is the 'Angular way' to set focus on input field in AngularJS?
More specific requirements:
When a Modal is opened, set focus on a predefined <input> inside this Modal.
Every time <input> becomes visible (e.g. by clicking some button), set focus on it.
I tried to achieve the first requirement with autofocus, but this works only when the Modal is opened for the first time, and only in certain browsers (e.g. in Firefox it doesn't work).
When a Modal is opened, set focus on a predefined <input> inside this Modal.
Define a directive and have it $watch a property/trigger so it knows when to focus the element:
Name: <input type="text" focus-me="shouldBeOpen">
app.directive('focusMe', ['$timeout', '$parse', function ($timeout, $parse) {
return {
//scope: true, // optionally create a child scope
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].focus();
});
}
});
// to address #blesh's comment, set attribute value to 'false'
// on blur event:
element.bind('blur', function () {
console.log('blur');
scope.$apply(model.assign(scope, false));
});
}
};
}]);
Plunker
The $timeout seems to be needed to give the modal time to render.
'2.' Everytime <input> becomes visible (e.g. by clicking some button), set focus on it.
Create a directive essentially like the one above. Watch some scope property, and when it becomes true (set it in your ng-click handler), execute element[0].focus(). Depending on your use case, you may or may not need a $timeout for this one:
<button class="btn" ng-click="showForm=true; focusInput=true">show form and
focus input</button>
<div ng-show="showForm">
<input type="text" ng-model="myInput" focus-me="focusInput"> {{ myInput }}
<button class="btn" ng-click="showForm=false">hide form</button>
</div>
app.directive('focusMe', function($timeout) {
return {
link: function(scope, element, attrs) {
scope.$watch(attrs.focusMe, function(value) {
if(value === true) {
console.log('value=',value);
//$timeout(function() {
element[0].focus();
scope[attrs.focusMe] = false;
//});
}
});
}
};
});
Plunker
Update 7/2013: I've seen a few people use my original isolate scope directives and then have problems with embedded input fields (i.e., an input field in the modal). A directive with no new scope (or possibly a new child scope) should alleviate some of the pain. So above I updated the answer to not use isolate scopes. Below is the original answer:
Original answer for 1., using an isolate scope:
Name: <input type="text" focus-me="{{shouldBeOpen}}">
app.directive('focusMe', function($timeout) {
return {
scope: { trigger: '#focusMe' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if(value === "true") {
$timeout(function() {
element[0].focus();
});
}
});
}
};
});
Plunker.
Original answer for 2., using an isolate scope:
<button class="btn" ng-click="showForm=true; focusInput=true">show form and
focus input</button>
<div ng-show="showForm">
<input type="text" focus-me="focusInput">
<button class="btn" ng-click="showForm=false">hide form</button>
</div>
app.directive('focusMe', function($timeout) {
return {
scope: { trigger: '=focusMe' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if(value === true) {
//console.log('trigger',value);
//$timeout(function() {
element[0].focus();
scope.trigger = false;
//});
}
});
}
};
});
Plunker.
Since we need to reset the trigger/focusInput property in the directive, '=' is used for two-way databinding. In the first directive, '#' was sufficient. Also note that when using '#' we compare the trigger value to "true" since # always results in a string.
##(EDIT: I've added an updated solution below this explanation)
Mark Rajcok is the man... and his answer is a valid answer, but it has had a defect (sorry Mark)...
...Try using the boolean to focus on the input, then blur the input, then try using it to focus the input again. It won't work unless you reset the boolean to false, then $digest, then reset it back to true. Even if you use a string comparison in your expression, you'll be forced to change the string to something else, $digest, then change it back. (This has been addressed with the blur event handler.)
So I propose this alternate solution:
Use an event, the forgotten feature of Angular.
JavaScript loves events after all. Events are inherently loosely coupled, and even better, you avoid adding another $watch to your $digest.
app.directive('focusOn', function() {
return function(scope, elem, attr) {
scope.$on(attr.focusOn, function(e) {
elem[0].focus();
});
};
});
So now you could use it like this:
<input type="text" focus-on="newItemAdded" />
and then anywhere in your app...
$scope.addNewItem = function () {
/* stuff here to add a new item... */
$scope.$broadcast('newItemAdded');
};
This is awesome because you can do all sorts of things with something like this. For one, you could tie into events that already exist. For another thing you start doing something smart by having different parts of your app publish events that other parts of your app can subscribe to.
Anyhow, this type of thing screams "event driven" to me. I think as Angular developers we try really hard to hammer $scope shaped pegs into event shape holes.
Is it the best solution? I don't know. It is a solution.
Updated Solution
After #ShimonRachlenko's comment below, I've changed my method of doing this slightly. Now I use a combination of a service and a directive that handles an event "behind the scenes":
Other than that, it's the same principal outlined above.
Here is a quick demo Plunk
###Usage
<input type="text" focus-on="focusMe"/>
app.controller('MyCtrl', function($scope, focus) {
focus('focusMe');
});
###Source
app.directive('focusOn', function() {
return function(scope, elem, attr) {
scope.$on('focusOn', function(e, name) {
if(name === attr.focusOn) {
elem[0].focus();
}
});
};
});
app.factory('focus', function ($rootScope, $timeout) {
return function(name) {
$timeout(function (){
$rootScope.$broadcast('focusOn', name);
});
}
});
I have found some of the other answers to be overly complicated when all you really need is this
app.directive('autoFocus', function($timeout) {
return {
restrict: 'AC',
link: function(_scope, _element) {
$timeout(function(){
_element[0].focus();
}, 0);
}
};
});
usage is
<input name="theInput" auto-focus>
We use the timeout to let things in the dom render, even though it is zero, it at least waits for that - that way this works in modals and whatnot too
HTML has an attribute autofocus.
<input type="text" name="fname" autofocus>
http://www.w3schools.com/tags/att_input_autofocus.asp
You can also use the jqlite functionality built into angular.
angular.element('.selector').trigger('focus');
This works well and an angular way to focus input control
angular.element('#elementId').focus()
This is although not a pure angular way of doing the task yet the syntax follows angular style. Jquery plays role indirectly and directly access DOM using Angular (jQLite => JQuery Light).
If required, this code can easily be put inside a simple angular directive where element is directly accessible.
I don't think $timeout is a good way to focus the element on creation. Here is a method using built-in angular functionality, dug out from the murky depths of the angular docs. Notice how the "link" attribute can be split into "pre" and "post", for pre-link and post-link functions.
Working Example: http://plnkr.co/edit/Fj59GB
// this is the directive you add to any element you want to highlight after creation
Guest.directive('autoFocus', function() {
return {
link: {
pre: function preLink(scope, element, attr) {
console.debug('prelink called');
// this fails since the element hasn't rendered
//element[0].focus();
},
post: function postLink(scope, element, attr) {
console.debug('postlink called');
// this succeeds since the element has been rendered
element[0].focus();
}
}
}
});
<input value="hello" />
<!-- this input automatically gets focus on creation -->
<input value="world" auto-focus />
Full AngularJS Directive Docs: https://docs.angularjs.org/api/ng/service/$compile
Here is my original solution:
plunker
var app = angular.module('plunker', []);
app.directive('autoFocus', function($timeout) {
return {
link: function (scope, element, attrs) {
attrs.$observe("autoFocus", function(newValue){
if (newValue === "true")
$timeout(function(){element[0].focus()});
});
}
};
});
And the HTML:
<button ng-click="isVisible = !isVisible">Toggle input</button>
<input ng-show="isVisible" auto-focus="{{ isVisible }}" value="auto-focus on" />
What it does:
It focuses the input as it becomes visible with ng-show. No use of $watch or $on here.
I've written a two-way binding focus directive, just like model recently.
You can use the focus directive like this:
<input focus="someFocusVariable">
If you make someFocusVariable scope variable true in anywhere in your controller, the input get focused. And if you want to "blur" your input then, someFocusVariable can be set to false. It's like Mark Rajcok's first answer but with two-way binding.
Here is the directive:
function Ctrl($scope) {
$scope.model = "ahaha"
$scope.someFocusVariable = true; // If you want to focus initially, set this to true. Else you don't need to define this at all.
}
angular.module('experiement', [])
.directive('focus', function($timeout, $parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.$watch(attrs.focus, function(newValue, oldValue) {
if (newValue) { element[0].focus(); }
});
element.bind("blur", function(e) {
$timeout(function() {
scope.$apply(attrs.focus + "=false");
}, 0);
});
element.bind("focus", function(e) {
$timeout(function() {
scope.$apply(attrs.focus + "=true");
}, 0);
})
}
}
});
Usage:
<div ng-app="experiement">
<div ng-controller="Ctrl">
An Input: <input ng-model="model" focus="someFocusVariable">
<hr>
<div ng-click="someFocusVariable=true">Focus!</div>
<pre>someFocusVariable: {{ someFocusVariable }}</pre>
<pre>content: {{ model }}</pre>
</div>
</div>
Here is the fiddle:
http://fiddle.jshell.net/ubenzer/9FSL4/8/
For those who use Angular with the Bootstrap plugin:
http://angular-ui.github.io/bootstrap/#/modal
You can hook into the opened promise of the modal instance:
modalInstance.opened.then(function() {
$timeout(function() {
angular.element('#title_input').trigger('focus');
});
});
modalInstance.result.then(function ( etc...
I found it useful to use a general expression. This way you can do stuff like automatically move focus when input text is valid
<button type="button" moo-focus-expression="form.phone.$valid">
Or automatically focus when the user completes a fixed length field
<button type="submit" moo-focus-expression="smsconfirm.length == 6">
And of course focus after load
<input type="text" moo-focus-expression="true">
The code for the directive:
.directive('mooFocusExpression', function ($timeout) {
return {
restrict: 'A',
link: {
post: function postLink(scope, element, attrs) {
scope.$watch(attrs.mooFocusExpression, function (value) {
if (attrs.mooFocusExpression) {
if (scope.$eval(attrs.mooFocusExpression)) {
$timeout(function () {
element[0].focus();
}, 100); //need some delay to work with ng-disabled
}
}
});
}
}
};
});
Not to resurrect a zombie or plug my own directive (ok that's exactly what I'm doing):
https://github.com/hiebj/ng-focus-if
http://plnkr.co/edit/MJS3zRk079Mu72o5A9l6?p=preview
<input focus-if />
(function() {
'use strict';
angular
.module('focus-if', [])
.directive('focusIf', focusIf);
function focusIf($timeout) {
function link($scope, $element, $attrs) {
var dom = $element[0];
if ($attrs.focusIf) {
$scope.$watch($attrs.focusIf, focus);
} else {
focus(true);
}
function focus(condition) {
if (condition) {
$timeout(function() {
dom.focus();
}, $scope.$eval($attrs.focusDelay) || 0);
}
}
}
return {
restrict: 'A',
link: link
};
}
})();
First, an official way to do focus is on the roadmap for 1.1. Meanwhile, you can write a directive to implement setting focus.
Second, to set focus on an item after it has become visible currently requires a workaround. Just delay your call to element focus() with a $timeout.
Because the same controller-modifies-DOM problem exists for focus, blur and select, I propose having an ng-target directive:
<input type="text" x-ng-model="form.color" x-ng-target="form.colorTarget">
<button class="btn" x-ng-click="form.colorTarget.focus()">do focus</button>
Angular thread here: http://goo.gl/ipsx4 , and more details blogged here: http://goo.gl/4rdZa
The following directive will create a .focus() function inside your controller as specified by your ng-target attribute. (It creates a .blur() and a .select() too.) Demo: http://jsfiddle.net/bseib/WUcQX/
Instead of creating your own directive, it's possible to simply use javascript functions to accomplish a focus.
Here is an example.
In the html file:
<input type="text" id="myInputId" />
In a file javascript, in a controller for example, where you want to activate the focus:
document.getElementById("myInputId").focus();
If you just wanted a simple focus that was controlled by an ng-click.
Html:
<input ut-focus="focusTigger">
<button ng-click="focusTrigger=!focusTrigger" ng-init="focusTrigger=false"></button>
Directive:
'use strict'
angular.module('focus',['ng'])
.directive('utFocus',function($timeout){
return {
link:function(scope,elem,attr){
var focusTarget = attr['utFocus'];
scope.$watch(focusTarget,function(value){
$timeout(function(){
elem[0].focus();
});
});
}
}
});
A simple one that works well with modals:
.directive('focusMeNow', ['$timeout', function ($timeout)
{
return {
restrict: 'A',
link: function (scope, element, attrs)
{
$timeout(function ()
{
element[0].focus();
});
}
};
}])
Example
<input ng-model="your.value" focus-me-now />
You could just create a directive that forces focus on the decorated element on postLinking:
angular.module('directives')
.directive('autoFocus', function() {
return {
restrict: 'AC',
link: function(_scope, _element) {
_element[0].focus();
}
};
});
Then in your html:
<input type="text" name="first" auto-focus/> <!-- this will get the focus -->
<input type="text" name="second"/>
This would work for modals and ng-if toggled elements, not for ng-show since postLinking happens only on HTML processing.
Mark and Blesh have great answers; however, Mark's has a flaw that Blesh points out (besides being complex to implement), and I feel that Blesh's answer has a semantic error in creating a service that's specifically about sending focus request to the frontend when really all he needed was a way to delay the event until all the directives were listening.
So here is what I ended up doing which steals a lot from Blesh's answer but keeps the semantics of the controller event and the "after load" service separate.
This allows the controller event to easily be hooked for things other than just focusing a specific element and also allows to incur the overhead of the "after load" functionality only if it is needed, which it may not be in many cases.
Usage
<input type="text" focus-on="controllerEvent"/>
app.controller('MyCtrl', function($scope, afterLoad) {
function notifyControllerEvent() {
$scope.$broadcast('controllerEvent');
}
afterLoad(notifyControllerEvent);
});
Source
app.directive('focusOn', function() {
return function(scope, elem, attr) {
scope.$on(attr.focusOn, function(e, name) {
elem[0].focus();
});
};
});
app.factory('afterLoad', function ($rootScope, $timeout) {
return function(func) {
$timeout(func);
}
});
This is also possible to use ngModelController. Working with 1.6+ (don't know with older versions).
HTML
<form name="myForm">
<input type="text" name="myText" ng-model="myText">
</form>
JS
$scope.myForm.myText.$$element.focus();
--
N.B.: Depending of the context, you maybe have to wrap in a timeout function.
N.B.²: When using controllerAs, this is almost the same. Just replace name="myForm" with name="vm.myForm" and in JS, vm.myForm.myText.$$element.focus();.
Probably, the simplest solution on the ES6 age.
Adding following one liner directive makes HTML 'autofocus' attribute effective on Angular.js.
.directive('autofocus', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())}))
Now, you can just use HTML5 autofocus syntax like:
<input type="text" autofocus>
Just a newbie here, but I was abble to make it work in a ui.bootstrap.modal with this directive:
directives.directive('focus', function($timeout) {
return {
link : function(scope, element) {
scope.$watch('idToFocus', function(value) {
if (value === element[0].id) {
$timeout(function() {
element[0].focus();
});
}
});
}
};
});
and in the $modal.open method I used the folowing to indicate the element where the focus should be putted:
var d = $modal.open({
controller : function($scope, $modalInstance) {
...
$scope.idToFocus = "cancelaAteste";
}
...
});
on the template I have this:
<input id="myInputId" focus />
The following directive did the trick for me. Use the same autofocus html attribute for input.
.directive('autofocus', [function () {
return {
require : 'ngModel',
restrict: 'A',
link: function (scope, element, attrs) {
element.focus();
}
};
}])
If you are using modalInstance and have the object you can use "then" to do actions after opening the modal. If you are not using the modalInstance, and hard coded to open the modal you can use the event. The $timeout is not a good solution.
You can do (Bootstrap3):
$("#" + modalId).on("shown.bs.modal", function() {
angular.element("[name='name']").focus();
});
At modalInstance you can look at library to how execute the code after open modal.
Don't use $timeout like this, the $timeout can be 0, 1, 10, 30, 50, 200 or more this will depend on client computer, and the process to open modal.
Don't use $timeout let the method tell you when you can focus ;)
I hope that this help! :)
All of the previous answer doesn't work if the desired focus element is injected in a directive template.
The following directive fit to both simple element or directive injected element (I wrote it in typescript). it accept selector for inner focusable element. if you just need to focus the self element - don't send any selector parameter to the directive :
module APP.Directives {
export class FocusOnLoadDirective implements ng.IDirective {
priority = 0;
restrict = 'A';
constructor(private $interval:any, private $timeout:any) {
}
link = (scope:ng.IScope, element:JQuery, attrs:any) => {
var _self = this;
var intervalId:number = 0;
var clearInterval = function () {
if (intervalId != 0) {
_self.$interval.cancel(intervalId);
intervalId = 0;
}
};
_self.$timeout(function(){
intervalId = _self.$interval(function () {
let focusableElement = null;
if (attrs.focusOnLoad != '') {
focusableElement = element.find(attrs.focusOnLoad);
}
else {
focusableElement = element;
}
console.debug('focusOnLoad directive: trying to focus');
focusableElement.focus();
if (document.activeElement === focusableElement[0]) {
clearInterval();
}
}, 100);
scope.$on('$destroy', function () {
// Make sure that the interval is destroyed too
clearInterval();
});
});
};
public static factory = ():ng.IDirectiveFactory => {
let directive = ($interval:any, $timeout:any) => new FocusOnLoadDirective($interval, $timeout);
directive.$inject = ['$interval', '$timeout'];
return directive;
};
}
angular.module('common').directive('focusOnLoad', FocusOnLoadDirective.factory());
}
usage example for simple element:
<button tabindex="0" focus-on-load />
usage example for inner element (usually for dynamic injected element like directive with template):
<my-directive focus-on-load="input" />
you can use any jQuery selector instead of "input"
If you wish to set focus on particular element, you can use below approach.
Create a service called focus.
angular.module('application')
.factory('focus', function ($timeout, $window) {
return function (id) {
$timeout(function () {
var element = $window.document.getElementById(id);
if (element)
element.focus();
});
};
});
Inject it into the controller from where you wish to call.
Call this service.
I edit Mark Rajcok's focusMe directive to work for multiple focus in one element.
HTML:
<input focus-me="myInputFocus" type="text">
in AngularJs Controller:
$scope.myInputFocus= true;
AngulaJS Directive:
app.directive('focusMe', function ($timeout, $parse) {
return {
link: function (scope, element, attrs) {
var model = $parse(attrs.focusMe);
scope.$watch(model, function (value) {
if (value === true) {
$timeout(function () {
scope.$apply(model.assign(scope, false));
element[0].focus();
}, 30);
}
});
}
};
});
I want to contribute to this discussion after searching for at better solution and not finding it, having to create it instead.
Criteria:
1. Solution should be independent of parent controller scope to increase re-usability.
2. Avoid the use of $watch to monitor some condition, this is both slow, increases the size of the digest loop and makes testing harder.
3. Avoid $timeout or $scope.$apply() to trigger a digest loop.
4. An input element is present within the element where the Directive is used open.
This is the solution I liked the most:
Directive:
.directive('focusInput', [ function () {
return {
scope: {},
restrict: 'A',
compile: function(elem, attr) {
elem.bind('click', function() {
elem.find('input').focus();
});
}
};
}]);
Html:
<div focus-input>
<input/>
</div>
I hope this will help someone out there!
I think the directive is unnecessary. Use HTML id and class attributes to select the required element and have the service use document.getElementById or document.querySelector to apply focus (or jQuery equivalents).
Markup is standard HTML/angular directives with added id/classes for selection
<input id="myInput" type="text" ng-model="myInputModel" />
Controller broadcasts event
$scope.$emit('ui:focus', '#myInput');
In UI service uses querySelector - if there are multiple matches (say due to class) it will only return the first
$rootScope.$on('ui:focus', function($event, selector){
var elem = document.querySelector(selector);
if (elem) {
elem.focus();
}
});
You may want to use $timeout() to force a digest cycle
Just throwing in some coffee.
app.directive 'ngAltFocus', ->
restrict: 'A'
scope: ngAltFocus: '='
link: (scope, el, attrs) ->
scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv
Not sure if relying on the timeout is a good idea, but this works for ng-repeat because this code runs AFTER angularjs updates the DOM, so you make sure all objects are there:
myApp.directive('onLastRepeat', [function () {
return function (scope, element, attrs) {
if (scope.$last) setTimeout(function () {
scope.$emit('onRepeatLast', element, attrs);
}, 1);
};
}]);
//controller for grid
myApp.controller('SimpleController', ['$scope', '$timeout', '$http', function ($scope, $timeout, $http)
{
var newItemRemoved = false;
var requiredAlert = false;
//this event fires up when angular updates the dom for the last item
//it's observed, so here, we stop the progress bar
$scope.$on('onRepeatLast', function (scope, element, attrs) {
//$scope.complete();
console.log('done done!');
$("#txtFirstName").focus();
});
}]);

Resources