Angular 6 asynchronous autocomplete not working but it displaying items without changing value - autocomplete

Angular 6 asynchronous autocomplete is not working but it displays items without changing value and does not reduce the list of proposed values
component.ts:
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder, Validators,FormControl} from '#angular/forms'
import { Observable } from 'rxjs';
import { startWith, map } from 'rxjs/operators';
export class AjouterMarcheComponent implements OnInit {
createForm: FormGroup;
myControl : FormControl;
directions: string[] = ['DGI','SSI','TTU','BLI'];
filteredDirections: Observable<string[]>;
constructor(private fb: FormBuilder) {
this.createForm = this.fb.group({
NomDirection: ['', Validators.required]})
this.myControl= new FormControl();
});
}
ngOnInit() {
this.filteredDirections = this.myControl.valueChanges.pipe(
startWith(''),
map(value => this._filter(value))
);
}
private _filter(value: string): string[] {
const filterValue = value.toLowerCase(); //miniscule
return this.directions.filter(direction => `direction.toLowerCase().indexOf(filterValue) === 0);
}
}
Thanks for your help

Compoment.html
compoment.html
<div>
<br>
<mat-card>
<br>
<form [formGroup]="createForm" class="create-form" >
<mat-form-field class="field-full-width">
<input placeholder="Choisir ou ajouter une direction" type="text" aria-label="Number" [formControl]="myControl" matInput [matAutocomplete]="auto" #NomDirection>
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of options" [value]="option">
{{ option }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
</mat-card>
</div>

Related

Angular/material autocomplete: options do not render

Angular version: 5.0.0
angular/material version: 5.2.4
I've got this form:
<form #updateForm="ngForm">
<mat-form-field>
<input type="text" matInput [formControl]="studentFormControl [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let student of students" [value]="student">
{{student}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
And I've got this component:
import { FormGroup, FormControl, FormsModule, ReactiveFormsModule } from '#angular/forms';
import { MatInputModule, MatAutocompleteModule, MatFormFieldModule } from '#angular/material';
export class DashboardComponent implements OnInit {
this.studentFormControl = new FormControl();
this.students = ['hi', 'hello'];
...
}
Although the form shows up, the autocomplete box remains dimensionless without options rendered within:
screenshot of input/autocomplete box
It should be noted as well that the form is nested within an ngbModal. Any ideas what could be happening here?
So I'm no expert here, but from what I understand, the autocomplete function requires an observable of sorts, at least if you want to be able to filter results as you type, to be selected. Since you are using an array you need to first turn it into an observable. As your array is loaded immediately you would need to use RXJS's subjects and more specifically the BehaviorSubject.
Once it becomes an observable you can then call it using the asyncpipe on your form.
This is not to say you couldn't have done it with an array and not used the asyncPipe, however that array would have to be loaded in OnInit.
See the example for both
import { Observable, from, Subscription, BehaviorSubject } from 'rxjs';
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, FormsModule, ReactiveFormsModule } from '#angular/forms';
import { MatInputModule, MatAutocompleteModule, MatFormFieldModule } from '#angular/material';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {
studentFormControl = new FormControl();
studentFormControl1 = new FormControl();
array = ['test','one','two'];
array1=[];
arrayObs: Observable<any>;
arrayBehObs = new BehaviorSubject(this.array);
constructor() { }
ngOnInit() {
this.array1.push('test','one','two');
this.arrayObs = this.returnAsObs();
}
private returnAsObs() {
return this.arrayBehObs.asObservable();
}
}
<form #updateForm="ngForm">
<input type="text" matInput [formControl]="studentFormControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let student of arrayObs|async" [value]="student">
{{student}}
</mat-option>
</mat-autocomplete>
<input type="text" matInput [formControl]="studentFormControl1" [matAutocomplete]="auto1">
<mat-autocomplete #auto1="matAutocomplete">
<mat-option *ngFor="let student of array1" [value]="student">
{{student}}
</mat-option>
</mat-autocomplete>
</form>

Angular 5 - Can't Pass Data to Modal

Short summary of what i actually want to do.
If i click on the heroes table. I need to pass the heroes data from the row clicked to a Modal.
I read that there are two ways to get Data into a Modal. (NGMODAL)
With #Input and #Output or through a shared Service.
But it doesn't matter which way I am using. The modal opens (and fetches data) always before the data is passed.
I've simply got a table in which all my heroes are displayed.
On a Click on the delete button in a table row I wan't to open the modal and pass the data from my table row. (I want to pass the whole hero, but shouldn't be a difference to just passing the name of the hero).
After that i want to show.
Do you really want to delete hero with name...?
[Cancel], [Yes, Delete] . Onclick on the Yes, Delete Button I want to delete the hero.
At the moment I can't even display the heroes name I want to delete
I hope anyone can tell my what I can do and how.
Thank you.
Here is my code:
Shows my Heroes: hero.component.html
<div class="container">
<h2>Heroes</h2>
<input type="text" placeholder="Suchbegriff eingeben" class="form-control has-float-label" [(ngModel)]="filter" >
<br>
<div *ngIf="heroes">
<table class="heroes table table-hover table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let hero of heroes | filter:filter:'name':'name'">
<td>{{hero.name}}</td>
// I could use (click)="pusHero(hero)" and open the modal afterwars. Than i would get the right data through input. But when i click to open the modal first there is no data, or the data from the row clicked before
<td>
<ngbd-modal-component (click)="selectRow(hero) [(delhero)]="delhero""></ngbd-modal-component> </td>
</tbody>
</table>
</div>
</div>
heroes.component.ts
import { Component, OnInit, ViewEncapsulation , Input , EventEmitter} from '#angular/core';
import { Hero } from '../../model/hero';
import { HeroService } from '../hero.service';
import { CommonModule } from '#angular/common';
import { Observable } from 'rxjs/Observable';
import { Response } from '#angular/http/src/static_response';
// For use of map
import 'rxjs/Rx';
// Für Pipe und Suche in Liste
import { Pipe, PipeTransform } from '#angular/core';
import { FilterPipe } from '../../pipes/filter.pipe';
// Component Decorator imported from Component
#Component({
// Unique Selector
selector: 'app-hero',
// URL of Template
templateUrl: './hero.component.html',
// URL of stylesheet
styleUrls: ['./hero.component.css']
// encapsulation: ViewEncapsulation.None
})
export class HeroesComponent implements OnInit {
heroes: Observable<Hero[]>;
hero: Hero;
filter = '';
// Is Input for Child Component. In this case modal
delhero: Hero;
constructor(
private heroService: HeroService,
// Modal Service
// private modalService: NgbModal,
// private ngbdModalComponent: NgbdModalComponent,
// private ngbdModalContent: NgbdModalContent,
) {
// this.searchableList = ['name','age']
}
selectRow(hero): void {
console.log("Select the tables data");
console.log(hero);
this.heroService.selectRow(hero);
}
}
Hero Service
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { HttpResponse } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { catchError, map, tap } from 'rxjs/operators';
import { Hero } from '../model/hero';
import { MessageService } from '../message.service';
import { Response } from '#angular/http/src/static_response';
// For use of map
import 'rxjs/Rx';
import { headersToString } from 'selenium-webdriver/http';
const httpOptions = {
headers: new HttpHeaders(
{
'Content-Type': 'application/json'
}
)
};
#Injectable()
export class HeroService {
private hero: Hero;
constructor(private http: HttpClient, private messageService: MessageService) { }
selectRow(input){
this.hero = input;
}
getRow(){
console.log(this.hero);
return this.hero;
}
}
My Modal
modal.component.html
<button class="btn btn-danger btn-xs more" (click)="open()"> <span class="fa fa-trash"></span></button>
modal.component.ts
import { Component, Input, EventEmitter, SimpleChange, Output, OnInit } from '#angular/core';
import { NgbModal, NgbActiveModal } from '#ng-bootstrap/ng-bootstrap';
import { NgbdModalContent } from './modal.content.component';
import { Hero } from '../../model/hero';
import { HeroService } from '../../hero/hero.service';
// Modal Component
#Component({
selector: 'ngbd-modal-component',
templateUrl: './modal.component.html'
})
export class NgbdModalComponent {
// #Input() delhero: Hero;
// hero: Hero;
// #Output() getDeleteHero = new EventEmitter();
name = '';
constructor(private modalService: NgbModal,
private heroService: HeroService) {}
ngOnInit(){
// this.open();
// console.log(this.delhero);
}
open() {
var name = this.heroService.getRow();
// this.getDeleteHero.emit();
console.log("Modal Component hero");
// console.log(this.delhero);
this.name = this.heroService.getRow().name;
const modalRef = this.modalService.open(NgbdModalContent);
modalRef.componentInstance.name = name;
}
delete(){
console.log("Delete Hero through Hero Service");
}
}
modal.content.component.html
<div class="modal-header ">
<h4 class="modal-title">Delete Hero?</h4>
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Delete Hero with Name: {{name}}?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="activeModal.close('Close click')">No, do not delete</button>
<button type="button" class="btn btn-primary">Ja, löschen</button>
</div>
modal.content.component.ts
import { Component, Input, EventEmitter } from '#angular/core';
import { NgbModal, NgbActiveModal } from '#ng-bootstrap/ng-bootstrap';
import { HeroService } from '../../hero/hero.service';
import { Hero } from '../../model/hero';
// Modal Content
#Component({
selector: 'ngbd-modal-content',
templateUrl: './modal.content.component.html'
// styleUrls: ['./modal.content.component.css']
})
export class NgbdModalContent {
// #Input() Hero;
name = "";
constructor(
public activeModal: NgbActiveModal,
privateheroService: HeroService
) {}
ngOnInit() {
//Called after the constructor, initializing input properties, and the first call to ngOnChanges.
//Add 'implements OnInit' to the class.
console.log("NG On Init ModalsContent");
this.name = this.heroService.getRow().name;
console.log(this.name);
}
}
Not sure if this helps but I found this after having the same problem.
I ended up with working code. Sorry it's not your code but I was having trouble following it. I had to set a variable and pass it in via the click code:
HTML:
<ng-template #content let-c="close" let-d="dismiss">
<div class="modal-header">
<h4 class="modal-title">Option Entry</h4>
<button type="button" class="close" aria-label="Close" (click)="d('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label class="control-form-label" for="optDescription">Description:</label>
<input type="text" class="form-control" name="description" ngModel id="optDescription" [(ngModel)]="editOption">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="c('Close click')">Update</button>
</div>
</ng-template>
<div *ngIf='optionList && optionList.length'>
<div *ngFor='let opt of optionList'>
<ngb-accordion #acc="ngbAccordion" activeIds="ngb-panel-0" class="accordianheading">
<ngb-panel>
<ng-template ngbPanelTitle>
<span>{{opt.description}}</span>
<button class='btn btn-sm btn-outline-primary btnline pull-right' (click)='$event.preventDefault(); $event.stopPropagation(); editItem(content, opt)'>Edit</button>
</ng-template>
<ng-template ngbPanelContent>
<js-option-lists [OptionListID]=opt.id></js-option-lists>
</ng-template>
</ngb-panel>
</ngb-accordion>
</div>
</div>
and:
.ts file
import { Component, OnInit, Input } from '#angular/core';
import { IOptionListHeader } from '../dtos/option-list-header';
import { OptionListService } from '../services/felixApi/option-list.service';
import { NgbModal, ModalDismissReasons } from '#ng-bootstrap/ng-bootstrap';
import { NgModel, NgForm } from '#angular/forms';
#Component({
selector: 'js-option-lists',
templateUrl: './option-lists.component.html',
styleUrls: ['./option-lists.component.css']
})
export class OptionListsComponent implements OnInit {
#Input() OptionListID: number;
errorMessage: any;
optionList: IOptionListHeader[] = [];
updatedOption: any;
closeResult: string;
editOption = '';
constructor(private _optionListService: OptionListService, private modalService: NgbModal) { }
// for now read in all options but will need to read by level
ngOnInit() {
this.getChildren(this.OptionListID);
}
getChildren (id) {
this._optionListService.getOptionListChildren(id)
.subscribe(
optionList => {
this.optionList = optionList;
},
error => this.errorMessage = <any>error);
}
// Patch change
patchOrderNo(updatedOption: any) {
this._optionListService.updateOption(updatedOption).subscribe(
res => {
this.getChildren(this.OptionListID);
},
err => {
console.log('error code ' + err.status);
}
);
}
editItem(content, opt: IOptionListHeader) {
this.editOption = opt.description;
this.modalService.open(content).result.then((result) => {
this.closeResult = `Closed with: ${result}`;
this.updatedOption = { id: opt.id, description: this.editOption };
this.patchOrderNo(this.updatedOption);
}, (reason) => {
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
console.log('cancelled');
});
}
private getDismissReason(reason: any): string {
if (reason === ModalDismissReasons.ESC) {
return 'by pressing ESC';
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
return 'by clicking on a backdrop';
} else {
return `with: ${reason}`;
}
}
}

Pass new password value to validator.ts in Angular 4

I am working through the challenges for a Udemy course on Angular 4, but I am stuck on a challenge where I have to create an input for a new password and then another input to confirm the new password using reactive forms.
I have an external .ts file called password.validators.ts that has custom form validation code, and I can get the value of the currently selected input box by passing a control object with AbstractControl, but how do I pass a value to my component.ts file and then from my component.ts to my password.validators.ts ? I need to be able to compare the new password value to the confirm password value and I'm stuck!
new-password.component.html
<form [formGroup]="form">
<div class="form-group">
<label for="oldPassword">Old Password</label>
<input
formControlName="oldPassword"
id="oldPassword"
type="text"
class="form-control">
<div *ngIf="oldPassword.pending">Checking password...</div>
<div *ngIf="oldPassword.touched && oldPassword.invalid" class="alert alert-danger">
<div *ngIf="oldPassword.errors.required">Old password is required</div>
<div *ngIf="oldPassword.errors.checkOldPassword">Password is incorrect</div>
</div>
</div>
<div class="form-group">
<label for="newPassword">New password</label>
<input
formControlName="newPassword"
id="newPassword"
type="text"
class="form-control">
<div *ngIf="newPassword.touched && newPassword.invalid" class="alert alert-danger">
<div *ngIf="newPassword.errors.required">New password is required</div>
</div>
</div>
<div class="form-group">
<label for="confirmNewPassword">Confirm new password</label>
<input
formControlName="confirmNewPassword"
id="confirmNewPassword"
type="text"
class="form-control">
<div *ngIf="confirmNewPassword.touched && confirmNewPassword.invalid" class="alert alert-danger">
<div *ngIf="confirmNewPassword.errors.required">Confirm password is required</div>
<div *ngIf="confirmNewPassword.errors.confirmNewPassword">Passwords don't match</div>
</div>
</div>
<button class="btn btn-primary" type="submit">Change Password</button>
</form>
new-password.component.ts
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, Validators } from '#angular/forms';
import { PasswordValidators } from './password.validators';
#Component({
selector: 'new-password',
templateUrl: './new-password.component.html',
styleUrls: ['./new-password.component.css']
})
export class NewPasswordComponent {
form = new FormGroup({
oldPassword: new FormControl('', Validators.required, PasswordValidators.checkOldPassword),
newPassword: new FormControl('', Validators.required),
confirmNewPassword: new FormControl('', Validators.required )
})
get oldPassword() {
return this.form.get('oldPassword');
}
get newPassword() {
return this.form.get('newPassword');
}
get confirmNewPassword() {
return this.form.get('confirmNewPassword');
}
addNewPassword(newPassword: HTMLInputElement) {
let np = this.newPassword;
return np;
}
}
password.validators.ts
import { AbstractControl, ValidationErrors } from '#angular/forms';
export class PasswordValidators {
static checkOldPassword(control: AbstractControl) : Promise<ValidationErrors | null> {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(control.value !== '1234')
resolve({ checkOldPassword: true }) ;
else resolve(null);
}, 2000);
});
}
static confirmNewPassword(control: AbstractControl) : ValidationErrors | null {
if(control.value === control.newPassword.value)
return null;
}
}
I have used the following code for my password validation may be this can help you
In password.validator write this code
import {AbstractControl} from '#angular/forms';
export class PasswordValidation {
static MatchPassword(AC: AbstractControl) {
let password = AC.get('password').value;
let confirmPassword = AC.get('confirmPassword').value;
if(password != confirmPassword) {
console.log('false');
AC.get('confirmPassword').setErrors( {MatchPassword: true} )
} else {
console.log('true');
return null
}
}
}
and in the component file use this code
constructor(fb: FormBuilder)
{
this.form = fb.group({
password: ['', Validators.required],
confirmPassword: ['', Validators.required]
}, {
validator: PasswordValidation.MatchPassword // your validation method
})
}
and in html file to find error use this code
<div class="alert alert-danger" *ngIf="form.controls.confirmPassword.errors?.MutchPassword">Password not match</div>
Hope it would help you

Get child form data when called from parent

Hi i have a question about Angular 2 and really don't know how to do that and I need your help :)
I have a child component that have a simple registration form but the submit button on parent template. So when I press the submit button it will get data from child component form.
here is my example.
Parent component ts
import {Component, ViewChild, OnDestroy, Output, EventEmitter} from '#angular/core';
import {GlobalEvent} from 'app/shared/GlobalEvent';
#Component({
selector: 'signup',
templateUrl: 'SignUpModalComponent.html',
styleUrls: ['SignUpModalComponent.scss'],
})
export class SignUpModalComponent implements OnDestroy {
public overlayState: boolean;
public step: number = 1;
public formSubmit: boolean;
#ViewChild('firstModal')
modal: any;
constructor(private modalEventService: GlobalEvent) {
this.modalEventService.modalChangeEvent.subscribe((res: boolean) => {
this.overlayState = res;
if (res) {
this.modal.open();
} else {
this.modal.close();
}
});
}
closeModal(value: boolean): void {
this.modalEventService.close(false);
this.modal.close()
}
ngOnDestroy() {
this.modalEventService.modalChangeEvent.unsubscribe();
}
incrementStep():void {
this.step += 1;
}
decrementStep():void {
this.step -= 1;
}
onFormSubmit(): void {
this.formSubmit = true;
}
}
Parent component html template
<div class="modal-overlay" *ngIf="overlayState"></div>
<modal (onClose)="closeModal(false)" #firstModal >
<modal-header>
<button (click)="firstModal.close()" class="close-btn"><i class="material-icons">close</i></button>
<div class="steps-container">
<span [ngClass]="{'active': step == 1 }">1</span>
<span [ngClass]="{'active': step == 2 }">2</span>
<span [ngClass]="{'active': step == 3 }">3</span>
<span [ngClass]="{'active': step == 4 }">4</span>
</div>
</modal-header>
<modal-content>
<signup-form [hidden]="step != 1" [formSubmit]="formSubmit"></signup-form>
<signup-social ngClass="social-step{{step}}" [step]="step"></signup-social>
</modal-content>
<modal-footer>
<button md-button (click)="decrementStep()" [hidden]="step == 1"><Back</button>
<button md-button (click)="incrementStep(); onFormSubmit()" [hidden]="step != 1">Next></button>
<button md-button (click)="incrementStep()" [hidden]="step == 1 || step == 4">Skip</button>
</modal-footer>
</modal>
Child component ts file
import {Component, OnInit, Input} from '#angular/core';
import {FormBuilder, FormGroup, Validators} from '#angular/forms';
import {CustomValidators} from 'ng2-validation';
import {SignUpFormInterface} from './SignUpFormInterface';
#Component({
selector: 'signup-form',
templateUrl: 'SignUpForm.html',
styleUrls: ['SignUpForm.scss']
})
export class SignUpForm implements OnInit {
signupInfo: FormGroup;
public tmppass: any;
#Input() set formSubmit(sendData: boolean) {
if (sendData) {
}
}
constructor(private formbuilder: FormBuilder) {
}
ngOnInit() {
this.signupInfo = this.formbuilder.group({
email: ['', [CustomValidators.email]],
username: ['', [Validators.required, CustomValidators.min(10)]],
password: ['', [Validators.required, CustomValidators.rangeLength([5, 100]), this.setPass]],
confirmPassword: ['', [Validators.required, CustomValidators.rangeLength([5, 100]), CustomValidators.equal(this.tmppass)]],
fullName: ['', [Validators.required, CustomValidators.min(10)]],
gender: ['', [Validators.required]],
countrys: ['', [Validators.required]],
});
}
setPass(c: any): void {
console.log('sdsd', c.value);
if (c.value) {
this.tmppass = '';
this.tmppass = <string>c.value;
}
}
onSubmit({value, valid}: {value: SignUpFormInterface, valid: boolean}) {
console.log(this.signupInfo);
}
cons() {
console.log(this.signupInfo);
}
}
function validateEmail(c: any) {
let EMAIL_REGEXP = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i;
return EMAIL_REGEXP.test(c.value) ? c.email = false : c.email = true;
}
Child component html template
<div class="sign-up-form clearfix">
<form novalidate (ngSubmit)="onSubmit(signupInfo)" [formGroup]="signupInfo">
<label>
<input type="file" placeholder="Last Name*" name="lastName"/>
upload an avatar
</label>
<md-input-container>
<input md-input placeholder="Email address*" name="email" formControlName="email"/>
</md-input-container>
<!--<p *ngIf="signupInfo.controls.email.errors?.email">error message</p>-->
<div class="errror-msg" *ngIf="signupInfo.controls.email.errors?.email">
<md-hint>
<span>Name should be minimum 6 characters</span>
</md-hint>
</div>
<p *ngIf="signupInfo.controls.username.errors?.min">error message</p>
<div class="input-wrap" [ngClass]="{'error': signupInfo.controls.username.errors?.min}">
<md-input-container>
<input md-input placeholder="Username*" name="username" formControlName="username"/>
</md-input-container>
<div class="errror-msg" *ngIf="signupInfo.controls.username.errors?.min">
<md-hint>
<span>Name should be minimum 6 characters</span>
</md-hint>
</div>
<div class="errror-msg"
*ngIf="signupInfo.get('username').hasError('minlength') && signupInfo.get('username').touched">
<md-hint>
<span>Name should be minimum 6 characters</span>
</md-hint>
</div>
</div>
<p *ngIf="signupInfo.controls.password.errors?.rangeLength">error xcdfdfd</p>
<md-input-container>
<input md-input placeholder="Password*" formControlName="password"/>
</md-input-container>
<p *ngIf="signupInfo.controls.confirmPassword.errors?.rangeLength">error password</p>
<p *ngIf="signupInfo.controls.confirmPassword.errors?.equal">error equal</p>
<md-input-container>
<input md-input placeholder="Confirm Password*" formControlName="confirmPassword"/>
</md-input-container>
<p *ngIf="signupInfo.controls.fullName.errors?.min">error password</p>
<md-input-container>
<input md-input placeholder="Full Name*" name="fullName" formControlName="fullName"/>
</md-input-container>
<p *ngIf="signupInfo.controls.gender.errors?.required">error password</p>
<md-select placeholder="Gender" formControlName="gender">
<md-option *ngFor="let food of ['react','angular']" [value]="food">
{{ food }}
</md-option>
</md-select>
<p *ngIf="signupInfo.controls.countrys.errors?.required">error password</p>
<md-select placeholder="Country" formControlName="countrys">
<md-option *ngFor="let country of countrys" [value]="country">
{{ country.name }}
</md-option>
</md-select>
<div class="input-wrap">
<md-input-container placeholder="Date of Birth *">
<input md-input placeholder="mm/dd/yyyy" type="text" name="DateofBirth"/>
</md-input-container>
</div>
</form>
</div>
Thanks for help :)

Angular2: issues converting a form's value to a Model with default fields

I am busy with a Stock take form that needs to be posted to a REST API. I have a stockTake model which has three fields that get initialized with default values of 0. the three fields "StockTakeID, IDKey and PackSize" are not part of the angular form where the data needs to be entered. My issue is submitting the model with those three fields with default values to my RestService. When I submit the stockTakeForm.value I get an error as those three fields are not part of the data that's being submitted... Any idea how I can go about getting this to work?
my stock-take.model.ts:
export class StockTakeModel {
constructor(
StockTakeID: number = 0,
IDKey: number = 0,
BarCode: number,
ProductCode: string,
SheetNo: string,
BinNo: string,
Quantity: number,
PackSize: number = 0) { }
}
my stock-take.component.ts:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { RestService } from '../../services/rest.service';
import { StockTakeModel } from '../../models/stock-take.model';
#Component({
selector: 'stock-take',
templateUrl: './stock-take.component.html',
styleUrls: ['./stock-take.component.css']
})
export class StockTakeComponent implements OnInit {
stockTakeForm: FormGroup;
constructor(private fb: FormBuilder, private restService: RestService) {
this.stockTakeForm = fb.group({
'sheetNo':['', Validators.required],
'binNo':['', Validators.required],
'barcode':['', Validators.required],
'Qty':['', Validators.required]
});
}
submitStockTake(stockTakeModel: StockTakeModel) {
//console.log(stockTakeModel);
this.restService.postStockTake(stockTakeModel)
.subscribe(
(res) => {
console.log(res);
},
(res) => {
console.log("failure: " + res);
}
);
this.stockTakeForm.reset();
}
ngOnInit() {
}
}
my stock-take.component.html:
<div class="row">
<div class="col-md-6 col-md-push-3">
<h1>Stock Take</h1>
<br /><br />
<form [formGroup]="stockTakeForm" role="form" (ngSubmit)="submitStockTake(stockTakeForm.value)">
<input type="text" placeholder="Enter Sheet Number" class="form-control" id="sheetNo" [formControl]="stockTakeForm.controls['sheetNo']" name="sheetNo">
<input type="text" placeholder="Enter Bin Number" class="form-control" id="binNo" [formControl]="stockTakeForm.controls['binNo']" name="binNo">
<input type="number" placeholder="Enter Barcode" class="form-control" id="bCode" [formControl]="stockTakeForm.controls['barcode']" name="barcode">
<input type="number" placeholder="Enter Quantity" clas="form-control" id="Qty" [formControl]="stockTakeForm.controls['Qty']" name="quantity">
<button class="btn btn-block" [disabled]="!stockTakeForm.valid">Submit</button>
</form>
</div>
</div>
updated stock-take.component:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { RestService } from '../../services/rest.service';
import { StockTakeModel } from '../../models/stock-take.model';
#Component({
selector: 'stock-take',
templateUrl: './stock-take.component.html',
styleUrls: ['./stock-take.component.css']
})
export class StockTakeComponent implements OnInit {
stockTakeForm: FormGroup;
stockTakeModel: StockTakeModel;
constructor(private fb: FormBuilder, private restService: RestService) {
this.stockTakeForm = fb.group({
'sheetNo':['', Validators.required],
'binNo':['', Validators.required],
'barcode':['', Validators.required],
'Qty':['', Validators.required]
});
}
doStockTake(val: any) {
//console.log("val:" + JSON.stringify(val));
this.stockTakeModel = new StockTakeModel(0, 0, val[Object.keys(val)[2]], '', val[Object.keys(val)[0]], val[Object.keys(val)[1]], val[Object.keys(val)[3]], 0);
// console.log(this.stockTakeModel);
console.log(JSON.stringify(this.stockTakeModel));
}
submitStockTake(stockTakeModel: StockTakeModel) {
//console.log(stockTakeModel);
this.restService.postStockTake(stockTakeModel)
.subscribe(
(res) => {
console.log(res);
},
(res) => {
console.log("failure: " + res);
}
);
this.stockTakeForm.reset();
}
ngOnInit() {
}
}
One workaround would to create a new variable, e.g:
stockTakeModel: StockTakeModel;
if that doesn't work (as you said it complained about undefined), you can also instantiate it as an empty Object, like so:
stockTakeModel = {};
EDIT, so you changed your code to something like this: I also added some console.logs to show where it is undefined, as there was a little question about that.
stockTakeModel: StockTakeModel; // undefined at this point (or empty if you have defined it as empty above)!
// use Object keys to access the keys in the val object, a bit messy, but will work!
submitStockTake(val: any) {
this.stockTakeModel =
new StockTakeModel(0, 0, val[Object.keys(val)[2]], '', val[Object.keys(val)[0]],
val[Object.keys(val)[1]], val[Object.keys(val)[3]]);
// console.log(this.StockTakeModel); // now it should have all values!
this.restService.postStockTake(this.stockTakeModel)
.subscribe(d => { console.log(d)} ) // just shortened your code a bit
this.stockTakeForm.reset();
}
EDIT: So finally the last issue is solved, besides the solution above. It's because your StockTakeModel-class wasn't properly declared the value was undefined, or actually empty. So fix your class to:
export class StockTakeModel {
constructor(
public StockTakeID: number = 0, // you were missin 'public' on everyone
public IDKey: number = 0,
public BarCode: number,
public ProductCode: string,
public SheetNo: string,
public BinNo: string,
public Quantity: number,
public PackSize: number = 0) { }
}
Note also that in your 'updated' code you still have (ngSubmit)="submitStockTake(stockTakeForm.value) in your form, so your created doStockTake-method is not called.
I don't understand the construction [formControl] in your template.
There should be at least [ngModel].
Usualy I use, for two-way communication, the [(ngModel)]. Not the [formControl].
So your template code should look like this:
<div class="row">
<div class="col-md-6 col-md-push-3">
<h1>Stock Take</h1>
<br /><br />
<form [formGroup]="stockTakeForm" role="form" (ngSubmit)="submitStockTake(stockTakeForm.value)">
<input type="text" placeholder="Enter Sheet Number" class="form-control" id="sheetNo" [(ngModel)]="stockTakeForm.controls['sheetNo']" name="sheetNo">
<input type="text" placeholder="Enter Bin Number" class="form-control" id="binNo" [(ngModel)]="stockTakeForm.controls['binNo']" name="binNo">
<input type="number" placeholder="Enter Barcode" class="form-control" id="bCode" [(ngModel)]="stockTakeForm.controls['barcode']" name="barcode">
<input type="number" placeholder="Enter Quantity" clas="form-control" id="Qty" [(ngModel)]="stockTakeForm.controls['Qty']" name="quantity">
<button class="btn btn-block" [disabled]="!stockTakeForm.valid">Submit</button>
</form>
</div>
</div>