How to clear a file input from Angular JS - angularjs

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/>

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, input field with a currency mask directive for money format on the fly

I'm trying to create an input mask for a EU money field using http://jquerypriceformat.com/
So far in my directive, the input shows correctly to the user with the mask applied, but I believe there's something wrong, because the POST values are being sent with weird formatting, totally different than what we see in the input field.
I include the priceformat.js
<script src="js/jquery.price_format.1.8.min.js"></script>
<input type="text" currency-input ng-model...>
And on angular:
app.directive('currencyInput', function() {
return {
require: '?ngModel',
link: function($scope, element, attrs, controller) {
element.priceFormat({
prefix: '',
centsSeparator: ',',
thousandsSeparator: '.'
});
}
};
});
My input shows the value with the mask correctly, but on POST data (called by angular) it's a different value, what am I missing?
input > 2.200,80 | post > 22,0080
Thanks
From your example I don't see that link returns something.
I would write directive something like:
.directive('format', ['$filter', function ($filter) {
return {
require: '?ngModel',
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) return;
ctrl.$formatters.unshift(function (a) {
return $filter(attrs.format)(ctrl.$modelValue)
});
ctrl.$parsers.unshift(function (viewValue) {
elem.priceFormat({
prefix: '',
centsSeparator: ',',
thousandsSeparator: '.'
});
return elem[0].value;
});
}
};
}]);
Demo 1 Fiddle
If you want on start fire the filter, use $formatters:
Now link is:
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) return;
var format = {
prefix: '',
centsSeparator: ',',
thousandsSeparator: ''
};
ctrl.$parsers.unshift(function (value) {
elem.priceFormat(format);
return elem[0].value;
});
ctrl.$formatters.unshift(function (value) {
elem[0].value = ctrl.$modelValue * 100 ;
elem.priceFormat(format);
return elem[0].value;
})
}
Demo 2 Fiddle
Push a $parser to the controller, and only update the value when it doesn't match the input using $setViewValue() and $render().
app.directive('currencyInput', function() {
return {
require: '?ngModel',
link: function($scope, element, attrs, controller) {
return ctrl.$parsers.push(function(inputValue) {
...
if (result != inputValue) {
controller.$setViewValue(res);
controller.$render();
}
});
}
};
});
Here's a fiddle with the logic I used for my currency input directive: Fiddle
Late to the party, but I believe this deserves another answer! I've been using the ng-currency module. It's absolutely fantastic.
I like Dubilla's approach cause of its simplicity and elegance. I decided to add (with due credits) some features on top of it to make it quite close to real world use case.
I've using it on a github project to create some useful finance directives github.
Notable extra features:
It does some rigorous checking of the inputs to give a valid response.
It has some keyboard shortcuts to make entering large numbers quicker.
I show how to integrate it with bootstrap and ngmodel css updates.
As a bonus I outputed the form's ngmonel as JSON to help people see how the form validation works in real time
It also uses a POJO as the ngmodel:
function Money() {
this.notional = 0;
this.maxValue = 99999999999.9;
this.maxValueString = "99,999,999,999.9";
this.maxPrecision = 10;
}
you can use it with Bootstrap 3 like so:
<h1>Currency Formatting directive</h1>
<div class="row">
<div class="col-md-6">
<form name="myForm">
<div class="form-group" ng-class="{'has-error': myForm.notional.$invalid && myForm.notional.$touched}">
<input type="text" ng-model="myForm.money.notional " money="money" money-input size="30" required
name="notional"
class="form-control"
placeholder="Enter notional amount"/>
<p class="help-block error" ng-show="myForm.notional.$error.required && myForm.notional.$touched">Required</p>
</div>
<button ng-disabled="myForm.$invalid" type="submit">SAVE</button>
</form>
<h2>Tips</h2>
<ol>
<li> Entering 'k' will multiply the amount by one thousand</li>
<li> Entering 'm' will multiply the amount by one million</li>
<li> Entering 'b' will multiply the amount by one billion</li>
</ol>
</div>
</div>
<p>Form debugger</p>
<pre>
form = {{ myForm | json }}
</pre>
Here's a way to handle this without jQuery using only an Angular directive. This example doesn't support decimals. It's easy to modify it to support that though, just change the $filter in the toView() function.
In my opinion this is a better approach to solve the same problem, as you can avoid loading in jQuery and the currency plugin mentioned by the author. Locale support for Euro should be supported by the use the $locale properties, but I've only tested this for use in USD.
(function() {
var app = angular.module('currencyMask', []);
// Controller
app.controller('ctrl', ['$scope', function($scope) {
$scope.amount = 100000;
}]);
// Directive
app.directive('inputCurrency', ['$locale', '$filter', function($locale, $filter) {
// For input validation
var isValid = function(val) {
return angular.isNumber(val) && !isNaN(val);
};
// Helper for creating RegExp's
var toRegExp = function(val) {
var escaped = val.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
return new RegExp(escaped, 'g');
};
// Saved to your $scope/model
var toModel = function(val) {
// Locale currency support
var decimal = toRegExp($locale.NUMBER_FORMATS.DECIMAL_SEP);
var group = toRegExp($locale.NUMBER_FORMATS.GROUP_SEP);
var currency = toRegExp($locale.NUMBER_FORMATS.CURRENCY_SYM);
// Strip currency related characters from string
val = val.replace(decimal, '').replace(group, '').replace(currency, '').trim();
return parseInt(val, 10);
};
// Displayed in the input to users
var toView = function(val) {
return $filter('currency')(val, '$', 0);
};
// Link to DOM
var link = function($scope, $element, $attrs, $ngModel) {
$ngModel.$formatters.push(toView);
$ngModel.$parsers.push(toModel);
$ngModel.$validators.currency = isValid;
$element.on('keyup', function() {
$ngModel.$viewValue = toView($ngModel.$modelValue);
$ngModel.$render();
});
};
return {
restrict: 'A',
require: 'ngModel',
link: link
};
}]);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
<div ng-app="currencyMask" ng-controller="ctrl">
<input input-currency ng-model="amount">
<p><strong>Amount:</strong> {{ amount }}</p>
</div>

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

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 = "";

Input autofocus attribute

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;
......
}

Resources