I have my interface like this
export interface IFilterParams {
columnTitle: string;
key: string;
mode: FILTER_MODE;
compareMethod: COMPARE_METHOD;
keyToCheck?: string;
sliderKeys?: string[];
startingValue?: number;
placeHolder?: string;
cssClass?: string;
formControl?: string;
}
which I use to create html (filter form) like below
<div [ngClass]="(filter.cssClass || 'col-md-4') + 'mt-2 pt-1'">
<div class="filter-key-label">
{{filter.columnTitle}}
</div>
<div class="filter-form-control d-flex flex-wrap justify-content-left">
<mat-form-field class="full-width">
<input type="text" matInput [formControl]="filter.formControl" [matAutocomplete]="auto" (change)="handleEmptyInput($event, filter.key)">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of getOptionsTypeAhead(filter.key, filter.formControl) | async" [value]="option" (onSelectionChange)="typeaheadChange($event, filter.key)">
{{ option }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
Now as you can see, I am trying to bring in values from the filter variable (look at how I am passing filter.key)
The thing is, I am unable to pass a variable value to [formControl] as it's throwing me
TypeError: Cannot create property 'validator' on string 'country' at setUpControl
for
{
key: 'country',
mode: FILTER_MODE.AUTOCOMPLETE,
columnTitle: 'Country',
compareMethod: COMPARE_METHOD.ONE_TO_ONE,
formControl: 'countryForm',
cssClass: ''
},
What am I doing wrong here? I want to use the same html to generate different forms with different form control names.
Update
This didn't work even after adding the direct string based variable in my component.ts file like this
countryForm: FormControl = new FormControl();
You are passing a string object to the formControl property with [formControl]="filter.formControl":
countryForm: FormControl = new FormControl();
filter = {
...
formControl: 'countryForm',
}
But you need to pass a FormControl object:
countryForm: FormControl = new FormControl();
filter = {
...
formControl: this.countryForm,
}
Related
I created a simple reusable component as:
TS
import {Component, Input, OnInit} from '#angular/core';
import {FormControl} from '#angular/forms';
#Component({
selector: 'app-select',
templateUrl: './select.component.html',
styleUrls: ['./select.component.css']
})
export class SelectComponent implements OnInit {
#Input() control: FormControl;
#Input() label: string;
#Input() options: [];
#Input() idAndForAttributes: string;
#Input() customClass: string;
constructor() { }
ngOnInit() {
}
}
HTML
<div class="form-group" [ngClass]="{'invalid': control.invalid && control.touched && control.dirty}">
<label [attr.for]="idAndForAttributes">{{ label }}:</label>
<select class="form-control" [ngClass]="customClass" [formControl]="control" [attr.id]="idAndForAttributes">
<option value="0">- Select -</option>
<option *ngFor="let item of options" [ngValue]="item.id">{{item.description}}</option>
</select>
<ng-container *ngIf="control.dirty && control.touched && control.invalid">
<div *ngIf="control.errors.required || (control.errors.min && control.value == 0)">
<small style="color: #c62828;">
Value is required.
</small>
</div>
</ng-container>
</div>
Now I'm trying to use it in my other html as:
<form [formGroup]="profileActivityForm">
<app-select [control]="profileActivityForm.get('activityType')" [idAndForAttributes]="'type'" [label]="'Type'"
[options]="profileActivityTypes"></app-select>
</form>
Then in TS
profileActivityTypes: string[] = [];
ngOnInit() {
this.profileActivityTypes.push('New')
this.profileActivityTypes.push('Update')
this.profileActivityForm = this.fb.group({
activityType: [0]
});
}
But it is showing invisible options like the following picture:
I think the problem is on the html of the reusable component <option *ngFor="let item of options" [ngValue]="item.id">{{item.description}}</option>
Because it is looking for a description, how can I send the item as a description from the child component?
UPDATE
I tried:
profileActivityTypes: [] = [];
....
let profileActivities = [{ description: 'New' }, { description: 'Update' }]
this.profileActivityTypes.push(profileActivities)
but it is throwing an error on push:
Argument of type '{ description: string; }[]' is not assignable to
parameter of type 'never'
In order to solve this, I changed the assignation of the profileActivities array instead of creating the array and then pushing it. I assign it directly as:
profileActivityTypes = [];
this.profileActivityTypes = [{ id: 1, description: 'New' }, {id: 2, description: 'Update'}]
I hope this works for more people!
I want to update the field by clicking the submit from matdialog .
html
<h2>network <mat-icon (click)="openDialog(mytemplate)">add_circle_outline</mat-icon></h2>
<ng-template #mytemplate>
<div class="container">
<div class="box">
<mat-form-field appearance="outline">
<input matInput placeholder="enter the items" />
</mat-form-field>
<button mat-stroked-button mat-dialog-close color="accent">CANCEL</button>
<button mat-flat-button color="accent" (click)="onclick()">Submit</button>
</div>
</div>
</ng-template>
.ts
provider = [];
providers: provider;
getnetwork() {
this.service.getAllNetworkProviders().subscribe(res => {
this.provider = res.data;
this.dataSource = this.provider;
});
}
openDialog(template): void {
// ask user to confirm, if he really wants to proceed
this.dialogRef = this.dialog.open(template);
this.dialogRef.afterClosed().subscribe(isTrue => {
if (isTrue) {
const activatenetworkprovider = { networkName: this.providers.id };
this.service.networkProviderStatus(activatenetworkprovider).subscribe(data => {
this.getnetwork();
this.snackBar.open('Successfully created new network provider sim', 'Close', { duration: 2000 });
});
}
});
}
model.ts
export interface provider {
id: number;
name: string;
}
I'm getting error [ERROR TypeError: Cannot read properties of undefined (reading 'id')
].
I have updated the questions .
This is a declaration:
providers: provider;
It is not an actual object/instance, but rather just a handle to an object. You still need to assign something to this with =. Preferably an object that contains a member id (e.g.: { id: 1, ... }).
It is also convention to name interfaces with capital letters, like this:
export interface Provider {
id: number;
name: string;
}
I have three matformfields named Form A,Form B,Form C and three mat radio buttons named A,B,C.
What I want is that when radiobutton A is enabled or checked Form A's default value should be A and in other two form fields there should be no value by default. When radiobutton B is enabled or checked Form B's default value should be B and in other two form fields there should be no value by default.I want the same for radiobutton C. I am getting the dropdown data from service.
Sample data:
listes: any[] = [
{ id: 1, name: "A" },
{ id: 2, name: "B" },
{ id: 3, name: "C" },];
WHAT I HAVE TRIED:
I tried to get the id 1 which has value A and used setvalue to patch it in Form A but its not worling
const toSelect = this.listes.find(c => c.id == 1);
this.patientCategory.get('patientCategory').setValue(toSelect);
STACKBLITZ: https://stackblitz.com/edit/how-to-set-default-value-of-mat-select-when-options-are-reo3xn?file=app%2Ftable-basic-example.html
I've corrected your code as per the scenario you described. Although my temporary code is a little lengthy, it applies the logic you described. But I hope you simplify it further.
Fix:
[value] attribute of a mat-option shouldn't be set to category itself as category is an object. [value] expects a singular uniquely identifying value. So you should set it to [value] = "category.name". Ideally, we set value to unique identifiers like [value]="category.id" or [value]="category.key" etc.
The three mat-selects should behave independently in your scneario. So they should never be assigned to the same formControl. Instead, assign individual formControls to have full control over them.
Finally, you can use the function valueChange assigned to the radio buttons, to conditionally apply values in FormA, FormB and FormC as per your scenario.
<form [formGroup]="patientCategory">
<mat-form-field class="full-width">
<mat-select placeholder="FORMA" formControlName="patientCategoryA">
<mat-option *ngFor="let category of listes" [value]="category.name">
{{category.name}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="full-width">
<mat-select placeholder="FORMB" formControlName="patientCategoryB">
<mat-option *ngFor="let category of listes" [value]="category.name">
{{category.name}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="full-width">
<mat-select placeholder="FORMC" formControlName="patientCategoryC">
<mat-option *ngFor="let category of listes" [value]="category.name">
{{category.name}}
</mat-option>
</mat-select>
</mat-form-field>
</form>
<div class="container" style="margin-top: 0px;">
<div class="example-container">
<mat-radio-group #group="matRadioGroup" [(ngModel)]="test" [(value)]="selectedValue"
(change)="onValueChange(group.value)">
<mat-radio-button value="A" [checked]="defaultSelected">A</mat-radio-button>
<mat-radio-button style="margin-left:10px" value="B">B</mat-radio-button>
<mat-radio-button style="margin-left:10px" value="C">C</mat-radio-button>
</mat-radio-group>
</div>
</div>
import { Component, ViewChild } from "#angular/core";
import {
FormBuilder,
FormGroup,
FormControl,
Validators
} from "#angular/forms";
import { DataService } from "./data.service";
/**
* #title Basic table
*/
#Component({
selector: "table-basic-example",
styleUrls: ["table-basic-example.css"],
templateUrl: "table-basic-example.html"
})
export class TableBasicExample {
patientCategory: FormGroup;
listes: any[];
constructor(private fb: FormBuilder, private dataService: DataService) {}
ngOnInit() {
this.dataService.getData().subscribe(res => {
this.listes = res;
});
this.patientCategory = this.fb.group({
patientCategoryA: [null, Validators.required],
patientCategoryB: [null, Validators.required],
patientCategoryC: [null, Validators.required]
});
}
onValueChange(value) {
if (value === "A") {
this.patientCategory.get("patientCategoryA").setValue(value);
this.patientCategory.get("patientCategoryB").setValue(null);
this.patientCategory.get("patientCategoryC").setValue(null);
} else if (value === "B") {
this.patientCategory.get("patientCategoryB").setValue(value);
this.patientCategory.get("patientCategoryA").setValue(null);
this.patientCategory.get("patientCategoryC").setValue(null);
} else if (value === "C") {
this.patientCategory.get("patientCategoryC").setValue(value);
this.patientCategory.get("patientCategoryB").setValue(null);
this.patientCategory.get("patientCategoryA").setValue(null);
}
}
}
Hope this helps. Let me know if you have any issues.
How to receive the input values of variable number of input boxes?
I searched a long time in Google but I'm not able to do it.
ParentComponent.html:
<my-Input [interfaces]="interfaces"> {{ title }}</my-Input>
ParentComponent.ts:
interface single_input{
title: string,
get_value(event: string): void;
}
interfaces: single_input[] = [
{
title: "something",
get_value(event){ console.log(event)}
}
];
ChildComponent.html:
<div *ngFor="let i of interfaces">
<Input (keyup)="i.get_value($event.target.value)"> {{ title }}</Input>
</div>
To log this in the console works, but I just want to bind the input values to the given interface and give it back by eventemitter to the parent.
How to do this? Do I need a class for it?
I got it now.
But it looks like a workaround. is there a better solution than following code?
Code-Update:
ParentComponent.ts:
interface single_input{
title: string;
value: string;
}
interfaces: single_input[] = [
{
title: "something",
}
];
ChildComponent.html:
<div *ngFor="let i of interfaces">
<Input (keyup)="i.value = $event.target.value"> {{ title }}</Input>
</div>
It is far simpler than what you are trying to do. Angular have specific mechanism to achieve this goal, one of them being the decorators #Input and #Output. You are already using the #Input decorator to pass in the data to the child component, now you just need to use the #Output decorator to pass the data back to the parent. I recommend looking at the docs and this example to have a better understanding on how this works. Below is an example for your use case.
ChildComponent.html
<div *ngFor="let i of interfaces">
<input (keyup)="onKeyUp(i, $event.target.value)"> {{ title }}</input>
</div>
ChildComponent.ts
import { Component, EventEmitter } from '#angular/core';
#Component({ ... })
export class ChildComponent {
...
#Input() interfaces;
#Output() childValue: EventEmitter<any> = new EventEmitter<any>();
// ^ Use a proper datatype ^
onKeyUp(source, value) {
this.childValue.emit({
source: source,
value: value
});
}
...
}
ParentComponent.html
<my-input [interfaces]="interfaces" (childValue)="onChildValueChange($event)"> {{ title }}</my-input>
ParentComponent.ts
import { Component} from '#angular/core';
#Component({ ... })
export class ParentComponent {
...
onChildValueChange(event) {
// Do what you want with it
const interface = this.interfaces.find(interface =>
interface.title === event.source.title);
console.log('[ParentComponent] Value from child component:', event);
console.log('[ParentComponent] Changed interface:', interface);
console.log('[ParentComponent] Changed value:', event.value);
}
...
}
Extra tip: Just for completeness, an equally popular approach, would be using a service to contain the data, where the child changes the data on the service and the parent read from it.
i create this dropdown component :
<mat-form-field appearance="outline">
<mat-label>{{formTitle| translate}} *</mat-label>
<mat-select [(value)]="itemId">
<div class="col-lg-12 mt-4 kt-margin-bottom-20-mobile">
<mat-form-field class="mat-form-field-fluid" appearance="outline">
<mat-label>{{'GENERAL.TITLE' | translate}} *</mat-label>
<input (keyup)="getValues($event.target.value)" matInput [placeholder]="'GENERAL.TITLE' | translate">
</mat-form-field>
</div>
<mat-progress-bar *ngIf="loading" class="mb-2" mode="indeterminate"></mat-progress-bar>
<mat-option (click)="emitdata(item.key)" *ngFor="let item of values" [value]="item.key">
{{item.value}}
</mat-option>
</mat-select>
ts:
export class SearchableDropdownComponent implements OnInit {
#Input() url: string;
#Input() formTitle: string;
#Output() selectedId = new EventEmitter<number>();
#Input() itemId: number;
loading = false;
values: KeyValue[];
title: string;
constructor(
private searchService: SearchableDropDownService,
private cdRef: ChangeDetectorRef) {
}
ngOnInit(): void {
this.getValues(null);
}
emitdata(event): void {
console.log(event);
this.selectedId.emit(event);
}
getValues(event): void {
this.cdRef.detectChanges();
this.loading = true;
let model = {} as SendDateModel;
model.page = 1;
model.pageSize = 60;
model.title = event;
this.searchService.getAll(this.url, model).subscribe(data => {
this.values = data['result']['records'];
this.cdRef.detectChanges();
this.loading = false;
});
}
}
and i used it in components :
<div class="col-lg-6 kt-margin-bottom-20-mobile">
<kt-searchable-dropdown [itemId]="304" [formTitle]="'COURSE.COURSE_GROUP'" [url]="url"></kt-searchable-dropdown>
</div>
i pass to dropdown 304 item for pre selected it in dropdown . but it not pre-selected item 304 in dropdown .
how can i set the 304 item selected in dropdown?
As you are taking the ItemId using Input() you can always use ngModel to implement two way data binding.
For e.g. in your case :
<mat-select [(ngModel)]="ItemId">
<mat-option *ngFor="let item of values" [value]="item.key">{{item.value}}</mat-option>
</mat-select>
So, basically it will get binded in two way with variable contain data and will remain selected as you load the component.
You can read more about two way data binding here.
Update
ngModel should work. You have not wrongly implemented but its not correct way also.
Your selected value only will come from [(ngModel)].
so your
[(value)]="itemId"
should be
[(ngModel)]="itemId"
complete code will be :
<mat-select [(ngModel)]="ItemId">
<mat-option *ngFor="let item of values" [value]="item.key">{{item.value}}</mat-option>
</mat-select>
Try not to declare any div or component after mat-select if not necessary or debug one by one if its bugging to your component or not.