I am attempting to pass a file into a function, where the file is originated from an input type of file. My code looks something like this.
<input type="file" id="scvFileUploadId"/>
<button id="uploadFilesubmit" ng-click"ctrl.uploadFile()">Upload</button>
In short, I would like to take the file that has been added to the input and pass it into the parameter of the function of the ng-click attribute that is fired when the button is clicked. I think this may be simple, but I cannot wrap my head around it at the moment.
I'm trying to do something like this.
<input type="file" id="scvFileUploadId"/>
<button id="uploadFilesubmit" ng-click"ctrl.uploadFile(#scvFileUploadId.file)">Upload</button>
My solution was to use Angular's element selector to point to the input element holding the file, and then pass the result of that as the parameter into the function, like so:
<input type="file" id="scvFileUploadId" />
<button id="uploadFilesubmit" onclick="angular.element(this).controller().uploadFile(angular.element(document.querySelector('#csvFileUploadID'))[0].files[0])">Upload</button>
I'm not sure if this solution is the most 'Angular' way of doing it, but there were existing workarounds for some lack of support with Angular input file uploading being done on this part of the application that forced me to interact with DOM, so this does suffice in my scenario.
I highly recommend using existing directives created by the community;
https://github.com/nervgh/angular-file-upload
https://github.com/danialfarid/ng-file-upload
But if you are restricted and want to avoid 3rd parties you can see this implementation:
ng-model for <input type="file"/>
<input type="file" fileread="vm.uploadme" />
<button id="uploadFilesubmit" ng-click"ctrl.uploadFile(vm.uploadme)">Upload</button>
You can use a simple directive with NgModelController to add ngModel support for the input type='file' like:
angular.module('myApp', [])
.controller('TestUploadController', ['$scope', function ($scope) {
var ctrl = this;
ctrl.imageFile = null;
ctrl.clearFile = clearFile;
ctrl.uploadFile = uploadFile;
function clearFile() {
ctrl.imageFile = null;
}
function uploadFile(file) {
if (file) {
console.log('Upload: ', file);
}
}
}])
.directive('fileUpload', [function () {
return {
require: "ngModel",
restrict: 'A',
link: function ($scope, el, attrs, ngModel) {
function onChange (event) {
//update bindings with $applyAsync
$scope.$applyAsync(function(){
ngModel.$setViewValue(event.target.files[0]);
});
}
//change event handler
el.on('change', onChange);
//set up a $watch for the ngModel.$viewValue
$scope.$watch(function () {
return ngModel.$viewValue;
}, function (value) {
//clear input value if model was cleared
if (!value) {
el.val("");
}
});
//remove change event handler on $destroy
$scope.$on('$destroy', function(){
el.off('change', onChange);
});
}
};
}]);
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.angularjs.org/1.6.2/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="TestUploadController as $ctrl">
<input type="file" file-upload ng-model="$ctrl.imageFile" />
<input type="button" ng-click="$ctrl.uploadFile($ctrl.imageFile)" value="Upload" />
<input type="button" ng-click="$ctrl.clearFile()" value="Reset" />
<div ng-if="$ctrl.imageFile">
{{$ctrl.imageFile.name}}<br />
{{$ctrl.imageFile.size}} byte(s)<br/>
{{$ctrl.imageFile.type}}
</div>
</div>
</div>
I have an html form that I am validating with an Angular controller. If the validation fails, I apply certain classes to the html. If it passes, I want to let the form submit itself. Although this seems very straightforward, I haven't found an easy way to do this. One method I have found uses the $scope.$broadcast function to tell the form to submit, however, I am using the Controller as Ctrl syntax so I would rather not use $scope. Is there anyway to submit a form from a controller?
My simplified HTML
<form ng-controller="LoginCtrl as login" ng-submit="login.validate()" method="post">
<input ng-model="login.username" />
<input ng-model="login.password" />
</form>
JS
var app = angular.module("app", []);
app.controller("LoginCtrl", ["$http", function($http) {
this.username = "";
this.password = "";
this.validate = function() {
//validate
if (valid) {
// somehow form.submit()
}
};
}]);
I am somewhat new to Angular so forgive me if this is an obvious quesion ;)
EDIT:
I need to clarify that I am looking to avoid submitting the form with AJAX (i.e. $http.post). Basically what I want is the controller equivalent of calling form.submit().
USE CASE:
Let me explain exactly what I am trying to do.
User arrives at login page
User enters credentials
User hits Submit
Controller asks server (using api path) if the credentials are valid
if valid then
Tell the form to submit to regular login path // how?
else
Immediately tell the user they submitted invalid credentials
This way the User gets immediate feedback if they entered incorrect credentials.
All of this I have implemented except for the actual form submission.
Simplest approach would be wrap all the form element data into one object. You don't have to create this object if you have no data to prepopulate, ng-model will create it for you.
<form ng-controller="LoginCtrl as login" ng-submit="login.validate()" method="post">
<input ng-model="login.MyFormData.username" />
<input ng-model="login.MyFormData.password" />
</form>
This will result in an object in your controller scope looking like:
$scope.MyFormData={
username :'foo',
password:'bar'
}
When ready to submit:
$http.post('path/to/server', $scope.myFormData).success(response){
/* do something with response */
})
I have an example with the bare minimum code here. Note, it is self validating, and you don't even need to submit anything from the COntroller! you can include the action and method fields as form attributes, and angular will submit the form if it is valid
HTML
<form name="LoginCtrl as loginForm" method="Post" action="not-a-real-script.php">
<input type="text" name="name" ng-model="loginData.name" placeholder="username" required="" />
<input type="password" name="password" ng-model="loginData.password" placeholder="Password" required />
<input type="submit" ng-disabled="loginForm.$invalid" value="Login" />
</form>
JS
angular.module('app', [])
.controller('LoginCtrl', function($scope) {
$scope.loginData = {};
});
I think you want to have validation as part of your form submission flow. Try something like this:
HTML
<form ng-controller="LoginCtrl as login" ng-submit="login.submit()" method="post">
<input ng-model="auth.username" />
<input ng-model="auth.password" />
<div class="error" ng-hide="valid">Something is invalid...</div>
</form>
JS
var app = angular.module("app", []);
app.controller("LoginCtrl", ["$http", "$scope", function($http, $scope) {
$scope.valid = true;
$scope.auth.username = "";
$scope.auth.password = "";
var valid = function() {
// validate
return valid; // true or false
};
this.submit = function() {
if (valid()) {
$http.post('/your/auth/url', { auth: auth }).success(function(response) {
// whatever you're doing to store the auth response.
});
} else {
// use this to conditionally show error messages with ng-show
$scope.valid = false;
}
};
}]);
I'm not sure I understand your comment about using the controller-as syntax. That shouldn't change how you use $scope.
I have a simple login form which works just peachy unless you use Chrome's auto complete feature.
If you start typing and use the auto complete feature and it auto populates your password, my angularjs model does not have any value for the password.
I tried to turn autocomplete off by setting the attribute on the form autocomplete="off" but that doesn't seem to have any effect.
How can I either:
1. Ensure that I can get the value if someone uses Chrome's auto-complete feature?
2. Disable Chrome's auto-complete feature?
<form class="form-signin" name="form" ng-submit="login()" autocomplete="off">
<h3>Login</h3>
<input type="email" name="email" class="form-control" placeholder="Email address" ng-model="user.email" required autofocus>
<input type="password" name="password" class="form-control" placeholder="Password" ng-model="user.password" required>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
From the link added in the comment:Github Issue's
// Due to browsers issue, it's impossible to detect without a timeout any changes of autofilled inputs
// https://github.com/angular/angular.js/issues/1460
// https://github.com/angular/angular.js/issues/1460#issuecomment-28662156
// Could break future Angular releases (if use `compile()` instead of `link())
// TODO support select
angular.module("app").config(["$provide", function($provide) {
var inputDecoration = ["$delegate", "inputsWatcher", function($delegate, inputsWatcher) {
var directive = $delegate[0];
var link = directive.link;
function linkDecoration(scope, element, attrs, ngModel){
var handler;
// By default model.$viewValue is equals to undefined
if(attrs.type == "checkbox"){
inputsWatcher.registerInput(handler = function(){
var value = element[0].checked;
// By default element is not checked
if (value && ngModel.$viewValue !== value) {
ngModel.$setViewValue(value);
}
});
}else if(attrs.type == "radio"){
inputsWatcher.registerInput(handler = function(){
var value = attrs.value;
// By default element is not checked
if (element[0].checked && ngModel.$viewValue !== value) {
ngModel.$setViewValue(value);
}
});
}else{
inputsWatcher.registerInput(handler = function(){
var value = element.val();
// By default value is an empty string
if ((ngModel.$viewValue !== undefined || value !== "") && ngModel.$viewValue !== value) {
ngModel.$setViewValue(value);
}
});
}
scope.$on("$destroy", function(){
inputsWatcher.unregisterInput(handler);
});
// Exec original `link()`
link.apply(this, [].slice.call(arguments, 0));
}
// Decorate `link()` don't work for `inputDirective` (why?)
/*
directive.link = linkDecoration;
*/
// So use `compile()` instead
directive.compile = function compile(element, attrs, transclude){
return linkDecoration;
};
delete directive.link;
return $delegate;
}];
$provide.decorator("inputDirective", inputDecoration);
$provide.decorator("textareaDirective", inputDecoration);
//TODO decorate selectDirective (see binding "change" for `Single()` and `Multiple()`)
}]).factory("inputsWatcher", ["$interval", "$rootScope", function($interval, $rootScope){
var INTERVAL_MS = 500;
var promise;
var handlers = [];
function execHandlers(){
for(var i = 0, l = handlers.length; i < l; i++){
handlers[i]();
}
}
return {
registerInput: function registerInput(handler){
if(handlers.push(handler) == 1){
promise = $interval(execHandlers, INTERVAL_MS);
}
},
unregisterInput: function unregisterInput(handler){
handlers.splice(handlers.indexOf(handler), 1);
if(handlers.length == 0){
$interval.cancel(promise);
}
}
}
}]);
From: Developer.mozilla.org docs Turning_off_form_autocompletion
If an author would like to prevent the auto-filling of password fields
in user management pages where a user can specify a new password for
someone other than themselves, autocomplete="new-password" should be
specified, though support for this has not been implemented in all
browsers yet.
So, what makes it work for me:
set autocomplete="new-password" on the password field
set autocomplete="off" in the username field.
I hope that it works for you too :)
As said here, https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form
The Google Chrome UI for auto-complete requests varies, depending on
whether autocomplete is set to off on input elements as well as their
form. Specifically, when a form has autocomplete set to off and its
input element's autocomplete field is not set, then if the user asks
for autofill suggestions for the input element, Chrome might display a
message saying "autocomplete has been disabled for this form." On the
other hand, if both the form and the input element have autocomplete
set to off, the browser will not display that message. For this
reason, you should set autocomplete to off for each input that has
custom auto-completion.
You need to set autocomplete="off" on both form and input
I don't think this is related to AngularJS
I had the same issue and found a very simple solution that just uses jQuery to grab the value on submit. In my controller I have the following:
$scope.username = "";
$scope.password = "";
$scope.login = function(){
$scope.username = $("#username").val();
$scope.password = $("#password").val();
// Proceed as normal
};
There are some downsides, if you need to do validation etc but otherwise it's fine for smaller forms like this.
You could watch the email field value and everytime the value in that field is changing, you could trigger a "change"-event on the password field. This events trigger all the ng-model magic on that field and updates the model.
module.directive("autocompleteFor", function () {
return {
restrict: "A",
link: function ($scope, $element, $attrs) {
$scope.$watch($attrs.autocompleteFor, function () {
$element.triggerHandler("change");
})
}
}
});
With this directive you could handle that scenario like this:
<input type="email" name="email" ng-model="user.email">
<input type="password" autocomplete-for="user.email" name="password" ng-model="user.password" required>
-----------------------------
To disable the autocomplete/autofill from a input, just type:
- autocomplete="false" instead of autocomplete="off"!
Below directive worked for me. It's simple and clean fix. Hope that helps!
Ref: AngularJS browser autofill workaround by using a directive
Here is a solution that is far less hacky than other solutions presented and is semantically sound AngularJS: VictorBlog.com
myApp.directive('formAutofillFix', function() {
return function(scope, elem, attrs) {
// Fixes Chrome bug: https://groups.google.com/forum/#!topic/angular/6NlucSskQjY
elem.prop('method', 'POST');
// Fix autofill issues where Angular doesn't know about auto-filled inputs
if(attrs.ngSubmit) {
setTimeout(function() {
elem.unbind('submit').submit(function(e) {
e.preventDefault();
elem.find('input, textarea, select').trigger('input').trigger('change').trigger('keydown');
scope.$apply(attrs.ngSubmit);
});
}, 0);
}
};
});
Then you simply attach the directive to your form:
<form ng-submit="submitLoginForm()" form-autofill-fix>
<div>
<input type="email" ng-model="email" ng-required />
<input type="password" ng-model="password" ng-required />
<button type="submit">Log In</button>
</div>
</form>
alternative solution is just to get rid off form element and use ng-form instead, it disables all browser interferings
<div ng-form="yourFormName" class="form-signin" ng-submit="login()">
Old question, but whatever
I came across the same problem and I've got a small "hack" solution. This problem happened at many different places in my app, so I created a directive for reusability.
module.directive("fakeAutocomplete", [
function () {
return {
restrict: "EA",
replace: true,
template: "<div><input/><input type=\"password\"/></div>",
link: function (scope, elem, attrs) {
elem.css({
"overflow": "hidden",
"width": "0px",
"height": "0px"
});
}
}
}
]);
And simply add
<fake-autocomplete></fake-autocomplete>
At the beginning of your form and the browser will detect the fake fields as the ones that should autocomplete. Simply putting display:none on the fields also does not seem to work anymore, I've tested it.
In my case, i set property autocomplete="off" in form and input.
<form autocomplete="off">
<input type="text" autocomplete="off">
</form>
It could be much simpler solution to the problem.
Angularjs couldn't "see" the value
Take the value via DOM (jQuery) then put it back into Angularjs.
```
angular.module('someModule').directive('chromeAutofillHack', function()
{
return {
require: '^ngModel',
restrict: 'A',
priority: 500, // set higher priority then other custom directives
link: function(scope, element, attrs , ngModelCtrl)
{
ngModelCtrl.$parsers.unshift(function(email)
{
if (!email) { // only do this when angular think there is no value
email = $(element).val();
ngModel.$setViewValue(email);
}
return email;
});
}
};
});
```
--- NO LONGER RELEVANT ---
I was able to disable autocomplete (weirdly enough) by adding the following.
<form ... novalidate>
<input ... formnovalidate />
Reference this Plunker
My solution for Chrome 35.0, Firefox 30.0, angular 1.2.18 (login page with password manager, autofill, angular method and redirect):
How does browser know when to prompt user to save password?
I ended up with a different solution that I don't see here yet. From what I found, the password value isn't exposed to the model (or possibly even the js api) until the user interacts with the page. Clicking the login button is enough interaction to make the value available, and the data binding will succeed early enough for the click handler on the button to access the password from the model. So if I could detect that the browser has auto-filled, I could enable the login button even though my model hadn't been updated yet. So I wrote a simple helper service to see if Chrome has auto-filled any inputs:
utilApp.service('autoFillDetectionService', [function () {
return {
hasAutoFillInputs: function () {
try{
return !!$(':-webkit-autofill').length;
}
catch (x) {
// IE gets here, it/jquery complains about an invalid pseudo-class
return false;
}
}
};
}]);
From the login controller, I have an interval checking if any input fields are marked as autofill and if so enable the login button.
Just Replace autocomplete="off" with autocomplete="new-password".
I have a reset function in angular to clear all the fields in a form. If I do something like:
reset
$scope.resetForm = function() {
$scope.someForm = {};
}
Everything works fine. But I want to use this function for multiple forms on the site. If I pass the form object in like:
reset
$scope.resetForm = function(form) {
$scope.form = {};
}
Then it won't work. Can someone explain to me why this would be happening?
You have 2 problems:
You're not accessing the passed in variable, still access the someForm of current scope.
When you pass parameter to the function, it's passed by reference. Even when you use form = {}, it does not work because it only changes the reference of the parameter, not the reference of the passed in someForm.
Try:
$scope.resetForm = function(form) {
//Even when you use form = {} it does not work
form.fieldA = null;
form.fieldB = null;
///more fields
}
Or
$scope.resetForm = function(form) {
//Even when you use form = {} it does not work
angular.copy({},form);
}
instead of:
$scope.resetForm = function(form) {
$scope.form = {};
}
In your plunk, I see that you're not separating the view from the model. You should do it for separation of concerns and to avoid problems that might happen when you clear all the fields (including DOM form object's fields).
<form name="form2" ng-controller="SecondController">
<label for="first_field">First Field</label>
<input ng-model="form2Model.first_field" />
<br />
<label for="second_field">Second Field</label>
<input ng-model="form2Model.second_field" />
<br />
Reset the form
</form>
http://plnkr.co/edit/x4JAeXra1bP4cQjIBld0?p=preview
You can also do:
form.fieldA = undefined;
It works great for radio buttons and checkboxes.
you can try this :
Deploy your function inside form button reset , in this way ...
<input type ="button" ng-click="Object.field1 = null; ObjectN.fieldN = null;" value="Reset" />
I'm trying to create a simple form that, upon hitting the 'enter' key, the entered data will be sent and appended to a list in my controller. The code I have is as follows:
controller.js
.controller('GenericTodosCtrl', function($scope) {
$scope.todos=[];
$scope.newTodo="";
function addTodo() {
alert("hello");
$scope.todos.push({
content: $scope.newTodo,
done: false,
editing: false
});
$scope.newTodo = "";
};
});
main.html
<form ng:submit="addTodo()">
<input name="newTodo" placeholder="Placeholder" type="text">
</form>
Currently, when I type in values and hit 'enter' nothing happens (not even the alert). I can't seem to figure out if I should be using "this" instead of $scope for things in controller.js. Does ng:submit require something in the ? Or is this an issue with my javascript instead?
You should make addTodo part of your $scope.
$scope.addTodo = function() {
alert("hello");
$scope.todos.push({
content: $scope.newTodo,
done: false,
editing: false
});
$scope.newTodo = "";
};
Also, make sure the value in the input field is actually databound to your $scope value by using ngModel:
<form ng:submit="addTodo()">
<input ng:model="newTodo" placeholder="Placeholder" type="text">
</form>