input event not triggering for dynamic fields on pasting in Angular - html

I have a form with dynamically generated input fields. While pasting content to the fields I need a preview of the input contents on a div. I am using Angular 11.
Here is my .ts file
quickSendForm = this.fb.group({
parameters: this.fb.array([]),
})
Here is my .html file
<form [formGroup]="quickSendForm" #formDirective="ngForm" (ngSubmit)="submit()">
<div>
<mat-form-field>
<textarea matInput placeholder="To number" rows="5" maxlength="1300" name="toNumber" id="toNumber" formControlName="toNumber" ></textarea>
</mat-form-field>
</div>
<div *ngFor="let param of parameters.controls; let i=index" class="col-md-3">
<mat-form-field>
<input matInput type="text" placeholder="Parameter- {{ i + 1 }} " name="parameter" id="showCustomParameter{{ i + 1 }}" formControlName="parameter">
</mat-form-field>
</div>
<div>--input content here --</div>
I am getting the content of toNumber field but not getting for parameter field

<input> tag isn't properly closed. But I used template reference to show input value there. Could just as easily use formControl value though.
<mat-form-field>
<input matInput #myInput type="text" placeholder="Parameter- {{ i + 1 }} "
name="parameter" id="showCustomParameter{{ i + 1 }}"
formControlName="parameter">
</mat-form-field>
<div> {{ myInput.value }}</div>

Related

Angular template inside reactive form

In our application I noticed lots of duplicate code in the HTML of our forms, most input elements have the same structure so I want to create a generic input component that we can use instead to keep the files clean.
The problem I currently have is that there is 1 form that has 2 nested formGroups inside, example:
this.addressForm = this.fb.group({
postalAddress: this.fb.group({
street: ["", [Validators.required]],
}),
visitorAddress: this.fb.group({
street: [""],
})
});
This leads to my new component's HTML to also have duplicate code due to some forms requiring a formGroupName.
<div [formGroup]="form" *ngIf="form && controlPath && controlName">
<div *ngIf="groupName" [formGroupName]="groupName">
<mat-form-field class="w-full">
<mat-label>{{ label }}</mat-label>
<input type="text" matInput [formControlName]="controlName" *ngIf="type === 'text'">
<input type="number" matInput [formControlName]="controlName" *ngIf="type === 'number'">
<mat-error *ngFor="let message of form.get(controlPath)?.errors?.['messages']">{{ message | i18n }}</mat-error>
</mat-form-field>
</div>
<div *ngIf="!groupName">
<mat-form-field class="w-full">
<mat-label>{{ label }}</mat-label>
<input type="text" matInput [formControlName]="controlName" *ngIf="type === 'text'">
<input type="number" matInput [formControlName]="controlName" *ngIf="type === 'number'">
<mat-error *ngFor="let message of form.get(controlPath)?.errors?.['messages']">{{ message | i18n }}</mat-error>
</mat-form-field>
</div>
The code above works fine but as mentioned, I would like to get rid of the duplicate code. I figured this would be a good case for a ng-template but it appears when using a template the nested controls can no longer find the surrounding FormGroup.
Example
<div [formGroup]="form" *ngIf="form && controlPath && controlName">
<div *ngIf="groupName" [formGroupName]="groupName">
<ng-content *ngTemplateOutlet="content"></ng-content>
</div>
<div *ngIf="!groupName">
<ng-content *ngTemplateOutlet="content"></ng-content>
</div>
<ng-template #content>
<mat-form-field class="w-full">
<mat-label>{{ label }}</mat-label>
<input type="text" matInput [formControlName]="controlName" *ngIf="type === 'text'">
<input type="number" matInput [formControlName]="controlName" *ngIf="type === 'number'">
<mat-error *ngFor="let message of form.get(controlPath)?.errors?.['messages']">{{ message | i18n }}</mat-error>
</mat-form-field>
</ng-template>
Error:
Has anyone encountered such a situation and if so, what would be a good way to go about this?
UPDATE
After Garbage Collectors answer I refactored my code to the following:
<div [formGroup]="form" *ngIf="form && controlPath && controlName">
<div *ngIf="groupName" [formGroupName]="groupName">
<ng-container [ngTemplateOutlet]="inputTemplate" [ngTemplateOutletContext]="{ templateForm: form, templateFormGroup: groupName, templateControlName: controlName }"></ng-container>
</div>
<div *ngIf="!groupName">
<ng-container [ngTemplateOutlet]="inputTemplate" [ngTemplateOutletContext]="{ templateForm: form, templateControlName: controlName }"></ng-container>
</div>
<ng-template #inputTemplate let-form="templateForm" let-groupName="templateFormGroup" let-controlName="templateControlName">
<div [formGroup]="form" [formGroupName]="groupName">
<mat-form-field class="w-full">
<mat-label>{{ label }}</mat-label>
<input type="text" matInput [formControlName]="controlName" *ngIf="type === 'text'">
<input type="number" matInput [formControlName]="controlName" *ngIf="type === 'number'">
<mat-error *ngIf="form.get(controlPath)?.errors?.['required']">{{ 'error.required' | i18n }}</mat-error>
<mat-error *ngIf="form.get(controlPath)?.errors?.['email']">{{ 'error.email' | i18n }}</mat-error>
<mat-error *ngIf="form.get(controlPath)?.errors?.['invalid']">{{ 'error.form.invalid' | i18n }}</mat-error>
<mat-error *ngIf="form.get(controlPath)?.errors?.['minlength']">{{ minLengthError }}</mat-error>
<mat-error *ngIf="form.get(controlPath)?.errors?.['pattern']">{{ patternError }}</mat-error>
<mat-error *ngFor="let message of form.get(controlPath)?.errors?.['messages']">{{ message | i18n }}</mat-error>
</mat-form-field>
</div>
</ng-template>
Though the template now has all variables correctly, it cannot find the control path for non-nested forms. I outputted controlName in the template as a test and it is correct. I expect the error occurring due to the formGroupName being null for non-nested forms.
Error:
As far as I remember, every isolated piece of code displaying form controls should have a reference to the form where those controls were defined.
Actions
Use ng-container instead of ng-content to include your templates
Pass form as a parameter to your template
Assign form parameter to [formGroup] attribute inside your template
Template
<ng-template #demoTemplate let-form="demoForm">
<div [formGroup]="form">
<!-- Insert the rest of your template here -->
</div>
</ng-template>
Main component using template
<div [formGroup]="form" *ngIf="form">
<ng-container
[ngTemplateOutlet]="demoTemplate"
[ngTemplateOutletContext]="{ demoForm: form }">
</ng-container>
</div>
Notice that attribute formGroup is added in both places, in the template and in the parent.
Use [formControl] instead of formControlName
You can pick nested variables via the get command, for instance form.get('Address.Street')
More explanations: https://stackoverflow.com/a/74668699/6019056

The value [value] is not assigned in input

I have a little problem using [value] in my inputs.
This is my component .html
<div class="form-group">
<mat-form-field class="example-full-width" style="padding-left: 15px;">
<input matInput [value]='value_2_' [(ngModel)]="form.name_1" placeholder="name_1"
name="name_pro_1" required>
</mat-form-field>
</div>
Everything looks good, but when I run the program, the value is shown in the corresponding input, however, being a value required by the input, it is still in red, it is solved until I add a letter to the value or remove something from the input.
I managed to solve the error by deleting
name = "name_pro_1"
but since I use NgModel I cannot remove the name because I get other errors. Like:
core.js:4352 ERROR Error: If ngModel is used within a form tag, either the name attribute must be set or the form
control must be defined as 'standalone' in ngModelOptions.
Example 1: <input [(ngModel)]="person.firstName" name="first">
Example 2: <input [(ngModel)]="person.firstName" [ngModelOptions]="{standalone: true}">
I think the problem is [(ngModel)]="form.name_1". Try to replace that with
[(ngModel)]="testValue" and define a variable in the class with the same name and it will work.
I can't replicate the problem, but using [(ngModel)] doesn't make using [value] unnecessary? You can set the form.name_1 to the value you want on the component.ts file.
Finally i resolved the issue, that it's my solution:
First thing first, i delete the [(ngModel)] in the input of component.html, then i used (change) to call the function when user select a SKU
<div class="form-group input-sku" >
<mat-form-field class="example-full-width">
<mat-label>SKU</mat-label>
<input type="text" placeholder="Search.." id="sku_select" aria-label="number"
matInput [formControl]="myControl" [matAutocomplete]="auto"
[(ngModel)]="selectedValue" (change)="changedata_1($event)" >
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
<mat-option *ngFor="let data of filter_products | async" [value]="data.sku">{{data.sku}}</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
Input who recived the value:
<div class="form-group inputs-name">
<mat-form-field class="example-full-width">
<mat-label>Name</mat-label>
<input type="text" matInput id="nombre_1" required [value]="name_1" name="name_1" placeholder="Name" >
</mat-form-field>
</div>
And in my component.ts
changedata_1(e) {
this.nombre_1 = e.target.value;
this.DataToSave.nombre_1 = this.nombre_1;}
private _filter(value: string): Products_BD[] {
const filterValue = value.toLowerCase();
const data_ = this.DatasProducts.filter(DataProducts => DataProducts.sku.toLowerCase().includes(filterValue));
for(let element of data_){
const sku1= elemento.sku;
const name1 = element.name;
this.name_1 = name1;
this.DataToSave.sku_1 = sku1;
this.DataToSave.name_1 = name1;
}
return this.DataProducts.filter(DataProducts => DataProducts.sku.toLowerCase().includes(filterValue));}
this.filter_products = this.myControl.valueChanges.pipe(
startWith(''),
map(value => this._filter(value))
);
I hope this solution helps! :)

Angular Input Placeholder on the top

I am new in Angular and I have to create an input field like that:
Everything is ok besides the Vorname placeholder on the top.
How can I put it there?
I am using Angular Materials.
<div class='grid-container-left'>
<mat-form-field appearance="outline">
<input matInput placeholder={{angeforderteVerfueger.request.vorname}} readonly>
</mat-form-field>
</div>
Thank you in advance!
Answer:
The below provided solutions works, just have to add floatLabel value with always.
<mat-form-field appearance="outline" floatLabel="always">
<mat-label>Vorname</mat-label>
<input matInput [placeholder]="angeforderteVerfueger.request.vorname" readonly>
</mat-form-field>
Try to add a label before your input:
<mat-label>Vorname</mat-label>
What you are doing is binding a property using string interpolation which is the wrong syntax. Try this
<div class='grid-container-left'>
<mat-form-field appearance="outline">
<mat-label>Vorname</mat-label>
<input matInput [placeholder]="angeforderteVerfueger.request.vorname" readonly>
</mat-form-field>
</div>

Angular 8 Material mat-error shows up on load by default

I am having this weird scenario. my mat-error shows up by default, without even touching the input field. Basically, when I load the page the error message is shown without any user interaction on the input field. I don't have any error messages on the browser console. Can anyone point out what did I miss?
This is my .ts
frmStep3: FormGroup;
matcher = new MyErrorStateMatcher();
// omited unrelated code
this.frmStep3 = this.fb.group({
courseDurationNum: [, [Validators.required, Validators.maxLength(3), Validators.minLength(1)]]
});
return {
frmStep3: this.frmStep3
};
This is my HTML
<form class="form-group-parent p-0" [formGroup]="frmStep3" (ngSubmit)="stepForward()">
<div class="form-group d-inline-block">
<label class="w-100">Choose your course duration<sup>*</sup></label>
<input [errorStateMatcher]="matcher" [maxLength]="3" type="text" class="float-left" matInput
formControlName="courseDurationNum" placeholder="Ex. 1 - 365 Day(s)" />
<mat-error class="w-100 float-left"
*ngIf="frmStep3.get('courseDurationNum').hasError('required') || frmStep3.get('courseDurationNum').invalid">
<strong>Duration</strong> must be between <strong>1 - 365</strong> day(s).
</mat-error>
</div>
</form>
I figured out, I missed wrapping them in from controls in <mat-form-field> tag. Correct code below.
<form class="form-group-parent p-0" [formGroup]="frmStep3" (ngSubmit)="stepForward()">
<div class="form-group d-inline-block">
<mat-form-field>
<label class="w-100">Choose your course duration<sup>*</sup></label>
<input [errorStateMatcher]="matcher" [maxLength]="3" type="text" class="float-left" matInput
formControlName="courseDurationNum" placeholder="Ex. 1 - 365 Day(s)" />
<mat-error class="w-100 float-left"
*ngIf="frmStep3.get('courseDurationNum').hasError('required') || frmStep3.get('courseDurationNum').invalid">
<strong>Duration</strong> must be between <strong>1 - 365</strong> day(s).
</mat-error>
</mat-form-field>
</div>
</form>

Displaying the data including the HTML Tags from firestore saved using Angular 5 Quill WYIWYG

I saved the data in Firestore in Angular 5 using Quill WYIWYG Editor. When I retrieved It is displaying including the HTML tags instead of not executing the the HTML tags.
quill editor code(form):
<mat-card>
<mat-form-field class="input">
<input [(ngModel)]="title" matInput placeholder="Title">
</mat-form-field><br>
<mat-form-field class="input">
<input [(ngModel)]="name" matInput placeholder="Enter your name or
penName">
</mat-form-field><br>
<quill-editor [(ngModel)]="article" placeholder="Write Anything!">
</quill-editor><br>
<button (click)="post()" class="post-btn" mat-raised-button color="warn"
type="submit" name="post">Post
</button>
</mat-card>
Datatype I Declared
export interface allarticle{
id ?: number;
title ?: string;
name ?: string;
article ?: any;
}
Html Document where I displayed
<div *ngFor="let article of articles | async" class="article-parent">
<mat-card class="article-card">
<h1>{{article.title}}</h1>
<code>{{article.article}}</code>
<span class="Author">By {{article.name}}</span>
</mat-card>
</div>