Read span value in angular reactive forms - angular7

<span class="headmain" style="cursor:pointer" (click)="modalshow(ratingImage,el.name+i,el)" >
hello</span>
How to get the value from span element in similar to formControlName?
Span element is in a form tag.

You can use #ViewChild / ElementRef.
HTML:
<span #mySpan (click)="handleClick()">
Hallo from span.
</span>
TS:
export class AppComponent {
#ViewChild("mySpan", { static: false }) mySpanRef: ElementRef;
public handleClick(): void {
const spanValue = this.mySpanRef.nativeElement.innerHTML;
console.log("Span-Value: ", spanValue);
}
}
Working stackblitz

Related

React ant design typescript form validation not working

I used react typescript project to ant design and i used this ant design form validation, but its not working correctly, anyone know how to fix that issue?
Thanks
git a this error
index.tsx?789d:32 Uncaught TypeError: Cannot read property
'getFieldDecorator' of undefined
import * as React from "react";
import { Input, Form, Icon, Button, } from "antd";
import 'antd/dist/antd.css';
import "./style.css";
export class Registerform extends React.Component<any> {
handleSubmit = (e:any) => {
e.preventDefault();
this.props.form.validateFieldsAndScroll((err:any, values:any) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
};
render() {
const { getFieldDecorator } = this.props.form;
return (
/* Start add bulk upload form*/
<div className="remindersform-section">
<Form onSubmit={this.handleSubmit}>
<Form.Item
label={
<span>
Nickname
<Icon type="question-circle-o" />
</span>
}
>
{getFieldDecorator('nickname', {
rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }],
})(<Input />)}
</Form.Item>
<Form.Item >
<Button type="primary" htmlType="submit">
Register
</Button>
</Form.Item>
</Form>
</div>
);
}
}
Because default form props are not assigned with their types, antd provided a solution for it, Try like below
import * as React from "react";
import { FormComponentProps } from "antd/es/form";
import { Input, Form, Icon, Button, } from "antd";
import 'antd/dist/antd.css';
interface UserFormProps extends FormComponentProps {
form: any;
}
class Registerform extends React.Component<UserFormProps> {
handleSubmit = (e:any) => {
e.preventDefault();
this.props.form.validateFieldsAndScroll((err:any, values:any) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
};
render() {
const { getFieldDecorator } = this.props.form;
return (
/* Start add bulk upload form*/
<div className="remindersform-section">
<Form onSubmit={this.handleSubmit}>
<Form.Item
label={
<span>
Nickname
<Icon type="question-circle-o" />
</span>
}
>
{getFieldDecorator('nickname', {
rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }],
})(<Input />)}
</Form.Item>
<Form.Item >
<Button type="primary" htmlType="submit">
Register
</Button>
</Form.Item>
</Form>
</div>
);
}
}
const WrappedForm = Form.create<UserFormProps>({})(Registerform);
export default WrappedForm;

Fill a Form with controls (according to the data returned from service)

I am trying to migrate Model-driven-form to Reactive-form.
This is a dynamic form that gets filled according to data from getCommandPacket
this.renderSvc.getCommandPacket - is getting the data from the server, this is the function signature:
Server
..
[HttpGet("[action]")]
public Dictionary<string, string> GetCommandPacket(int ID){
..
}
..
Html
<form>
<div *ngFor="let key of commandKeys">
<span class="ui-float-label">
<textarea [id]="key" [name]="key" pInputTextArea [(ngModel)]="commandPacket[key]" style="width: 40em;"></textarea>
<label [for]="key">{{ key }}</label>
</span>
</div>
<p-button label="Add Field"></p-button>
<button p-button type="submit" icon="fa fa-angle-right" iconPos="right">
<span class="ui-button-text ui-clickable">Re-Submit</span>
</button>
</form>
TS
...
export class CommandPacketDetailsComponent implements OnInit {
#Input() id: number;
myForm: FormGroup;
constructor(private renderSvc: PfServerService, private fb: FormBuilder) {
}
commandPacket: { [key: string]: string; };
commandKeys: string[];
message: string = null;
ngOnInit() {
if (this.id !== 0 && typeof this.id !== "undefined")
this.getCommandPacket(this.id);
else
this.message = "No ID Given for Packet";
}
getCommandPacket(id: number) {
this.renderSvc.getCommandPacket(id).subscribe(data => {
this.commandPacket = data;
this.commandKeys = Object.keys(this.commandPacket);
});
}
...
how can I achieve the same result but in Reactive-form way?
You want to use FormArray. Declare form and within it, declare formArray. Then when you get your data from service, create as many formControls as you have results and add them to FormArray.
you have an example here:
https://angular.io/guide/reactive-forms#use-formarray-to-present-an-array-of-formgroups
form type:
yourForm:FormGroup;
form definition:
this.yourForm = this.fb.group({
yourFormArray: this.fb.array([])
});
make a getter for your formArray:
get yourFormArray(): FormArray {
return this.cpForm.get('commands') as FormArray;
}
and then once you get your data from server:
this.yourFormArray.reset();
this.commandKeys.forEach(val =>
this.yourFormArray.push(this.fb.group({ command: [''] }))
);
that will create as many command (without s) formGroups (having only one input field) as you have keys in your commandKeys result.
PS.
once you set that up you can use patchValue on formArray to fill it with actual values. something like:
this.myFormArray.patchValue(commandKeys.map(key => ({ command: key })));
PS2.
to clear form controls from formarray, you can use function like this:
//Clear formArray
clearItemsFormArray() {
while (this.yourFormArray.length > 0)
this.yourFormArray.removeAt(0);
}
yourFormArray is the one coming from getter.

Programmatically open an N level self nested mat-menu component with matMenuTrigger openMenu

I am trying to make a self nested component that uses Angular Material mat-menu. I have a flyoutcomponent that is a wrapper for flyout-menu-item component, that will have a button as a matMenuTrigger for the nested component that will appear as many levels as the FeatureInput.FeatureChoices dictates. FeatureInput is an object that has FeatureChoices that may or may not contain other featurechoices etc N levels deep. Below code does not compile but it should demonstrate what I am doing. Basically I have flyout menu component as a input to a form and I am trying to load a stored answer on a form rather than select new, which I can do easily using the nested component. The desired behavior is that if the user clicks top matMenuTrigger button to open the top menu that it would expand all child menus to the menu item that matches with the FeatureInput.FeatureValue and sets the menu item _highlighted to true. I am using the menuOpen input parameter and ngChanges successfully to find the match(with I a setTimeout which cannot be right). Basically when I console.log this.trigger it is undefined. Ideally in the ngOnChange to the openMenu I would go through all menus and call openMenu on all the triggers but I cannot get access to the matMenuTrigger with ViewChild as the docs say. I get undefined. *-( All help welcome please and thanks.
Here is flyout template component.
<div>
<buttonmat-button [matMenuTriggerFor]="menu.childMenu"
(onMenuOpen)="onMenuOpen()"
(onMenuClose)="onMenuClose()">
<span [innerHTML]="featureInput.Text"></span>
</button>
<app-flyout-menu-item #menu
[featureChoicesObject]="featureInput.FeatureChoices"></app-flyout-menu-item>
</div>
And here is its .ts
import { Component, OnInit, Input, ViewChild } from '#angular/core';
import { MatMenuTrigger } from '#angular/material';
#Component({
selector: 'app-flyout',
templateUrl: './flyout.component.html',
styleUrls: ['./flyout.component.scss']
})
export class FlyoutComponent implements OnInit {
#Input() featureInput: FeatureInput
constructor() { }
ngOnInit() {
}
onMenuOpen() {
this.menuOpen = true;
}
onMenuClose() {
this.menuOpen = false;
}
}
And here is flyout-menu-item template
<mat-menu #childMenu="matMenu" [overlapTrigger]="false">
<span *ngFor="let featureChoice of featureChoices">
<span>
<button mat-menu-item [matMenuTriggerFor]="menu.childMenu">
<span [innerHTML]="featureChoice.Text"></span>
</button>
<app-flyout-menu-item #menu
[menuOpen]="menuOpen"
[featureInput]="featureInput"
[featureChoicesObject]="featureChoice.FeatureChoices"
(onOptionSelected)="someService.SomeMethod($event)"></app-flyout-menu-item>
</span>
<span *ngIf="!featureChoice.FeatureChoices">
<button mat-menu-item (click)="selectOption(featureChoice.ID)" [innerHTML]="featureChoice.Text" value="{{featureChoice.ID}}"></button>
</span>
</span>
</mat-menu>
And here is its .ts
import { Component, OnInit, Input, Output, ViewChild, EventEmitter, OnChanges, SimpleChanges } from '#angular/core';
import { MatMenuTrigger } from '#angular/material';
import { FeatureChoice } from 'app/model/feature-choice';
import { FeatureInput } from 'app/model/feature-input';
#Component({
selector: 'app-flyout-menu-item',
templateUrl: './flyout-menu-item.component.html',
styleUrls: ['./flyout-menu-item.component.scss']
})
export class FlyoutMenuItemComponent implements OnInit{
#ViewChild('menu') public menu;
#ViewChild('childMenu') public childMenu;
#ViewChild(MatMenuTrigger) public trigger: MatMenuTrigger;
#Input() featureInput: FeatureInput;
#Input() featureChoicesObject: FeatureChoice;
#Output() onOptionSelected: EventEmitter<FeatureInput> = new EventEmitter<FeatureInput>();
constructor(public solutionDataService: SolutionDataService) { }
ngOnInit() {
console.log(this.trigger);
}
ngOnChanges(simpleChanges: SimpleChanges) {
if (simpleChanges.menuOpen && simpleChanges.menuOpen.currentValue) {
setTimeout(() => {
// console.log(this.menu);
const itemsArray = this.childMenu.items.toArray();
for (let x = 0; x < itemsArray.length; x++) {
const menuItem = itemsArray[x];
if (this.featureInput.FeatureValue !== '' && menuItem._elementRef.nativeElement.value === this.featureInput.FeatureValue) {
menuItem._highlighted = true;
}
}
}, 1);
}
}
}
this.menuOpen = true;
Perhaps add menuOpen: boolean = false as an attribute at the top of your FlyoutComponent. I don't know where the value of menuOpen is saved.
the menuOpen property relates to the matMenuTrigger.
here's an example:
<button [ngClass]="{'active-icon': trigger.menuOpen}" type="button" mat-
icon-button #trigger="matMenuTrigger" [matMenuTriggerFor]="help">
<mat-icon></mat-icon>
</button>
<mat-menu #help="matMenu">
<div> textId </div>
</mat-menu>

Angular2: get value of input in dynamic form

I need to get value of a certain input in my dynamic form.
I have JSON parameters like this
{
"etiquette":"Téléphone mobile",
"ordre":1,
"obligatoire":true,
"pattern":"^(?:(?:(?:\\\\+|00)33[ ]?(?:\\\\(0\\\\)[ ]?)?)|0){1}[1-9]{1}([ .-]?)(?:\\\\d{2}\\\\1?){3}\\\\d{2}$",
"section":"TelMail",
"type":"text",
"nom":"telephoneMobile",
"texteIndice":"Téléphone mobile"
}
,
{
"etiquette":"Mail",
"ordre":2,
"obligatoire":true,
"pattern":"(?:[a-zA-Z0-9!#$%&''*+=?^_`{|}~-]+(?:\\\\.[a-zA-Z0-9!#$%&''*+=?^_`{|}~-]+)*|\u201d(?:[\\\\x01-\\\\x08\\\\x0b\\\\x0c\\\\x0e-\\\\x1f\\\\x21\\\\x23-\\\\x5b\\\\x5d-\\\\x7f]|\\\\\\\\[\\\\x01-\\\\x09\\\\x0b\\\\x0c\\\\x0e-\\\\x7f])*\u201d)#(?:(?:[a-zA-Z0-9àâäçéèëêîïôôûüù](?:[a-zA-Z0-9-àâäçéèëêîïôôûüù]*[a-zA-Z0-9àâäçéèëêîïôôûüù])?\\\\.)+[a-zA-Z0-9àâäçéèëêîïôôûüù](?:[a-zA-Z0-9-àâäçéèëêîïôôûüù]*[a-zA-Z0-9àâäçéèëêîïôôûüù])?|\\\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]:(?:[\\\\x01-\\\\x08\\\\x0b\\\\x0c\\\\x0e-\\\\x1f\\\\x21-\\\\x5a\\\\x53-\\\\x7f]|\\\\\\\\[\\\\x01-\\\\x09\\\\x0b\\\\x0c\\\\x0e-\\\\x7f])+)\\\\])",
"section":"TelMail",
"type":"email",
"nom":"mail",
"texteIndice":"Mail"
},
{
"etiquette":"Type alerte",
"ordre":3,
"obligatoire":true,
"options": [{
"code" : "SMS",
"valeur" : "Alertes SMS"
},
{
"code" : "MAIL",
"valeur" : "Alertes Mail"
}],
"section":"Alerte",
"type":"radio",
"nom":"typeAlerte",
"texteIndice":"Type alerte"
}
I have Interface like below screenshot.
I customised radio component to contain radio input and input for mail and phone.
What I need is: get value of input phone/mail and put in in the input related to the radio.
Here is my radio component:
import {Component,Inject,Input} from '#angular/core';
import {NgForm, FormGroup} from '#angular/forms';
import {ExtraFormField} from '../model/form';
import {ExtraField} from './extra-field';
import { CatalogueService } from "../../catalogue/catalogue.service";
#Component({
selector: 'radio-extra-field',
template:`
<div class="form-group" >
<label [attr.for]="field.nom">{{field.etiquette}}</label>
<div *ngFor="let option of field.options" >
<input type="radio" name ="{{field.nom}}" value="{{option.code}}" id="{{option.code}}" [(ngModel)]="typeSelectionne">{{option.valeur}}
<input id="{{option.code}}" [attr.title]="field.etiquette" [attr.minlength]="field.longueurMin" [attr.min]="field.min" [attr.max]="field.max"
[attr.maxlength]="field.longueurMax" [attr.value]="field.valeur"
[attr.type]="text" [formControl]="fieldControl" (change)="maj(id.value)"
[attr.id]="option.code" type="text" [attr.disabled]="typeSelectionne != option.code? disabled : null ">
<error-messages [control]="field.nom"></error-messages>
</div>
<error-messages [control]="field.nom"></error-messages>
</div>
`
})
export class RadioExtraField extends ExtraField {
typeSelectionne: string;
#Input() field:ExtraFormField;
#Input() entity:{fields:Object};
#Input() formGroup:FormGroup;
constructor(public catalogueService: CatalogueService, #Inject(NgForm) formDir: NgForm) {
super(null, catalogueService, formDir);
}
get disabled():string {
if(this.field) {
return 'disabled';
}
return null;
}
}
Is there a way to do this ?
thank you
You can use
(ngModelChange)
what it do is it will call function ,there you can assign value to ngModel of input field.

Trying to get a dom-if to work

I am trying to remove and add a template content using the a paper-toggle-button. However, I am not able to see the contents of the template even though the toggle is occurring.
The code used is shown below:
.dart
PolymerRegister( 'test-if-form' )
class TestIfForm extends PolymerElement {
TestIfForm.created( ) : super.created( );
#Property( observer: 'togglerChanged' )
//#property
bool toggler = false;
#reflectable
void toggleNormalChangedEvent( event, [_] ) {
toggler = !toggler;
print( 'toggler $toggler' );
}
#reflectable
void togglerChanged( newValue, oldValue ) {
print( 'new value $newValue' );
if ( newValue ) {
toggler = !toggler;
}
}
}
.html
<dom-module id = "test-if-form">
<template>
<div
class = "layout horizontal center-justified"
id = "normal-changed-toggler-container">
<span class = "margin-rt-5px">Normal</span>
<paper-toggle-button
required
on-change = "toggleNormalChangedEvent"
id = "togglerBtn">Changed
</paper-toggle-button>
<div>
<hr>
</div>
</div>
<br>
<template is = "dom-if"
if = "{{toggler}}"
restamp>
<div>I am now visible</div>
</template>
</template>
</dom-module>
The contents of the nested div element is never visible. It does not seem that the if is being called?
Thanks for your help.
toggler = !toggler;
should be
set('toggler', !toggler);

Resources