In our angular application we have a form with validation and a reset button. We based it off of the examples in the documentation: https://docs.angularjs.org/guide/forms
The trouble happens when someone tries to reset the form when a field is in an invalid state. My expectation is that upon reset, the field should be reset to an initial value and any validation will also be reset. However, I haven't figured out how to clear the validationl.
This can be demonstrated in the aforementioned example from the documentation: https://docs.angularjs.org/guide/forms Load the demo and type in an invalid email address (potatoes) and then press reset. It resets valid fields, but not invalid. We have tried using $setPristine() and fiddling with $valid hasn't led anywhere either. This seems like a straight forward usecase, but I have not been able to find any answers. Please someone point how the trivial solution we have overlooked!
Update: added code from https://docs.angularjs.org/guide/forms
<div ng-controller="ExampleController">
<form novalidate class="simple-form">
Name: <input type="text" ng-model="user.name" /><br />
E-mail: <input type="email" ng-model="user.email" /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<button ng-click="reset()">RESET</button>
<button ng-click="update(user)">SAVE</button>
</form>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
<script>
angular.module('formExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.master = {};
$scope.update = function(user) {
$scope.master = angular.copy(user);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.reset();
}]);
</script>
I had the same issue, and I found the solution here: https://github.com/angular/angular.js/issues/10027#issuecomment-62857430
The solution is to have all fields of the object set with empty value on the first place, and reset with the same kind of empty object instead of using {}.
Here a plunkr which show the solution:
https://plnkr.co/edit/qKAI4OlmCbu2HNM237Z3?p=preview
angular.module('myApp', [])
.controller('myCtrl', ['$scope', function ($scope) {
function getEmpty() {
return {
name: '',
contact: {email: ''}
};
}
$scope.manufacturer = getEmpty();
$scope.reset = function() {
$scope.manufacturer = getEmpty()
$scope.form.$setPristine();
$scope.form.$setUntouched();
$scope.form.$setValidity();
}
}]);
Related
I'm new to Angular, and I struggle with validating a multi select input with ngOptions attribute.
I want the field to be required, so the user must chose at least one option. However the validation methods Angular have simply doesn't work in my case, Here's what I've tried:
<form name="products" novalidate>
<div class="form-group">
<label for="select-product">Chose product/s</label>
<select id="select-product" name="selectedProduct" class="form-control"
required
multiple
ng-model="selectedProduct"
ng-options="product.name for product in products">
</select>
</div>
<button class="btn btn-primary pull-left" ng-click="products.selectedProduct.$valid && goToChildrenId()">Next</button>
</form>
Even if I chose a product and I can see that the form class switch from .ng-invalid to .ng-valid, the function goToChildrenId() doesn't run.
Also, if I add {{products.selectedProduct.$valid}} at the bottom, when I refresh the page I can see "false" for a second but it disappear, why?
If it's relevent, this is the controller:
adminCheckout.controller('productsCtrl', function($scope, $http, wpMiniapps, $location, $rootScope){
$scope.products = [];
var url = wpMiniapps.routeUrl("getProducts");
$http.post(url, {}).then(function(res){
$scope.products = res.data.data;
}, function(err){
console.log(err);
});
$scope.$watch('selectedProduct', function (newVal) {
$rootScope.globalData.products = newVal;
});
$scope.goToChildrenId = function(){
$location.path('/select-children');
};
});
I searched the web but nothing seems to work in my case.
I will really appreciate any help.
You have a variable name collision which is causing the problem: the form is named products and this ends up as a $scope variable. But it collides with $scope.products = [] from the controller. Simply renaming the form to, e.g., productsForm solves the problem:
<form name="productsForm" novalidate>
...
<button class="btn btn-primary pull-left" ng-click="productsForm.selectedProduct.$valid && goToChildrenId()">Next</button>
</form>
Here is my JS code.
angular.module('myApp', [])
.controller("myController", [function () {
this.master = [];
this.addUser = function(user) {
this.master.push(user)
};
this.removeUser = function(user) {
var indexToRemove = this.master.indexOf(user);
this.master.splice(indexToRemove,1)
}
this.reset = function() {
this.user = this.master[this.master.length - 1];
}
}])
Here is my HTML part.
<body ng-app="myApp" ng-controller="myController as Cntrl">
<form>
Name: <input type="text" ng-model="user.name" /> <br />
Email: <input type="email" ng-model="user.email" /> <br />
Gender: <input type="radio" ng-model="user.gender" value="male" /> Male
<input type="radio" ng-model="user.gender" value="female" /> female <br /> <br />
<input type="button" ng-click="Cntrl.addUser(user)" value="Add User">
<input type="button" ng-click="Cntrl.reset()" value="Reset User">
</form>
<div ng-repeat="users in Cntrl.master track by $index">
<span ng-click="Cntrl.removeUser(users)" >X</span> <pre>{{users | json}}</pre>
</div>
</body>
I am able to add new users and remove the selected users. But whenever I add any new users, the properties of all the old users in the array are getting updated with the properties of the newly added user.
Please explain the mistake I am making here.
all users pushed into array are the same one, and remove better use index,
ng-click="Cntrl.removeUser($index)"
angular.module('myApp', [])
.controller("myController", [
function() {
this.master = [];
this.user = this;
//use this.user
this.addUser = function() {
this.master.push(this.user)
this.user = {}
};
//use index
this.removeUser = function(index) {
this.master.splice(indexToRemove, 1)
}
this.reset = function() {
this.user = this.master[this.master.length - 1];
}
}
])
you are giving user reference to master array , and because of two way data binding your values are updated , can u try this, will work.
just modify your addUser method as below.
this.addUser = function(user) {
this.master.push(angular.copy(user));
};
angular copy will create deep copy of object , see docs https://docs.angularjs.org/api/ng/function/angular.copy
As there has already been 3 posts that answers your question. But I
guess there are some serious logical misunderstanding on the VM
approach your are trying to implement in your code. Though the program
is now working but I highly recommend you to go through the John
Papa's tutorial before proceeding with your solution. This would clear
your fundamentals and make you understand why it wasn't working
before.
https://johnpapa.net/angularjss-controller-as-and-the-vm-variable/
I am new to Angular.
Implementing localization in the project. I've got many inputs and I must translate placeholders. In HTML I have something like this
<input type="email" placeholder="{{ 'TRANSLATION_KEY' | translate }}" onfocus="this.placeholder=''" onblur="this.placeholder='{{ 'TRANSLATION_KEY' | translate }}'" required>
But this part of the code doesn't work:(
onblur="this.placeholder='{{ 'TRANSLATION_KEY' | translate }}'"
How to set translated value to placeholder onblur?
I'll appreciate any help!
I have another solution. More universal and easy. Just add this line to your input in the view
onfocus="this.ph=this.placeholder;this.placeholder=''" onblur="this.placeholder=this.ph"
This is a good approach to the problem JSFiddle:
HTML:
<div ng-app="myApp" ng-controller="myCtrl">
<input type="email" placeholder="{{placeholder}}" ng-focus="setPlaceholder()" ng-blur="setPlaceholder('TRANSLATION_KEY')" required>
</div>
JS:
angular.module('myApp', [])
.controller('myCtrl', function ($scope, $filter) {
$scope.placeholder = $filter('translate')('TRANSLATION_KEY');
$scope.setPlaceholder = function (data) {
$scope.placeholder = $filter('translate')(data);
};
})
.filter('translate', function () {
return function (data) {
return data;
};
});
Having a super strange problem with my current build.(Angular Version 1.2.16)
Controller:
app.controller('recoverController', function(
$scope,
$rootScope,
$http,
$window,
$cookies,
$state
){
$scope.recoverAddress = 'hello';
});
View:
Works:
<input type="text" ng-model="recoverAddress" />
Fails:
<input type="email" ng-model="recoverAddress" />
AngularJS will bind to a model only when a value in the input field passes validation. As soon as you supply a valid e-mail adress model is bound, watchers are updated etc. This is how angular works
just input valid email here
<div ng-app ng-controller="testCtrl">
<input ng-model="emailInput" type="email">
<div>{{emailInput}}</div>
</div>
function testCtrl($scope) {
$scope.$watch('emailInput', function(newValue){
console.log(newValue);
});
}
I'm trying to generate a set of check-boxes from an object array. I'm aiming to have the check-boxes dynamically map their ng-model to a property of the new object that will be submitted into the array.
What I had in mind is something like
<li ng-repeat="item in items">
<label>{{item.name}}</label>
<input type="checkbox" ng-model="newObject.{{item.name}}">
</li>
This doesn't work as can be seen on this JSFiddle:
http://jsfiddle.net/GreenGeorge/NKjXB/2/
Can anybody help?
This should give you desired results:
<input type="checkbox" ng-model="newObject[item.name]">
Here is a working plunk: http://plnkr.co/edit/ALHQtkjiUDzZVtTfLIOR?p=preview
EDIT
As correctly noted in the comments using this with ng-change requires a "dummy" ng-model to be present beforehand. It should however be noted that apparently with 1.3 the required options have been provided by the framework. Please check out https://stackoverflow.com/a/28365515/3497830 below!
/EDIT
Just in case you are like me stumbling over a simple case while having a more complex task, this is the solution I came up with for dynamically binding arbitrary expressions to ng-model: http://plnkr.co/edit/ccdJTm0zBnqjntEQfAfx?p=preview
Method: I created a directive dynamicModel that takes a standard angular expression, evaluates it and links the result to the scope via ng-model and $compile.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.data = {};
$scope.testvalue = 'data.foo';
$scope.eval = $scope.$eval;
});
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.data = {};
$scope.testvalue = 'data.foo';
$scope.eval = $scope.$eval;
});
app.directive('dynamicModel', ['$compile', function ($compile) {
return {
'link': function(scope, element, attrs) {
scope.$watch(attrs.dynamicModel, function(dynamicModel) {
if (attrs.ngModel == dynamicModel || !dynamicModel) return;
element.attr('ng-model', dynamicModel);
if (dynamicModel == '') {
element.removeAttr('ng-model');
}
// Unbind all previous event handlers, this is
// necessary to remove previously linked models.
element.unbind();
$compile(element)(scope);
});
}
};
}]);
Usage is simply dynamic-model="angularExpression" where angularExpression results in a string that is used as the expression for ng-model.
I hope this saves someone the headache of having to come up with this solution.
Regards,
Justus
With Angular 1.3, you can use ng-model-options directive to dynamically assign the model, or bind to an expression.
Here is a plunkr: http://plnkr.co/edit/65EBiySUc1iWCWG6Ov98?p=preview
<input type="text" ng-model="name"><br>
<input type="text" ng-model="user.name"
ng-model-options="{ getterSetter: true }">
More info on ngModelOptions here: https://docs.angularjs.org/api/ng/directive/ngModelOptions
This is my approach to support deeper expression, e.g. 'model.level1.level2.value'
<input class="form-control" ng-model="Utility.safePath(model, item.modelPath).value">
where item.modelPath = 'level1.level2' and
Utility(model, 'level1.level2') is the utility function that returns model.level1.level2
<!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="myCtrl">
<form name="priceForm" ng-submit="submitPriceForm()">
<div ng-repeat="x in [].constructor(9) track by $index">
<label>
Person {{$index+1}} <span class="warning-text">*</span>
</label>
<input type="number" class="form-control" name="person{{$index+1}}" ng-model="price['person'+($index+1)]" />
</div>
<button>Save</button>
</form>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function ($scope) {
$scope.price = [];
$scope.submitPriceForm = function () {
//objects be like $scope.price=[{person1:value},{person2:value}....]
console.log($scope.price);
}
});
</script>
</body>
</html>