I generating dynamic drop-down based on my model and trying to set it's selected item same as data from model. I am doing all this in *ngFor loop. But first time after the page has loaded it is binding correct values to drop-down but once I click inside window it changes values in all drop-downs to same value and throws below error :
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed
after it was checked. Previous value: 'model:
d9cddd06-911a-4fea-9b87-f045362ede52'. Current value: 'model:
bd39b9a1-42f2-4db1-9639-953206062l67'
I have tried using Change Detector in ngOnInit,ngAfterViewInit and even in subscribe method where I am getting the model.
I have also tried using ngModelChange but no luck.
I am getting error on third line in below code:
[(ngModel)]="data.reportTypeTemplate[i].reportTemplateId"
I am not sure now how to fix this issue.
Has anyone faced this issue or know the solution for this.
Let me know if more information is required.
Below is my Code :
<div class="form-group" *ngFor = "let type of masterRTTList; let i = index">
<label for="reportType">{{type.reportTypeName}}*</label>
<select [(ngModel)]="data.reportTypeTemplate[i].reportTemplateId" //Here
*ngIf="data.reportTypeTemplate.length > 0"
class="form-control rounded-0" formControlName="reportType">
<option value = "">Select Template</option>
<option *ngFor="let template of type.reportTemplateName" [ngValue]="template.id">
{{template.name}}
</option>
</select>
</div>
ngOnInit(){
this.apiService.get(environment.api_url, 'GetReports/')
.subscribe(templates => {
this.masterRTTList = templates
});
}
I'm using Material UI for my react app and am trying to get the value of a select form.
This works perfectly with the following code:
<select name="role" value={props.role} onChange={props.handleInputChange}>
<option value="client">Client</option>
<option value="worker">Worker</option>
</select>
However, if I try to implement the exact same thing using Material UI
<FormControl margin="dense" className={classes.textFieldSelect}>
<InputLabel htmlFor="role" >Role</InputLabel>
<Select
value={props.role}
onChange={props.handleInputChange}
input={<Input id="role" />}
>
<MenuItem value="worker">Worker</MenuItem>
<MenuItem value="client">Client</MenuItem>
</Select>
</FormControl>
Here's the code for handInput change in my parent component:
handleInputChange(e) {
e.preventDefault();
const target = e.target;
const value = target.value;
const name = target.name;
this.setState({ currentUser: { ...this.state.currentUser, [name]: value } });
}
The value of the select bar doesn't change when I click the menu items. However, the values do change in the TextField if I select a different role in the plain HTML element.
Any clues on what I am doing wrong?
I just spent some time debugging it and it turns out that the reason it didn't work is because the e.target.name property is undefined when I click the . I can fix this by passing in a specific name parameter when I call handleInputChange (e.g. handleInputChange('role')). Thanks for looking at it though!
I'm trying to build searchable select element in React. What is really important to me is:
Avoide jQuery
Posibility to set one of options as default selected
I have list of options, which now looks like that:
<select
onChange={props.onChange}
id={props.id}
className={props.style || "form-control"}>
{!props.defaultOption ?
<option value="" selected>Select</option> :
<option value={props.defaultOptionValue} disabled selected>{props.defaultOption}</option>}
{props.options.map((item, i) => {
if (item.id === props.defaultOptionValue) return false;
return <option key={i} value={item.id}>{item.name}</option>
})}
</select>
Nothing really complicated. I pass onChange handler, id and styles. If i provide defaultOptionValue and defaultOption in props, then it means i need to check this one as selected.
Datalist works great for me, but it doesn't support safari and i want to display label inside my input, not a value, as value is id and label name of the object.
I have already checked react-select, but it's missing option to set default value.
I have an Angular form. The fields are validated using the ng-pattern attribute. I also have a reset button. I'm using the Ui.Utils Event Binder to handle the reset event like so:
<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
<div>
<label>
Area Code
<input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern">The area code must be three digits</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern">The phone number must be seven digits</div>
</div>
</div>
<br>
<div>
<button type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
As you can see, when the form is reset it calls the reset method on the $scope. Here's what the entire controller looks like:
angular.module('app').controller('mainController', function($scope) {
$scope.resetCount = 0;
$scope.reset = function(form) {
form.$setPristine();
form.$setUntouched();
$scope.resetCount++;
};
$scope.search = function() {
alert('Searching');
};
});
I'm calling form.$setPristine() and form.$setUntouched, following the advice from another question here on Stack Overflow. The only reason I added the counter was to prove that the code is being called (which it is).
The problem is that even after reseting the form, the validation messages don't go away. You can see the full code on Plunker. Here's a screenshot showing that the errors don't go away:
I started with the comment from #Brett and built upon it. I actually have multiple forms and each form has many fields (more than just the two shown). So I wanted a general solution.
I noticed that the Angular form object has a property for each control (input, select, textarea, etc) as well as some other Angular properties. Each of the Angular properties, though, begins with a dollar sign ($). So I ended up doing this (including the comment for the benefit of other programmers):
$scope.reset = function(form) {
// Each control (input, select, textarea, etc) gets added as a property of the form.
// The form has other built-in properties as well. However it's easy to filter those out,
// because the Angular team has chosen to prefix each one with a dollar sign.
// So, we just avoid those properties that begin with a dollar sign.
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
// Set each control back to undefined. This is the only way to clear validation messages.
// Calling `form.$setPristine()` won't do it (even though you wish it would).
for (let name of controlNames) {
let control = form[name];
control.$setViewValue(undefined);
}
form.$setPristine();
form.$setUntouched();
};
$scope.search = {areaCode: xxxx, phoneNumber: yyyy}
Structure all models in your form in one place like above, so you can clear it like this:
$scope.search = angular.copy({});
After that you can just call this for reset the validation:
$scope.search_form.$setPristine();
$scope.search_form.$setUntouched();
$scope.search_form.$rollbackViewValue();
There doesn't seem to be an easy way to reset the $errors in angular. The best way would probably be to reload the current page to start with a new form. Alternatively you have to remove all $error manually with this script:
form.$setPristine(true);
form.$setUntouched(true);
// iterate over all from properties
angular.forEach(form, function(ctrl, name) {
// ignore angular fields and functions
if (name.indexOf('$') != 0) {
// iterate over all $errors for each field
angular.forEach(ctrl.$error, function(value, name) {
// reset validity
ctrl.$setValidity(name, null);
});
}
});
$scope.resetCount++;
You can add a validation flag and show or hide errors according to its value with ng-if or ng-show in your HTML. The form has a $valid flag you can send to your controller.
ng-if will remove or recreate the element to the DOM, while ng-show will add it but won't show it (depending on the flag value).
EDIT: As pointed by Michael, if form is disabled, the way I pointed won't work because the form is never submitted. Updated the code accordingly.
HTML
<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
<div>
<label>
Area Code
<input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern" ng-if="searchForm.areaCode.$dirty">The area code must be three digits</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern" ng-if="searchForm.phoneNumber.$dirty">The phone number must be seven digits</div>
</div>
</div>
<br>
<div>
<button type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
JS
$scope.search = function() {
alert('Searching');
};
$scope.reset = function(form) {
form.$setPristine();
form.$setUntouched();
$scope.resetCount++;
};
Codepen with working solution: http://codepen.io/anon/pen/zGPZoB
It looks like I got to do the right behavior at reset. Unfortunately, using the standard reset failed. I also do not include the library ui-event. So my code is a little different from yours, but it does what you need.
<form name="searchForm" id="searchForm" ng-submit="search()">
pristine = {{searchForm.$pristine}} valid ={{searchForm.$valid}}
<div>
<label>
Area Code
<input type="tel" required name="areaCode" ng-model="obj.areaCode" ng-pattern="/^([0-9]{3})?$/" ng-model-options="{ allowInvalid: true }">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern">The area code must be three digits</div>
<div class="error" ng-message="required">The area code is required</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" required name="phoneNumber" ng-model="obj.phoneNumber" ng-pattern="/^([0-9]{7})?$/" ng-model-options="{ allowInvalid: true }">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern">The phone number must be seven digits</div>
<div class="error" ng-message="required">The phone number is required</div>
</div>
</div>
<br>
<div>
<button ng-click="reset(searchForm)" type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
And JS:
$scope.resetCount = 0;
$scope.obj = {};
$scope.reset = function(form_) {
$scope.resetCount++;
$scope.obj = {};
form_.$setPristine();
form_.$setUntouched();
console.log($scope.resetCount);
};
$scope.search = function() {
alert('Searching');
};
Live example on jsfiddle.
Note the directive ng-model-options="{allowinvalid: true}". Use it necessarily, or until the entry field will not be valid, the model value is not recorded. Therefore, the reset will not operate.
P.S. Put value (areaCode, phoneNumber) on the object simplifies purification.
Following worked for me
let form = this.$scope.myForm;
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
for (let name of controlNames) {
let control = form [name];
control.$error = {};
}
In Short: to get rid of ng-messages errors you need to clear out the $error object for each form item.
further to #battmanz 's answer, but without using any ES6 syntax to support older browsers.
$scope.resetForm = function (form) {
try {
var controlNames = Object.keys(form).filter(function (key) { return key.indexOf('$') !== 0 });
console.log(controlNames);
for (var x = 0; x < controlNames.length; x++) {
form[controlNames[x]].$setViewValue(undefined);
}
form.$setPristine();
form.$setUntouched();
} catch (e) {
console.log('Error in Reset');
console.log(e);
}
};
I had the same problem and tried to do battmanz solution (accepted answer).
I'm pretty sure his answer is really good, but however for me it wasn't working.
I am using ng-model to bind data, and angular material library for the inputs and ng-message directives for error message , so maybe what I will say will be useful only for people using the same configuration.
I took a lot of look at the formController object in javascript, in fact there is a lot of $ angular function as battmanz noted, and there is in addition, your fields names, which are object with some functions in its fields.
So what is clearing your form ?
Usually I see a form as a json object, and all the fields are binded to a key of this json object.
//lets call here this json vm.form
vm.form = {};
//you should have something as ng-model = "vm.form.name" in your view
So at first to clear the form I just did callback of submiting form :
vm.form = {};
And as explained in this question, ng-messages won't disappear with that, that's really bad.
When I used battmanz solution as he wrote it, the messages didn't appear anymore, but the fields were not empty anymore after submiting, even if I wrote
vm.form = {};
And I found out it was normal, because using his solution actually remove the model binding from the form, because it sets all the fields to undefined.
So the text was still in the view because somehow there wan't any binding anymore and it decided to stay in the HTML.
So what did I do ?
Actually I just clear the field (setting the binding to {}), and used just
form.$setPristine();
form.$setUntouched();
Actually it seems logical, since the binding is still here, the values in the form are now empty, and angular ng-messages directive is triggering only if the form is not untouched, so I think it's normal after all.
Final (very simple) code is that :
function reset(form) {
form.$setPristine();
form.$setUntouched();
};
A big problem I encountered with that :
Only once, the callback seems to have fucked up somewhere, and somehow the fields weren't empty (it was like I didn't click on the submit button).
When I clicked again, the date sent was empty. That even more weird because my submit button is supposed to be disabled when a required field is not filled with the good pattern, and empty is certainly not a good one.
I don't know if my way of doing is the best or even correct, if you have any critic/suggestion or any though about the problem I encountered, please let me know, I always love to step up in angularJS.
Hope this will help someone and sorry for the bad english.
You can pass your loginForm object into the function ng-click="userCtrl.login(loginForm)
and in the function call
this.login = function (loginForm){
loginForm.$setPristine();
loginForm.$setUntouched();
}
So none of the answers were completely working for me. Esp, clearing the view value, so I combined all the answers clearing view value, clearing errors and clearing the selection with j query(provided the fields are input and name same as model name)
var modelNames = Object.keys($scope.form).filter(key => key.indexOf('$') !== 0);
modelNames.forEach(function(name){
var model = $scope.form[name];
model.$setViewValue(undefined);
jq('input[name='+name+']').val('');
angular.forEach(model.$error, function(value, name) {
// reset validity
model.$setValidity(name, null);
});
});
$scope.form.$setPristine();
$scope.form.$setUntouched();
I am using a select tag in a webpage. I am controlling it using Angular JS. I want a function to run whenever a value is selected. I know about the ng-change event but that only works for a change in value. What can I do?
<select class="form-control"
ng-model="types"
ng-options="x.id as x.name for x in records"
ng-change="genres=undefined; events=undefined; clearDetails()" >
</select>
<select class="form-control"
ng-change="events=undefined; clearDetails()"
ng-model="genres"
ng-options="x.id as x.name for x in records[types-1]['genre']"
ng-change="currentgenre=records[types-1]['genre'][genre-1][intro]" >
</select>
<select class="form-control"
ng-change="clearDetails(); loadEventData()"
ng-model="events"
ng-options="x.event_id as x.name for x in records[types-1]['genre'][genres-1]['details']" >
</select>
This can (and should) be handled in your controller, and you can do it easily by watching the variable that is changing. In this case that's genres, so you can do something like:
$scope.$watch('genres', function(newValue, oldValue) {
$scope.events = undefined;
clearDetails();
...
});
And you can make it even more specific by using newValue and oldValue to only run certain methods in certain cases, if you so desire.
#OZ_ gave me the right answer in the comments section. ng-click was what I needed.