Drop Down List in Angular 2 Model Driven Form - html

I have a model driven form in which I would like to add a drop down list containing several options. The options are from a pre-fetched list, and the model of the form is injected to the component. The form is loaded correctly: All the fields from the model are filled properly, abd the list of options for the drop-down list is also loaded correctly.
The only problem is that I can't set the selected value of the list, and it appears with an empty value at first.
Here, I am loading the HMOs list, then loading the patient, and only then creating the form.
All the values of the form (name, id, etc. which are omitted here) are loaded correctly into the form.
The drop-down list in the form is filled correctly: All HMOs are populating the list.
Still, the selected value in the list is missing, and the lost is loaded with no initial value.
For debugging purposes, I have replaced the boolean condition in the option tag: patient.hmo.uid == hmo.uid to a function call: isSelected(hmo).
This function essentially does the same comparison and returns its value, but first logs it. Indeed I see that the option with the correct hmo gets a true value and all other options get false values, meaning all data is loaded correctly.
Also, when I set the [selected]="true" (always to be true), I do see the change affects: The last option is selected (the default in HTML).
So where am I wrong? How should I set the selected option correctly?
Code for the component (all fields except HMO are omitted):
import {Component, Input, Inject, OnInit} from "#angular/core";
import {
FormGroup,
FormControl,
REACTIVE_FORM_DIRECTIVES,
Validators,
FormBuilder,
FormArray
} from "#angular/forms";
import {Patient} from "./patient";
import {PatientsService} from "./patients.service";
import {Hmo} from "../hmos/hmo";
import {HmosService} from "../hmos/hmos.service";
import {Doctor} from "../doctors/doctor";
import {DoctorsService} from "../doctors/doctors.service";
import {Router, ActivatedRoute} from "#angular/router";
import {Subscription} from "rxjs/Rx";
import {Response} from "#angular/http";
import {JavaDate} from "./java-date";
#Component({
moduleId: module.id,
selector: 'gy-patient-edit',
templateUrl: 'patient-edit.component.html',
directives: [REACTIVE_FORM_DIRECTIVES],
})
export class PatientEditComponent implements OnInit {
patientForm: FormGroup;
#Input() patient: Patient;
private hmos: Hmo[];
private patientUid: number;
private showForm: boolean = false;
constructor(#Inject(PatientsService) private patientsService: PatientsService,
#Inject(HmosService) private hmosService: HmosService,
#Inject(ActivatedRoute) private route: ActivatedRoute,
#Inject(FormBuilder) private formBuilder: FormBuilder) {
}
ngOnInit(): any {
this.subscription = this.route.params.subscribe(
(params: any) => {
this.patientUid = params['id']; //Getting the UID from the URL
}
);
this.hmosService.hmosChanged.subscribe(
(hmos: Hmo[]) => {
this.hmos = hmos; //Fetching available HMOs
}
);
this.hmosService.fetchHmos();
this.patientsService.fetchPatient(this.patientUid) //Fetching the Patient
.map((response: Response) => response.json())
.subscribe((data: Patient) => {
this.patient = data;
this.restartForm(); //Only required so the form will ne initialized only after the patient is received from the server
});
}
restartForm(){
this.patientForm = this.formBuilder.group({
hmo: [this.patient.hmo]]
});
this.showForm = true;
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Code for the HTML form:
<div class="row" *ngIf="showForm">
<div class="col-xs-12" *ngIf="showForm">
<form [formGroup]="patientForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="hmo">HMO</label>
<select formControlName="hmo" id="hmo">
<option *ngFor="let hmo of hmos"
[value]="hmo.uid" [selected]="patient.hmo.uid == hmo.uid">
{{hmo.name}}
</option>
</select>
</form>
</div>
</div>
Code for Patient:
import {Hmo} from "../hmos/hmo";
export class Patient {
constructor(public hmo: Hmo) {
}
}
Code for Hmo:
export class Hmo{
constructor(public uid: number, public name: string){}
}

The selected option is calculated by comparing the <option>'s value with <select>'s value. In light of that, to mark an <option> as selected, we need to make sure the wrapping <select> is containing the same value, which in turn requires the correct value of the corresponding form control in your model.
Your code can be slightly modified as follow:
restartForm(){
this.patientForm = this.formBuilder.group({
hmo: [this.patient.hmo.uid]
});
this.showForm = true;
}
And Template:
<select formControlName="hmo" id="hmo">
<option *ngFor="let hmo of hmos"
[value]="hmo.uid">
{{hmo.name}}
</option>
</select>

Related

Refresh datalist in view after user finishes typing in textbox Angular 7

I am trying to refresh a datalist in the view after waiting for user to finish typing in the textbox and updating results. Tried with angular directives, tried with Observable and various timeouts and debounces and no luck. I ran out of options.
In the html file:
<input type="text" class="form-control" id="Other"
(keyup)="onKeySearch($event)" list="dynamicList" formControlName="Other"/>
<datalist id="dynamicList">
<option *ngFor="let employee of employeesList" [value]="employee.Name">
{{employee.Name}}</option>
</datalist>
in the .ts file:
public employeesList: EmployeeData[] = [];
timeout: any = null;
getEmployeesList(name : string) {
let empList: EmployeeData[] = [];
// get employees list from service
this.employeeService.getEmployeesList(name).subscribe((data: any) => {
empList = data;
console.log(empList)
})
return empList;
}
public onKeySearch(event: any) {
let empListt: EmployeeData[] = [];
clearTimeout(this.timeout);
var $this = this;
this.timeout = setTimeout(() => {
empListt = $this.getEmployeesList(event.target.value);
console.log(empListt)
}, 1000);
this.employeesList = empListt;
}
The problem is that the datalist is not updates after retrieving the data an populating the list. After it exists the method the list is again empty, thus no data to display.
I have added stackblitz example code with similar code as above (same behavior):
.ts file:
import { Component, VERSION, OnInit } from "#angular/core";
import { FormControl } from "#angular/forms";
import { distinctUntilChanged, debounceTime, tap } from "rxjs/operators";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
searchControl = new FormControl("");
message = "";
public employeesList: string[] = [];
ngOnInit() {
this.searchControl.valueChanges
.pipe(
tap(() => (this.message = "User is typing...")),
distinctUntilChanged(),
debounceTime(1000)
)
.subscribe(res => {
this.message = "User finished typing!";
this.employeesList.push('1');
this.employeesList.push('2');
this.employeesList.push('3');
});
}
}
.html file:
<input [formControl]="searchControl" list="dynamicList">
<datalist id="dynamicList">
<option *ngFor="let employee of employeesList">
{{employee}}</option>
</datalist>
<span> {{message}} </span>
The dropdown will be filtered according to the text you have entered. So, in the given example, since you have pushed 1,2 and 3 values into the list, the drop down will only list with the filtered value.
Eg. if you input 1, the drop down list will have 1 (which is the required functionality)
You can see this behavior clearly if you slightly change the test input as :
ngOnInit() {
this.searchControl.valueChanges
.pipe(
tap(() => (this.message = "User is typing...")),
distinctUntilChanged(),
debounceTime(1000)
)
.subscribe(res => {
this.message = "User finished typing!";
this.employeesList.push('Employee 1');
this.employeesList.push('Employee 2');
this.employeesList.push('Employee 3');
});
}
Now, when you search 'Employee', it will list all 3, and if you search 'Employee 1', it will list the required one entry only. (Which is the expected behavior)
Your formControl (called 'Other', for some reason) has an event that is emitted as an Observable on each change. You can subscribe to that event, and use the RxJS operator debounceTime() to specify how many milliseconds should it wait for the user to stop typing until the event is emitted.
I have created a simple DEMO, maybe it's easier to understand.
Good luck!

Not able to set default value of input (Angular)

I have a web page that uses angular form. This page is basically a user data so it shows the name, phone, mail, etc of the user.The data are obtained using a mongoDB database. I want it to show by default the value already set in placeholder and I want for the user to be possible to change it as well. If they don't type anything in the field then the value that should be passed is the placeholder. For example the value {{this.cliente.nome}} when it is in a different field than the input one but when I try to use it as default value of input it doesn't work. It says "Cannot read property 'nome' of undefined" when the other one works. The data comes correct from the mongoDB but it isn't working. This is my code:
import { Component, OnInit } from '#angular/core';
import { UserService } from '../user.service';
import { User } from 'src/user';
import { FormGroup, FormControl, FormArray, FormBuilder } from "#angular/forms";
import { Router } from '#angular/router';
#Component({
selector: 'app-cliente-dados',
templateUrl: './cliente-dados.component.html',
styleUrls: ['./cliente-dados.component.css']
})
export class ClienteDadosComponent implements OnInit {
cliente: User;
updateForm: FormGroup;
constructor(
private userService: UserService,
private formBuilder: FormBuilder,
public router: Router
) {
this.updateForm = this.formBuilder.group({
nomeUpdate: this.formBuilder.control("")
})
}
ngOnInit(): void {
this.getCliente();
}
getCliente(){
this.userService.getUser(localStorage.getItem('userAtual')).subscribe(user => {
this.cliente = user[0];
});
}
updateCliente(updateData){
console.log("update");
}
}
<form [formGroup]="updateForm" (ngSubmit)="updateCliente(updateForm.value)">
<div class="data">
<img id="iconePerfil" src="assets/Imagens/Icones/perfil.png">
<div id="informacaoRegisto">
<div class="container">
<label id="nome"><strong>{{this.cliente.nome}} <!--This one shows the value--></strong></label>
<input class="input" type=" text " [(value)]="this.cliente.nome" formControlName="nomeUpdate"> <!--Here the value isn't shown-->
<span class="border"></span>
</div>
</div>
</div>
<button type="submit" class="submit">Atualizar Dados</button>
</form>
Representation of the issue: top and bottom values are shown(not input field)
Middle one not shown(input field with default value)
You are using FormBuilder and as such the value property of input is not used. The initial value is initialized by the FormControl
Remove the following from the constructor
this.updateForm = this.formBuilder.group({
nomeUpdate: this.formBuilder.control("")
})
Modify getCliente()
getCliente(){
this.userService.getUser(localStorage.getItem('userAtual'))
.pipe(first()).subscribe(user => {
this.cliente = user[0];
this.updateForm = this.formBuilder.group({
nomeUpdate: this.formBuilder.control(this.cliente.nome)
})
});
}
Have added .pipe(first()) so only first value received is used.
Hope it helps!

How to get selected value and selected text of a dropdown on click a button in angular 7 [duplicate]

This question already has answers here:
Get Value From Select Option in Angular 4
(7 answers)
Closed 3 years ago.
I have a dropdown select,Here on click the button I need to console selected text(green..) and selected value(1..) ,I tried with ngmodel but I can able to capture only value and also empty option is showing there in select field.Here is the code below,
home.component.html
<select>
<option *ngFor="let status of statusdata" value="{{status.id}}">{{status.name}}</option>
</select>
<button type="submit" (click)="register()" class="btn btn-primary mr-1">Register</button>
home.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
dotsh:any;
hoverIndex:number = -1;
groups:any;
test:any;
constructor(private formBuilder: FormBuilder) {
}
onHover(i:number){
this.hoverIndex = i;
}
columns = ["name", "Items","status"];
ngOnInit() {
this.test = false;
this.statusdata = [{"id":1,"name":"green"},{"id":2,"name":"red"},{"id":3,"name":"yellow"}];
}
register(){
console.log('selected Value');
console.log('selected name');
}
}
If what you want is to store the whole object as a value when selected, you can try this:
<select [(ngModel)]="value">
<option *ngFor="let status of statusdata" [ngValue]="status">
{{status.name}}</option>
</select>
And then in your component just declare the variable value, and either leave it undefined or assign it to the value you wish (it needs to be the whole object). For instance:
export class HomeComponent implements OnInit {
statusdata = [{"id":1,"name":"green"},{"id":2,"name":"red"},{"id":3,"name":"yellow"}];
value = statusdata[0];
....
That should default to the first option selected. Then if you want to print the value just:
console.log(this.value.id);
console.log(this.value.name);
To achieve expected result, use below option of using Formbuilder, FormGroup and Form Control name in component.html and commponent.ts
component.html
Use form name for your form as [formGroup]="profileForm"
Set formControlName for select i.e formControlName="name"
`component.ts
3. Declare formgroup and using formbuilder initialize name
profileForm: FormGroup
constructor(private formBuilder: FormBuilder) {
this.profileForm = this.formBuilder.group({
name: new FormControl(1)
});
4. On button click get value using this.profileForm.get('name').value
register(){
console.log('selected Value', this.profileForm.get('name').value);
console.log('selected name', this.statusdata.filter(v => v.id == this.profileForm.get('name').value)[0].name);
}
Sample working code for reference - https://stackblitz.com/edit/angular-unnal4?file=src/app/app.component.ts
Please refer this link for more details on reactive forms - https://angular.io/guide/reactive-forms

Passing Parameter to Angular2 Component

I'm Learning Angular2 so be gentle... I have a basic Component which has a string array. I want to pass an integer to this component and have it return the string found at the index of that parameter.
E.g. myComponent[number]=1 returns string "second element".
My code so far is this:
import { Component } from '#angular/core';
#Component({
selector: 'myComponent',
template:
`<h1>Returned Value {{returnedString}}</h1>,`,
inputs:['number']
})
export class MyComponent {
myStringArray: string[];
returnedString: string;
constructor(){
this.myStringArray = ['First','Second','Third','Forth','Fifth','Sixth'];
this.returnedString = 'number'+this.myStringArray['number'.valueOf()];
}
}
I am calling this component as follows
<myComponent [number]=1></myComponent>
I print the value returned to the screen and get 'undefined'.
Any ideas folks?
Since you want to bind to a custom property import Input and OnChanges from core and then implement as Input to create your custom property. The OnChanges just ensures your value gets updated when the bound value changes.
Remove the inputs key from your component decorator
import { Component, Input, OnChanges } from '#angular/core';
#Component({
selector: 'myComponent',
template:
`<h1>Returned Value {{returnedString}}</h1>,`
})
export class MyComponent implements OnChanges {
myStringArray: string[];
returnedString: string;
#Input() inputNumber: number;
constructor(){
this.myStringArray = ['First','Second','Third','Forth','Fifth','Sixth'];
this.returnedString = 'number'+this.myStringArray[Number(this.inputNumber)];
}
ngOnChanges() {
this.returnedString = 'number'+this.myStringArray[Number(this.inputNumber)];
}
}
Update your code usage to the following
<myComponent [inputNumber]="1"></myComponent>
Here is a sample plunker.
https://plnkr.co/edit/S074zoVJ3ktQDKkcQgxe?p=preview
I had tough time to send string inputs. here is the correct way,
<myComponent [stringvar]="'string value'"></myComponent>
"string value" will not work. angular expecting object or number inside double quotes. string should be inside single quotes within double quotes "'string'"
You need to create a number variable in your component too that will hold the value.
import {Component, OnInit} from '#angular/core';
#Component({
selector: 'myComponent',
template:
`<h1>Returned Value {{returnedString}}</h1>,`,
inputs:['myNum']
})
export class MyComponent implements OnInit {
myStringArray: string[] = ['First','Second','Third','Forth','Fifth','Sixth'];
returnedString: string;
public myNum: number; <= here is your variable
ngOnInit() {
//you can use this.myNum anywhere now like this
this.returnedString = 'number '+ this.myStringArray[this.myNum];
}
constructor(){
}
}
You may have to change the name of your input because number is a keyword.
Another Note: You have to use OnInit instead of constructor to start using your inputs. ngOnInit is an Angular2 lifecycle method that is called by Angular when it's done building the component and evaluated the bindings
Here is another alternative. It demonstrates how to use a getter for returnedString. Less code needed than with ngOnChanges.
import { Component, Input } from '#angular/core';
#Component({
selector: 'my-cmp',
template: `
<p>returnedString = {{ returnedString }}</p>
`
})
export class MyComponent {
myStringArray: string[] = ['First','Second','Third','Forth','Fifth','Sixth'];
#Input() stringIndex: number;
get returnedString(): string {
if (this.stringIndex !== undefined) {
return this.myStringArray[this.stringIndex];
}
}
}
It's quite simple. See this demo. Let's say you have two components parent and child. And you want to pass a variable to child and modify it there, say views.
On parent template:
<child [(views)]="views"></child>
On child component:
#Input() views: number;
#Output() viewsChange = new EventEmitter<number>();
// Bind this function to button click or some events:
updateViews() {
this.views++;
this.viewsChange.emit(this.views); // Emit the value to parent:
}
Detail explanation:
When you bind [(views)] in parent, it is acting as:
<child
[views]="views"
(viewsChange)="views = $event">
</child>
So, it is listening to viewsChange output function. Whenever, you do viewsChange.emit, the parent views get updated.
Gotcha: The output function should be precisely named $var + "Change". If you chose to name something else you will have to do:
<child
[views]="views"
(customEmitterFunction)="views = $event">
</child>
In order to pass data from the child component to the father component you shuold set an Output parameter, to trigger the signal your component should implements the OnChanges interface, your component should be like this
import { Component, Input,Output,EventEmitter,OnChanges,SimpleChanges } from '#angular/core';
#Component({
selector: 'my-cmp',
template: `
<p>returnedString = {{ returnedString }}</p>
`
})
export class MyComponent implements OnChanges {
myStringArray: string[] = ['First','Second','Third','Forth','Fifth','Sixth'];
#Input() stringIndex: number;
#Output() cardinalNumber:EventEmitter<string> = new EventEmitter<string>();// you define an Output, this will emit a signal that will received from the father Component
ngOnChanges(changes:SimpleChanges) {
// when the input changes we emit a signal
this.cardinalNumber.emit(this.myStringArray[this.stringIndex]);
}
get returnedString(): string {
if (this.stringIndex !== undefined) {
return this.myStringArray[this.stringIndex];
}
}
}
then in the template of the father component you should insert :
<my-cmp [stringIndex]=the parameter in father Component's controller
(cardinalNumber)="method2beinvoked($event)">
</my-cmp>
method2beInvoked is the method in the father component that handles the message;
or you could do like this:
<my-cmp [stringIndex]=the parameter in father Component's controller
(cardinalNumber)="parameter=$event")>
</my-cmp
where parameter is a parameter in the father's component controller

Angular2: How do you pass a selected item from a HTML datalist element back to the component?

I have simple component which includes a to dreate a drop-down box. This is list is filled with the results from a Web API call. For display purposes I only use two fields of the item. However, once an element has been selected I need to do stuff with all the other fields. How do I pass the entire element back to the component?
Really would appreciate any help.
<h1>Get Locations</h1>
<div>
<div>
<input list="browsers" name="browser" #term (keyup)="search(term.value)">
<datalist id="browsers">
<option *ngFor="let item of items | async" >
{{item.code + " " + item.description}}
</option>
</datalist>
</div>
<input type="submit" value="Click" (click)="onSelect(item)" />
</div>
The component code is as follows:
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { LocationService } from './location.service';
import {Location} from './location.component';
import './rxjs-operators';
#Component({
selector: 'lesson-08',
templateUrl: './views/lesson08.html',
providers: [LocationService]
})
export class Lesson08 implements OnInit{
constructor(private locationService: LocationService) { }
aLoc: Location;
ngOnInit() {
this.aLoc = new Location();
}
errorMessage: string;
locations: Location[];
mode = 'Observable';
displayValue: string;
private searchTermStream = new Subject<string>();
search(term: string) {
this.searchTermStream.next(term);
}
onSelect(item: Location) {
// do stuff with this location
}
items: Observable<Location[]> = this.searchTermStream
.debounceTime(300)
.distinctUntilChanged()
.switchMap((term: string) => this.locationService.search(term));
}
The (click) event calling the method should be on the option selected. Your declared "item" is only known in the scope of the *ngFor, so on the levels of its children. You declared it on a too high level.