I'm working with angular material, but I have a problem in Angular 7 with a FormArray.
When I only have 1 item, it's working fine, but once I add another item dynamically, selects has correct value, but the input related has the same value which is wrong.
Example:
1) change select 1:
Value of select 1: Item1 (correct)
Value of input 1: Code1 (correct)
2) Add a new item, copying previous values:
Value of select 1: Item1 (correct)
Value of input 1: Code1 (correct)
Value of select 2: Item1 (correct)
Value of input 2: Code1 (correct)
3) Change select 2 to Item2:
Value of select 1: Item1 (correct)
Value of input 1: Code2 (incorrect)
Value of select 2: Item2 (correct)
Value of input 2: Code2 (correct)
The input 1 should have "Code1" as value, but all inputs changes its values to the last changed select.
Here is my code:
Template:
<div formArrayName="transporteArrayItems">
<div *ngFor="let item of transporteForms.controls; let i=index" [formGroupName]="i">
<div class="form-group row">
<div class="col-md-9 dropdown-input-inline">
<mat-form-field appearance="outline">
<mat-label>Condiciones de entrega</mat-label>
<mat-select placeholder="Condiciones de entrega" formControlName="selectCondicionesEntrega"
(selectionChange)="condicionesEntregaChange($event, i)" required>
<mat-option *ngFor="let condicion of listCondiciones" [value]="condicion">
{{condicion.descripcion}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="col-md-3">
<mat-form-field appearance="outline">
<mat-label>Código</mat-label>
<input formControlName="condicionesCodigo"
[value]="item.controls.selectCondicionesEntrega.value.codigo || null" matInput required disabled>
</mat-form-field>
</div>
</div>
</div>
</div>
ts:
addTransporteItem(index) {
const transporteItem = this.formBuilder.group({
selectCondicionesEntrega: ['', Validators.required],
condicionesCodigo: ['', Validators.required]
});
let formularioAnterior = transporteItem;
if (index != undefined) {
Object.assign(formularioAnterior, this.transporteForm.controls.transporteArrayItems['controls'][index]);
this.transporteForms.push(formularioAnterior);
} else {
this.transporteForms.push(transporteItem);
}
}
I'm not sure if could be because I'm copying the previous formgroup, but tried different ways and still didn't work.
Working example:
https://codesandbox.io/s/determined-satoshi-xpkpf
Thanks in advance.
Related
UPDATED
Creating user input popup form, in one field trying to do autocomplete, with THIS solution.
I guess the auto-complete is working fairly fine, the issue is the options not coming below the input box.
Code:
<div>
<div class="modal-header">
</div>
<div class="modal-body">
<form novalidate [formGroup]="myform">
<div class="form-floating form-group">
<input type="text" class="form-control" id="fltName" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<label for="fltName">Name</label>
<mat-autocomplete autoActiveFirstOption #emp="matAutocomplete" class="form-control">
<mat-option *ngFor="let option of filteredOptions | async" [value]="option">
{{option}}
</mat-option>
<mat-autocomplete>
</div>
Question:
1. The options box should display, only if there 2 or less matches?
2. return emp names options by inserting emp_id?
How can I setup option such as {name:xyz, emp_id:123, emp_no:333}, and if I text any value should give name as option like if I write 33 should bring option xyz.
Question 1: The options box should display, only if there 2 or less matches
For this you need to apply the ng-container (To avoid rendering the option box) directive and apply the length condition over there according to your requirement.
<mat-autocomplete #auto="matAutocomplete">
<ng-container *ngIf="filteredOptions && (filteredOptions | async)?.length <= 2">
<mat-option *ngFor="let option of filteredOptions | async" [value]="option" >
{{option.name}} //this is the value you always want to display
</mat-option>
</ng-container>
</mat-autocomplete>
Question 2: return emp names options by inserting emp_id?
The second part can be achieved by applying filter on the required fields, i.e.
private _filter(value: string): string[] {
if(value && value !== ""){
const filterValue = value.toLowerCase();
return this.options.filter(option => option.name.toLowerCase().includes(filterValue) || option.emp_no.toString().startsWith(filterValue));
} else {
return [];
}
}
apart from this you need to fix the pipe part as well, you have to pass the control value in RxJs startWith operator like,
this.filteredOptions = this.myControl.valueChanges
.pipe(
startWith(this.myControl.value),
map(value => this._filter(value))
);
Note: you can access the working solution here
I am able to dynamically Add or Remove multiple input fields, But the issue is once I have added items and enter some values in the input fields and remove it using delete button, values are not getting cleared, i have tried to empty my array for that index as well but it is not working. Any suggestions please?
I appreciate your help!
Here is my html code:
<div class="row">
<div>
<button mat-raised-button (click)="test()">Add </button>
</div>
<div *ngFor="let value of fieldArray; let i = index">
<mat-form-field >
<input matInput value="{{getDataValue(i)}}" (focusout)="onFocusOut($event)" name ="test">
</mat-form-field>
<button mat-raised-button class="btn btn-danger btn-sm " (click)="remove(i)">Delete</button>
</div>
</div>
code in my .ts file:
fieldArray: Array = [];
remove(i: number) {
this.fieldArray.splice(i,1);
}
test(){
this.fieldArray.push(this.fieldArray.length);
}
With TrackBy, Angular is able to detect which items are deleted or added to an array, using a unique identifier. So, only the items added or removed have to be updated in the DOM.
Like below code for view:
<div *ngFor="let value of fieldArray; let i = index; trackBy: trackByFn">
Below code for .ts file:
trackByFn(index, item) {
return index; // or item.id
}
I am stuck trying to simplify the user inputs by automatically populating the input value based on the above selection box in a model.
Context:
I am trying to populate the account payments by using standard items.
In my DB I have the following:
item | value
item 1 | Value 1
item 2 | Value 2
item 3 | value 3
I would have the following in my modal code
<div class="modal-body">
<label class="label-control">Membership Number:</label>
<div class="input-group">
<input type = "text" class = "form-control" name = "membership" value="">
</div>
<label class="label-control">Rank:</label>
<div class="input-group">
<select type="text" class="selectpicker" data-sytle="select-with-transition" name="item" data-size="6">
#foreach ($payments as $p)
<option value ={{$p->item}}>{{$p->item}}</option>
#endforeach
</select>
</div>
<label class="label-control">Amount</label>
<div class="input-group">
<input type="text" class="form-control" name = "amount" value="xxxx">
</div>
I would like to populate the value="xxxx" based is what selected in the item drop down.
So if Item 3 is selected from the drop down, the value 3 is populated in the value field.
I understand that I require an onchange javascript, but I am a little lost.
Saving this to the DB will not be an issue, just the javascript to update the value field is where I get stuck
Give an ID "selectBox" to selectbox And "textField" to the text box. By using Jquery then you can populate its value.
$(document).on('change', 'select#selectBox', function(){
$('#textField').val();
});
Also make small change in your loop, add quotes " " to value.
#foreach ($payments as $p)
<option value ="{{$p->item}}">{{$p->item}}</option>
#endforeach
I am trying to solve the problem of having my field change on both form array fields when you change the field type.
As you can see in the image. When I select the left field first name the right field changes to a text field. But when a add a new field it generates a dropdown field to the right and changes the top one as well. How do I make these fields act independently?
I also think the problem is the ngTemplate function I am using to check to see if the field is a option or text field. maybe this is a global within the form I am using, if that makes sense?
Html
<div class="filter-wrapper">
<form [formGroup]="usersForm" (ngSubmit)="onSearch()">
<ng-container *ngFor="let userFormGroup of usersForm.controls.users.controls; let i = index">
<div [formGroup]="userFormGroup" class="filter-form">
<div class="search-wrapper">
<mat-form-field class="form-toggle input-half dialog-dropdown">
<mat-select formControlName="columnOptions">
<mat-option value="null">
-- none --
</mat-option>
<mat-option *ngFor="let formOption of displayedOptions" [value]="formOption.name" (click)="getOptionData(formOption.name)">
{{formOption.title}}
</mat-option>
</mat-select>
</mat-form-field>
<span class="match-txt">matches</span>
<mat-form-field class="input dialog-dropdown">
<ng-container *ngIf="selectedOpt.options?.length > 0; else inputField">
<mat-select formControlName="columnValues">
<mat-option value="null">
-- none --
</mat-option>
<mat-option *ngFor="let formOption of selectedOpt.options" [value]="formOption.display_long_value || formOption.description">
{{ formOption.display_long_value || formOption.description }}
</mat-option>
</mat-select>
</ng-container>
<ng-template #inputField>
<input matInput type="text" formControlName="columnValues">
</ng-template>
</mat-form-field>
</div>
<div class="action-btns">
<mat-icon (click)="addFormControl()">add_circle_outline</mat-icon>
<ng-container *ngIf="i !== 0">
<mat-icon (click)="removeFormControl(i)">remove_circle_outline</mat-icon>
</ng-container>
</div>
</div>
</ng-container>
</form>
</div>
Form builder
this.usersForm = this.fb.group({
users: this.fb.array([
this.fb.group({
columnOptions: ['', Validators.required],
columnValues: ['', Validators.required],
}),
])
})
https://stackblitz.com/edit/angular-l7stwq
The stackblitz wasn't working because the import was missing to initialize columnOption/columnValues, but I think you should reference the formgroups created by your array. I usually start by logging the formgroup.value every step of the way to make sure My ui matches my data model, But try this:
change
<div [formGroup]="userFormGroup" class="filter-form">
to
<div [formGroupName]="i" class="filter-form">
This should match with what your model would be showing for the formArray (since
the groups have index-based names, per them being 'arrays'.
Hopefully this gets you somewhere, If you could update the stackblitz to be able to run I'd do some trial and error to see where I get
Good luck/Happy Coding!
Simply I have two radio buttons when the first radio button is selected one select box appears and when the radio button is selected two select box appears. After this, I have one button "SUBMIT" now I want that when I select first radio then the submit button must be enabled but when I select second radio button then the button must be disabled till both of the select box doesn't have some value.
I am using the form in HTML taking form-group to consider all the fields, when all the fields have some value then the only button will get enabled but in case of the above situation I got stuck.
**This is my javascript code.
I m new in this plz help.**
function statecheck(){
if (document.getElementById('state').checked = true) {
document.getElementById('ifstate').style.display = 'block';
document.getElementById('ifapmc').style.display = 'none';
document.getElementById('ifapmc').disabled = true;
}
}
function stateapmccheck(){
if (document.getElementById('apmc').checked = true) {
document.getElementById('ifapmc').style.display = 'block';
document.getElementById('ifstate').style.display = 'none';
document.getElementById('ifstate').disabled = true;
}
}
This is my Angular material code
<div>
<label>Registration Level: </label>
<mat-radio-group aria-label="Select an option"
formControlName="choosereglevel" #reglevel>
<mat-radio-button value="1" id="state"
onclick="javascript:statecheck();" color="primary">State</mat-radio
button>
<mat-radio-button value="2" id="apmc"
onclick="javascript:stateapmccheck();" color="primary">APMC</mat-radio-
button>
<!--<mat-error
*ngIf="firstFormGroup?.controls?.choosereglevel?.hasError('required')">
Please choose one <strong>level.</strong>
</mat-error>-->
</mat-radio-group>
</div>
<br>
<!--hidden select box-->
<div id="ifstate" style="display:none;">
<mat-form-field>
<mat-label>Registered With State: </mat-label>
<mat-select formControlName="choosestate" #selectstate>
<mat-option *ngFor="let selectstate of registrationstate"
[value]="selectstate.value">
{{selectstate.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<!--second T&C select box-->
<div id="ifapmc" style="display:none;">
<mat-form-field>
<mat-label>Registered With State: </mat-label>
<mat-select formControlName="choosestate" #selectstate>
<mat-option *ngFor="let selectstate of registrationstate"
[value]="selectstate.value">
{{selectstate.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>Registered With APMC: </mat-label>
<mat-select formControlName="chooseapmc" #selectapmc>
<mat-option *ngFor="let selectstate of
registrationapmcstate"
[value]="selectstate.value">
{{selectstate.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
Looks to me like you've got a syntax error inside your if(...) statements
When you use a single = sign it sets the property on the left of it to match the value on the right
To do a comparison you need a double ==
So try document.getElementById('state').checked == true and if document.getElementById('apmc').checked == true