How can I populate user info into edit form angular - html

I have an app that you can import student firstname and lastname data into and it writes to mongodb. I can create new students, and I can view the list of students in the db, but when updating, I can't seem to get the student data to populate into the firstname and lastname input fields.
In my list-students-component.ts, i can select update and it will forward to (the new-student-component) to edit students based on the student ID.
I can't seem to figure out to get this data to populate
<div>
<ul *ngFor ="let student of students let i = index">
<li> {{student._id}}: {{student.firstName}} {{student.lastName}} (index {{i}})
<button mat-button color="warn" (click)="onDelete(student._id)">DELETE</button>
<button mat-button color="accent" [routerLink]="['/editStudent', student._id]">UPDATE</button>
</li>
</ul>
</div>
<div>
<h3>{{mode}} Student Form</h3>
<form (ngSubmit)="onSubmit()" #myForm="ngForm">
<mat-form-field >
<input matInput placeholder="First Name" id="firstname" [(ngModel)]=" firstName" name="firstname" >
</mat-form-field>
<mat-form-field >
<input matInput placeholder="Last Name" id="lastname" [(ngModel)]=" lastName" name="lastname" >
</mat-form-field>
<p>
<button type="submit" mat-raised-button color="primary">Submit</button>
</p>
</form>
</div>
import { Component, OnInit, Input } from '#angular/core';
import { StudentService } from '../student.service';
import { Router } from '#angular/router';
import { ActivatedRoute, ParamMap } from '#angular/router';
import { FormGroup, FormBuilder, FormControl } from '#angular/forms';
#Component({
selector: 'app-new-student-form',
templateUrl: './new-student-form.component.html',
styleUrls: ['./new-student-form.component.css']
})
export class NewStudentFormComponent implements OnInit {
#Input() firstName: string;
#Input() lastName: string;
private mode = 'Add New'; //default mode
private id: string; //student ID
public ngForm = new FormGroup({
firstName: new FormControl('firstName'),
lastName: new FormControl('lastName')
})
constructor(private _myService: StudentService, private router: Router,
public route: ActivatedRoute) { }
onSubmit() {
console.log("You submitted: " + this.firstName + " " + this.lastName);
this._myService.addStudents(this.firstName, this.lastName);
this.router.navigate(['/listStudents'])
.then(() => {
location.reload();
});
}
ngOnInit() {
this.route.paramMap.subscribe((paramMap: ParamMap) => {
if (paramMap.has('_id')) {
this.mode = 'edit'; /*request had a parameter _id */
this.id = paramMap.get('_id');
}
else {
this.mode = 'Add New';
this.id = null;
}
if (this.mode == 'Add New')
this._myService.addStudents(this.firstName, this.lastName);
if (this.mode == 'edit')
this.ngForm.value;
this._myService.updateStudent(this.id, this.firstName, this.lastName);
});
}
}

Related

Angular TypeError: Cannot read property '0' of undefined when using event.target.file[0]

I'm currently trying to take a file input from a user and store it to Firebase Storage using AngularFire. Since it's undefined, I'm convinced that whatever file they're looking at, it's NULL, but my UI clearly shows that I'm successfully inputting a file, but the error gets caught as soon as I select the file. Am I misunderstanding how the input sends information to my TypeScript file?
Here's my TS file:
import { AngularFirestore } from '#angular/fire/firestore';
import { Component, OnInit, Input, ChangeDetectorRef } from '#angular/core';
import { finalize, tap, map } from 'rxjs/operators';
import { FormBuilder, FormGroup, FormArray, Validators } from '#angular/forms';
import { SubmitPostService } from './submit-post.service';
import { Post } from '../post.model';
import { Observable } from 'rxjs';
#Component({
selector: 'app-post-creation',
templateUrl: './post-creation.component.html',
styleUrls: ['./post-creation.component.scss']
})
export class PostCreationComponent implements OnInit {
private uuidv4 = require('uuid/v4');
// Main task
task: AngularFireUploadTask;
// Progress Monitoring
percentage: Observable<number>;
// Download URL
downloadURL: Observable<string>;
myForm: FormGroup;
post: Post;
ref: AngularFireStorageReference;
files: Observable<any>;
constructor(
private formBuilder: FormBuilder,
private postService: SubmitPostService,
private storage: AngularFireStorage,
) { }
ngOnInit() {
this.myForm = this.formBuilder.group({
title: ['', [Validators.required]],
artists: ['', [Validators.required]],
description: '',
medium: ''
});
this.myForm.valueChanges.subscribe(console.log);
}
onSubmit() {
this.post = new Post(
this.uuidv4(),
this.myForm.get('artists').value,
this.myForm.get('title').value,
new Date(),
new Date(),
`images/${Date.now()}_${this.myForm.get('title').value}`,
this.myForm.get('description').value,
this.myForm.get('medium').value
);
this.postService.submitPost(this.post);
}
upload(event: any) {
console.log('Upload successful');
try {
const file = event.target.file[0];
// The storage path
const path = `images/${Date.now()}_${file.name}`;
// Reference to storage bucket
this.ref = this.storage.ref(path);
// Main Task
this.task = this.ref.put(file);
// Tracks progress
this.percentage = this.task.percentageChanges();
this.percentage = this.task.snapshotChanges()
.pipe(map(s => (s.bytesTransferred / s.totalBytes) * 100));
this.downloadURL = this.ref.getDownloadURL();
} catch (err) { console.log(err); }
}
}
Here's my HTML:
<div>
<div class="progress">
<progress max="100" [value]="(percentage | async)"></progress>
</div>
<h1>Post Creation</h1>
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<mat-form-field>
<input matInput placeholder="Title" formControlName="title">
</mat-form-field>
<br>
<mat-form-field>
<input matInput placeholder="Artists" formControlName="artists">
</mat-form-field>
<br>
<mat-form-field>
<input matInput placeholder="Description" formControlName="description">
</mat-form-field>
<br>
<mat-form-field>
<input matInput placeholder="Medium" formControlName="medium">
</mat-form-field>
<br>
<input type="file" (change)="upload($event)" accept=".png,.jpg">
<br>
<br>
<button mat-raised-button [disabled]="myForm.invalid" color="primary">Submit Post</button>
</form>
</div>
</body>
I've successfully created the Post objects in the Firestore, so it's not an issue of my firebase configs aren't correctly initialize.
In your upload method, in the const file = event.target.file[0]; statement, the file property should be plural

ReactiveForms refresh page after submitting in Angular CLI

I am following the tutorial:
https://jasonwatmore.com/post/2018/10/29/angular-7-user-registration-and-login-example-tutorial
and write a login form according to this by using ReactiveFormsModule in Angular 7
However, after I click on submit with wrong information I just see alert at top for a second and page refreshes itself. I searched a lot about this topic and changing button type does not help.
Here is my HTML code:
<div>
<h2 align="center">Login</h2>
<form [formGroup]="loginForm">
<div class="form-group">
<label for="email">E-mail</label>
<input type="text" formControlName="email" class="form-control" placeholder="your_email#example.com" [ngClass]="{ 'is-invalid': submitted && f.email.errors }" />
<div *ngIf="submitted && f.email.errors" class="invalid-feedback">
<div *ngIf="f.email.errors">Invalid e-mail</div>
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" formControlName="password" placeholder="******" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.password.errors }" />
<div *ngIf="submitted && f.password.errors" class="invalid-feedback">
<div *ngIf="f.password.errors" >Invalid password</div>
</div>
</div>
<div class="form-group">
<button (click)="onSubmit()" [disabled]="loading || !loginForm.controls.email.value || !loginForm.controls.password.value" class="btn btn-primary">Login</button>
<img *ngIf="loading" class="pl-2" src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==" />
</div>
<a routerLink="/register" class="go-register">Do you need an account?</a>
</form>
</div>
and TypeScript file:
import { Component, OnInit , OnDestroy} from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { FormBuilder, FormGroup, Validators} from '#angular/forms';
import { first } from 'rxjs/operators';
import { AuthenticationService } from '../services/auth.service';
import { AlertService } from '../services/alert.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
loginForm: FormGroup;
loading = false;
submitted = false;
returnUrl: string;
error = '';
constructor(
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService,
private alertService: AlertService
) { }
ngOnInit() {
this.loginForm = this.formBuilder.group({
email: ['', [Validators.email, Validators.required]],
password: ['', [Validators.pattern('^(?=[^A-Z]*[A-Z])(?=[^a-z]*[a-z])(?=[^0-9]*[0-9]).{6,}$'),
Validators.minLength(6),
Validators.required]]
});
// reset login status
this.authenticationService.logout();
// get return url from route parameters or default to '/'
this.returnUrl = this.route.snapshot.queryParams.returnUrl || '/';
}
// convenience getter for easy access to form fields
get f() { return this.loginForm.controls; }
onSubmit() {
this.submitted = true;
// stop here if form is invalid
if (this.loginForm.invalid) {
return;
}
this.loading = true;
this.authenticationService.login(this.f.email.value, this.f.password.value)
.subscribe(
data => {
this.router.navigate([this.returnUrl]);
},
error => {
this.error = error;
// alert(error);
this.alertService.error(error);
this.loading = false;
});
}
}
I debug my code and I guess problem is about HTML but I cannot resolve.
Also, I tried add event.preventDefault() and (ngSubmit), none of them helped me.
I am open for any ideas...
https://angular.io/guide/reactive-forms#saving-form-data
You have to use ngSubmit...
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
Then the default behaviour is disabled and angular handles it.

To pass input field value of type string in angular

I have component called customer which is used to add new customers. The component code as follows:
HTML
<div >
<form [formGroup]="addForm">
<div>
<mat-form-field>
<input matInput placeholder="First Name" formControlName="firstname" required>
<mat-error *ngIf="addForm.controls.firstname.hasError('required')">
Please enter first name
</mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field>
<input matInput placeholder="Last Name" formControlName="lastname" required>
<mat-error *ngIf="addForm.controls.lastname.hasError('required')">
Please enter last name
</mat-error>
</mat-form-field>
</div>
<div>
<mat-radio-group formControlName="gender">
<div>Gender</div>
<mat-radio-button value="Male">Male</mat-radio-button>
<mat-radio-button value="Female">Female</mat-radio-button>
</mat-radio-group>
</div>
<div>
<mat-form-field>
<input matInput placeholder="Email Address" formControlName="email" required>
<mat-error *ngIf="addForm.controls.email.hasError('required') ">
Please enter email address
</mat-error>
</mat-form-field>
</div>
<div>
<button mat-raised-button (click)="onAdd()">ADD</button>
</div>
</form>
TS
import { Component, OnInit, VERSION, ViewChild } from '#angular/core';
import {
FormBuilder,
FormControl,
FormGroup,
Validators,
} from '#angular/forms';
import { Router } from '#angular/router';
import { IContact } from 'src/app/models/app.models';
import { CustomersService } from 'src/app/services/customers.service';
#Component({
selector: 'asd-customer',
templateUrl: './customer.component.html',
styleUrls: ['./customer.component.css'],
})
export class CustomerComponent implements OnInit {
public addForm: FormGroup;
public someContact: IContact;
constructor(private fb: FormBuilder,
public customersService: CustomersService) {}
public ngOnInit(): void {
this.addForm = this.fb.group({
firstname: [null, [Validators.required],
lastname: [null, [Validators.required],
email: ['', [Validators.required],
gender: [null],
});
}
public onAdd(): void {
this.someContact = this.addForm.value;
this.customersService.addCustomer(this.someContact);
}
}
model.ts file
export interface IContact {
firstName: string;
lastName: string;
gender: string;
eMailAddresses: string[];
}
The expected JSON after the POST request:
{
"firstName": "Alfien",
"lastName": "Urlich",
"gender": "Male",
"eMailAddresses": ["aurlich6v#hotmail.com"],
}
When i fill every input fields of the form and tried performing http POST operation. I am getting warning as Bad Request because of email.
Below is the warning:
When i perform POST operation without filling email(input field) the POST happens fine, the JSON appears like this:
{
"firstName": "Lee",
"lastName": "Cooper",
"gender": "Male",
}
What's wrong with the code ??
There is a problem in Email-address field In Form-control it is an single string and in interface it is an array.So if u want to add it set it like below,
public someContact: IContact={};
public onAdd(): void {
this.someContact.firstName = this.addForm.value.firstName;
this.someContact.lastname = this.addForm.value.lastname;
this.someContact.gender = this.addForm.value.gender;
this.someContact.eMailAddresses=[]
this.someContact.eMailAddresses.push(this.addForm.value.email);
}
change interface as follows
export interface IContact {
firstName ?: string;
lastName ?: string;
gender ?: string;
eMailAddresses ?: string[];
}
but according to your scenario there will be only one email address in everytime you submit.

How to two-way bind a form value to a nested angular model

I am new to angular or front-end development for that matter. I am trying to two-way bind a form value to a 'nested array fields' of angular model. Unfortunately, it doesn't work and returns undefined or errors. Certainly looks like a blunder to me, please advice on a fix.
Code as follows,
customer.model.ts
export class Customer {
customerID:String;
customerName:String;
contract:Contract[];
}
export class Contract{
customerID: String;
startDate: Date;
endDate: Date ;
conditions: String;
price: Number;
author: String;
}
customer.component.ts
//Package imports
import { Component, OnInit } from '#angular/core';
import { NgForm } from '#angular/forms';
//local imports
import { CustomerService } from '../shared/customer.service';
import { Contract,Customer } from '../shared/customer.model';
declare var M: any;
#Component({
selector: 'app-customer',
templateUrl: './customer.component.html',
styleUrls: ['./customer.component.css'],
providers: [CustomerService]
})
export class CustomerComponent implements OnInit {
constructor(private customerService: CustomerService) { }
ngOnInit() {
this.resetForm();
}
resetForm(form?: NgForm) {
if (form) {
form.reset();
this.customerService.selectedCustomer = {
customerID: "",
customerName: "",
contract: [
{
customerID: "",
startDate: null,
endDate: null,
conditions: "",
price: null,
author: ""
}
]
}
}
}
onSubmit(form: NgForm) {
this.customerService.postCustomer(form.value).subscribe((res) => {
this.resetForm(form);
M.toast({ html: 'Saved successfully', classes: 'rounded' });
});
}
}
customer.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Operatoroperator';
import { Customer,Contract } from './customer.model';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable({
providedIn: 'root'
})
export class CustomerService {
selectedCustomer: Customer|{}={};
customers: Customer[];
readonly baseURL = 'http://localhost:3000/customers';
constructor(private http: HttpClient) { }
postCustomer(cust: Customer) {
console.log('front end cust value' + cust + '---' + cust.contract + '----' + cust.customerID + '----' + cust.customerName);
return this.http.post(this.baseURL, cust);
}
}
customer.component.html (only snippet)
Option 1 :
<input type="text" name="startDate" #name="ngModel" [(ngModel)]="customerService.selectedCustomer.contract.startDate"
placeholder="Enter Contract Start Date">
Option 2 :
<input type="text" name="startDate" #name="ngModel" [(ngModel)]="customerService.selectedCustomer.contract[0].startDate"
placeholder="Enter Contract Start Date">
Option 3 :
<input type="text" name="startDate" #name="ngModel" [(ngModel)]="customerService.selectedCustomer.contract[].startDate"
placeholder="Enter Contract Start Date">
Option 4:
<input type="text" name="startDate" #name="ngModel" [(ngModel)]="customerService.selectedCustomer.contract[startDate]"
placeholder="Enter Contract Start Date">

No provider for ControlContainer and No provider for ControlContainer

I am working on an application using Angular2.
I am trying to use Reactive Forms in my application but I am running into some errors :
The first error is about NgControl as below:
No provider for NgControl ("
div class="col-md-8"
[ERROR ->]input class="form-control"
id="productNameId"
"): ProductEditComponent#16:24
The second error is about ControlContainer as below:
No provider for ControlContainer ("
div
[ERROR ->]div formArrayName="tags">
div class="row">
button cl"):
Htmm file is as below:
<div class="panel panel-primary">
<div class="panel-heading">
{{pageTitle}}
</div>
<div class="panel-body">
<form class="form-horizontal"
novalidate
(ngSubmit)="saveProduct()"
formGroup="productForm" >
<fieldset>
<div class="form-group"
[ngClass]="{'has-error': displayMessage.productName }">
<label class="col-md-2 control-label" for="productNameId">Product Name</label>
<div class="col-md-8">
<input class="form-control"
id="productNameId"
type="text"
placeholder="Name (required)"
formControlName="productName" />
<span class="help-block" *ngIf="displayMessage.productName">
{{displayMessage.productName}}
</span>
</div>
</div>
<div formArrayName="tags">
<div class="row">
<button class="col-md-offset-1 col-md-1 btn btn-default"
type="button"
(click)="addTag()">Add Tag
</button>
</div>
<div class="form-group"
*ngFor="let tag of tags.controls; let i=index" >
<label class="col-md-2 control-label" [attr.for]="i">Tag</label>
<div class="col-md-8">
<input class="form-control"
[id]="i"
type="text"
placeholder="Tag"
formControlName="i" />
</div>
</div>
</div>
<!--more piece of code here -->
My component file is as below:
import { Component, OnInit, AfterViewInit, OnDestroy, ViewChildren, ElementRef } from '#angular/core';
import { FormBuilder, FormGroup, FormControl, FormArray, Validators, FormControlName,NgForm } from '#angular/forms';
import { ActivatedRoute, Router } from '#angular/router';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/observable/merge';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { IProduct } from './product';
import { ProductService } from './product.service';
import { NumberValidators } from '../shared/number.validator';
import { GenericValidator } from '../shared/generic-validator';
#Component({
templateUrl: './product-edit.component.html'
})
export class ProductEditComponent implements OnInit, AfterViewInit, OnDestroy {
#ViewChildren(FormControlName, { read: ElementRef }) formInputElements: ElementRef[];
pageTitle: string = 'Product Edit';
errorMessage: string;
productForm: FormGroup;
product: IProduct;
private sub: Subscription;
// Use with the generic validation message class
displayMessage: { [key: string]: string } = {};
private validationMessages: { [key: string]: { [key: string]: string } };
private genericValidator: GenericValidator;
get tags(): FormArray {
return <FormArray>this.productForm.get('tags');
}
constructor(private fb: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private productService: ProductService) {
// Defines all of the validation messages for the form.
// These could instead be retrieved from a file or database.
this.validationMessages = {
productName: {
required: 'Product name is required.',
minlength: 'Product name must be at least three characters.',
maxlength: 'Product name cannot exceed 50 characters.'
},
productCode: {
required: 'Product code is required.'
},
starRating: {
range: 'Rate the product between 1 (lowest) and 5 (highest).'
}
};
// Define an instance of the validator for use with this form,
// passing in this form's set of validation messages.
this.genericValidator = new GenericValidator(this.validationMessages);
}
ngOnInit(): void {
this.productForm = this.fb.group({
productName: ['', [Validators.required,
Validators.minLength(3),
Validators.maxLength(50)]],
productCode: ['', Validators.required],
starRating: ['', NumberValidators.range(1, 5)],
tags: this.fb.array([]),
description: ''
});
// Read the product Id from the route parameter
this.sub = this.route.params.subscribe(
params => {
let id = +params['id'];
this.getProduct(id);
}
);
}
ngOnDestroy(): void {
this.sub.unsubscribe();
}
ngAfterViewInit(): void {
// Watch for the blur event from any input element on the form.
let controlBlurs: Observable<any>[] = this.formInputElements
.map((formControl: ElementRef) => Observable.fromEvent(formControl.nativeElement, 'blur'));
// Merge the blur event observable with the valueChanges observable
Observable.merge(this.productForm.valueChanges, ...controlBlurs).debounceTime(800).subscribe(value => {
this.displayMessage = this.genericValidator.processMessages(this.productForm);
});
}
addTag(): void {
this.tags.push(new FormControl());
}
getProduct(id: number): void {
this.productService.getProduct(id)
.subscribe(
(product: IProduct) => this.onProductRetrieved(product),
(error: any) => this.errorMessage = <any>error
);
}
onProductRetrieved(product: IProduct): void {
if (this.productForm) {
this.productForm.reset();
}
this.product = product;
if (this.product.id === 0) {
this.pageTitle = 'Add Product';
} else {
this.pageTitle = `Edit Product: ${this.product.productName}`;
}
// Update the data on the form
this.productForm.patchValue({
productName: this.product.productName,
productCode: this.product.productCode,
starRating: this.product.starRating,
description: this.product.description
});
this.productForm.setControl('tags', this.fb.array(this.product.tags || []));
}
deleteProduct(): void {
if (this.product.id === 0) {
// Don't delete, it was never saved.
this.onSaveComplete();
} else {
if (confirm(`Really delete the product: ${this.product.productName}?`)) {
this.productService.deleteProduct(this.product.id)
.subscribe(
() => this.onSaveComplete(),
(error: any) => this.errorMessage = <any>error
);
}
}
}
saveProduct(): void {
if (this.productForm.dirty && this.productForm.valid) {
// Copy the form values over the product object values
let p = Object.assign({}, this.product, this.productForm.value);
this.productService.saveProduct(p)
.subscribe(
() => this.onSaveComplete(),
(error: any) => this.errorMessage = <any>error
);
} else if (!this.productForm.dirty) {
this.onSaveComplete();
}
}
onSaveComplete(): void {
// Reset the form to clear the flags
this.productForm.reset();
this.router.navigate(['/products']);
}
}
I am trying to solve this problem for more than 2 days but I still do not have a solution. I have seen many other answers in stackoverflow but none of them is solving my problem.
Import both Forms Module and ReactiveFormsModule from #angular/forms in the file app.module.ts