Can I use [formControl] without also needing [formGroup]? - javascript

I am using a dropdown in my UI, but when I come to use [formControl] I am getting the error thrown:
Cannot find control with unspecified name attribute
I am using ReactiveFormsModule in my app.module.ts.
I have Google'd and found that a solution is to use [formGroup] in the parent div, but I'm unsure of how to implement properly as I am defining my formControl from within a subscribe.
myComp.component.html
<div class="exceptional-status">
<select #exceptionalSelect id="excep-status-dropdown" [formControl]="select">
<option disabled value="default">--Exceptional statuses--</option>
<!-- other options here with *ngFor -->
</select>
</div>
myComp.component.ts
select: FormControl;
mySubscription() {
this.myService.myFunct.subscribe(res => {
this.select = new FormControl(res.status)
});
}

Yes, you can use FormControlDirective without FormGroup.
Angular expects FormControl to be defined:
select = new FormControl();
mySubscription() {
this.myService.myFunct.subscribe(res => {
this.select.setValue(res.status)
});
}

Yes, you can use it without FormGroup.
select = new FormControl();
For set the value:-
select.setValue('your data here');
To get the value:-
select.value

Why would you define the form control within the subscribe? My advice would be to structure the form skeleton outside the subscription and just populate the control using
mySubscription() {
this.myService.myFunct.subscribe(res => {
this.controls['select'].patchValue(res.status);
});
}

Related

Dynamic Angular Forms on User Interaction

I have a dropdown that the user uses to switch between different Angular forms.
HTML
<form [formGroup]="evalForm">
<mat-form-field appearance="fill">
<mat-label>Select Analysis Group</mat-label>
<mat-select (selectionChange)="groupSelected($event)">
<mat-option *ngFor="let item of groupDropdown | async" [value]="item.value">{{item.text}}</mat-option>
</mat-select>
</mat-form-field>
<ng-container *ngFor="let groups of groupSections;">
<input type="text" [formControlName]="groups?.name">
</ng-container>
</form>
What should the Angular look like to get the reactive forms working when someone selects an item in the dropdown?
What groupSelected() currently does (basic non-working pseudo-code):
groupSelected(selectedItem) {
// 1) Grabs the FormControlNames from the DB
const formControlNamesList = getItemsFromDB(selectedItem);
// 2) Sets FormControlNames for DOM
// [{groups:{name: 'keyOne'}}, {groups:{name: 'keyTwo'}},{groups:{name: 'keyThree'}}]
this.groupSections = setGroupSections(formControlNamesList);
// 3) Creates a formControlNamesList Object in the form
// {"keyOne": FormControl, "keyTwo": FormControl, "keyThree": FormControl}
const formObj = formatForAngularForms(formControlNamesList);
// 4) Set the Reactive Form
this.evalForm = this.fb.group(new FormGroup(formObj));
}
formatForAngularForms(formControlNamesList) {
const formObj = {};
for(let i=0;i<formControlNamesList.length;i++) {
formObj[formControlNamesList[i].name] = new FormControl(null,[Validators.required]);
}
return formObj;
}
At the moment, the console explodes with errors about how it can't find the FormControlName and I suspect it has to do with the timing of when everything gets rendered, but I'm not entirely sure if that's it or not. I need the forms to render completely new forms each time a new value selected from the dropdown. The UI will always be dynamic and each time the dropdown changes the input FormControlNames could be different based on what the DB sends.
I've read that maybe FormArray might be something use but I can't find an example anywhere that matches what I'm looking to do.
Shouldn't you wait for the answers from backend before proceeding to build the form? If getItemsFromDB() returns an observable you could do async/await:
async groupSelected(selectedItem) {
const formControlNamesList = await getItemsFromDB(selectedItem);

Select/Unselect and get values of dynamically generated checkbox using ngModel directive in Angular

I have a set of data response from an API and dynamically generating checkboxes on my HTML file using DataView component of PrimeNG.
The goal is to have a functionality where I can select/unselect all checkbox via button click and store their values in an array, for example.
Here's what I have so far;
Component HTML
<p-dataView [value]="requestList" {...} >
<ng-template let-request pTemplate="listItem">
<p-checkbox
name="reqNo"
inputId="reqNo"
(click)="getCheckBoxValue()"
value="{{ request.requestNo }}"
[(ngModel)]="reqNo"
[ngModelOptions]="{ standalone: true }"
></p-checkbox>
</ng-template>
</p-dataview>
Main TS File
reqNo: any; reqNo is binded using ngModel.
Giving me arrays of values when consoled;
['R08000036', 'R08000002']
Each object in the API response looks like this;
{
requestNo: "R08000036",
requestBy: "SUPER_USER",
requestTs: "2021-02-18T04:27:05.710+0000",
collectTs: "2008-07-30T16:00:00.000+0000",
testReason: "After Visit",
noOfPrisoner: 2,
status: "Printed",
printTs: "2008-07-21T16:00:00.000+0000",
escortingOfficer: "00552",
}
getCheckBoxValue event handler;
getCheckBoxValue() {
console.log(this.reqNo);
}
I'm new to Angular and I think I'm using the ngModel wrong. Can someone please teach me?
You can select all values by setting a new value for reqNo by values from requestList.
selectAll() {
this.reqNo = this.requestList.map(item => item.requestNo);
}
unselectAll() {
this.reqNo = [];
}
Example

angular5:ngFor works only in second button click

My scenario as follows
1) When the user enters a keyword in a text field and clicks on the search icon it will initiate an HTTP request to get the data.
2)Data is rendered in HTML with ngFor
The problem is on the first click the data is not rendered in HTML but I am getting the HTTP response properly, and the data rendered only on second click.
component.ts
export class CommerceComponent implements OnInit {
private dealList = [];
//trigger on search icon click
startSearch(){
//http service call
this.getDeals();
}
getDeals(){
this.gatewayService.searchDeals(this.searchParams).subscribe(
(data:any)=>{
this.dealList = data.result;
console.log("Deal list",this.dealList);
},
(error)=>{
console.log("Error getting deal list",error);
this.dealList = [];
alert('No deals found');
}
);
}
}
Service.ts
searchDeals(data){
var fd = new FormData();
fd.append('token',this.cookieService.get('token'));
fd.append('search',data.keyword);
return this.http.post(config.url+'hyperledger/queryByParams',fd);
}
HTML
//this list render only on second click
<div class="deal1" *ngFor="let deal of dealList">
{{deal}}
</div>
UPDATE
click bind html code
<div class="search-input">
<input type="text" [(ngModel)]="searchParams.keyword" class="search" placeholder="" autofocus>
<div class="search-icon" (click)="startSearch()">
<img src="assets/images/search.png">
</div>
</div>
According to Angular official tutorial, you could have problems if you bind a private property to a template:
Angular only binds to public component properties.
Probably, setting the property dealList to public will solve the problem.
Remove "private" from your dealList variable. That declaration makes your component variable available only during compile time.
Another problem: you are implementing OnInit in yout component but you are not using ngOnInit. Angular is suposed to throw an error in this situation.
My suggestion is to switch to observable:
I marked my changes with CHANGE
component.ts
// CHANGE
import { Observable } from 'rxjs/Observable';
// MISSING IMPORT
import { of } from 'rxjs/observable/of';
export class CommerceComponent implements OnInit {
// CHANGE
private dealList: Observable<any[]>; // you should replace any with your object type, eg. string, User or whatever
//trigger on search icon click
startSearch() {
//http service call
this.getDeals();
}
getDeals() {
this.gatewayService.searchDeals(this.searchParams).subscribe(
(data:any)=>{
// CHANGE
this.dealList = of(data.result);
console.log("Deal list",this.dealList);
},
(error)=>{
console.log("Error getting deal list",error);
// CHANGE
this.dealList = of([]);
alert('No deals found');
}
);
}
}
HTML
<!-- CHANGE -->
<div class="deal1" *ngFor="let (deal | async) of dealList">
{{deal}}
</div>
Try this:
this.dealList = Object.assign({},data.result);
Better do this inside the service.
By default, the angular engine renders the view only when it recognizes a change in data.

what is the equevalant for getelementbyid in angular 2 [duplicate]

I have a code:
document.getElementById('loginInput').value = '123';
But while compiling the code I receive following error:
Property value does not exist on type HTMLElement.
I have declared a var: value: string;.
How can I avoid this error?
Thank you.
if you want to set value than you can do the same in some function on click or on some event fire.
also you can get value using ViewChild using local variable like this
<input type='text' id='loginInput' #abc/>
and get value like this
this.abc.nativeElement.value
here is working example
Update
okay got it , you have to use ngAfterViewInit method of angualr2 for the same like this
ngAfterViewInit(){
document.getElementById('loginInput').value = '123344565';
}
ngAfterViewInit will not throw any error because it will render after template loading
(<HTMLInputElement>document.getElementById('loginInput')).value = '123';
Angular cannot take HTML elements directly thereby you need to specify the element type by binding the above generic to it.
UPDATE::
This can also be done using ViewChild with #localvariable as shown here, as mentioned in here
<textarea #someVar id="tasknote"
name="tasknote"
[(ngModel)]="taskNote"
placeholder="{{ notePlaceholder }}"
style="background-color: pink"
(blur)="updateNote() ; noteEditMode = false " (click)="noteEditMode = false"> {{ todo.note }}
</textarea>
import {ElementRef,Renderer2} from '#angular/core';
#ViewChild('someVar') el:ElementRef;
constructor(private rd: Renderer2) {}
ngAfterViewInit() {
console.log(this.rd);
this.el.nativeElement.focus(); //<<<=====same as oldest way
}
A different approach, i.e: You could just do it 'the Angular way' and use ngModel and skip document.getElementById('loginInput').value = '123'; altogether. Instead:
<input type="text" [(ngModel)]="username"/>
<input type="text" [(ngModel)]="password"/>
and in your component you give these values:
username: 'whatever'
password: 'whatever'
this will preset the username and password upon navigating to page.
Complate Angular Way ( Set/Get value by Id ):
// In Html tag
<button (click) ="setValue()">Set Value</button>
<input type="text" #userNameId />
// In component .ts File
export class testUserClass {
#ViewChild('userNameId') userNameId: ElementRef;
ngAfterViewInit(){
console.log(this.userNameId.nativeElement.value );
}
setValue(){
this.userNameId.nativeElement.value = "Sample user Name";
}
}

Angular 2 - Kendo UI Dropdown default value

I'm trying to create a dropdownlist using Kendo UI, it's working great except for having a default selected value when the screen loads.
according to their documentation my code should look like this:
HTML:
<kendo-dropdownlist formControlName="description"
[data]="definitionData.Languages"
[(ngModel)]="languageValue"
[textField]="'Value'"
[valueField]="'Key'"
[value]="2"
[valuePrimitive]="true">
</kendo-dropdownlist>
<span class="left col-xs-6">
<input type="text" id="descriptionField" class="form-control" [value]="getValue(descriptionForm.controls.description.value)" #descriptionField (blur)="updateDescriptionValue(descriptionField.value, languageValue)" />
</span>
COMPONENT:
public descriptionForm: FormGroup = new FormGroup({
description: new FormControl()
});
My dropdown works, but the default selected value is blank when I load the page, and it should be the object with Key: 2
note: I don't want to use [defaultItem] since It will just duplicate the item, meaning it will be in the dropdown list 2 times.
I've tried numerous things, but I can't figure out what I should do!
Thanks in advance
You should choose between value and ngModel binding. From documentation:
The DropDownList does not support the simultaneous usage of the value property and the ngModel value binding.
Solution with value property:
Delete ngModel from HTML.
Bind to valueChange event and set value in your model.
HTML:
<kendo-dropdownlist formControlName="description"
[data]="definitionData.Languages"
(valueChange)="handleValue($event)"
[textField]="'Value'"
[valueField]="'Key'"
[value]="2"
[valuePrimitive]="true">
</kendo-dropdownlist>
COMPONENT:
handleValue(value) {
this.languageValue = value;
}
Solution with ngModel property:
Delete value from HTML.
Set default value in your model.
HTML:
<kendo-dropdownlist formControlName="description"
[data]="definitionData.Languages"
[(ngModel)]="languageValue"
[textField]="'Value'"
[valueField]="'Key'"
[valuePrimitive]="true">
</kendo-dropdownlist>
COMPONENT:
constructor(){
this.languageValue = 2;
}

Categories

Resources