Angular: json to formBuilder to json - json

From my server I am receiving a json that contains questions and different options:
[
{"description":"what is a color","questionID":"1","options":[{"response":"blue","optionID":"1"},{"response":"red","optionID":"2"},{"response":"football","optionID":"3"}]},
{"description":"what is a sport","questionID":"2","options":[{"response":"working","optionID":"4"},{"response":"playing","optionID":"5"},{"response":"dad","optionID":"6"},{"response":"chess","optionID":"7"}]}
]
With the formbuilder I created a form for this:
If I press submit I would like to send this json to my server:
{
"answers": [{"questionID":"1", "selectedoptionIDS":[{"selectedOptionID":"2"}]},
{"questionID":"2", "selectedoptionIDS":[{"selectedOptionID":"1"},{"selectedOptionID":"3"}]}
],
"email": "test#test.com"
}
I know how I can build my form with the formbuilder but when I press submit I am having troubles with responding the right JSON. Certainly because I can not work with this checkboxes. Can somebody help me with this?
Html page
<form [formGroup]="examForm" (ngSubmit)="onSubmit(examForm.value)">
<div formArrayName="answers">
<div *ngFor="let question of questions; let i=index">
<label>{{i+1}}) {{question.description}}</label>
<br />
<div *ngFor="let response of question.options">
<input type="checkbox" value="response.optionID" />
{{response.response}}
</div>
</div>
</div>
<label>Email:</label>
<input class="form-control" id="email" type="text" formControlName="email">
<div class="block-content block-content-full block-content-sm bg-body-light font-size-sm">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</form>
TS Page
import { Component, OnInit } from '#angular/core';
import { ExamSimulatorService } from '../services/exam-simulator.service';
import { ActivatedRoute } from '#angular/router';
import { FormBuilder, FormArray } from '#angular/forms';
#Component({
selector: 'app-exam',
templateUrl: './exam.component.html'
})
export class ExamComponent implements OnInit {
software;
questions;
examForm;
constructor(
private examSimulatorService: ExamSimulatorService,
private formBuilder: FormBuilder
) {
this.examForm = this.formBuilder.group({
email: "",
answers: this.formBuilder.array([
this.initAnswer()])
})
}
buildForm() {
for (var i = 0; i < this.questions.length() + 1; i++) {
this.addAnswer();
}
}
initAnswer() {
return this.formBuilder.group({
questionID: "",
selectedOptionIDs: this.formBuilder.array([
this.initOptions()
])
})
}
initOptions() {
return this.formBuilder.group({
selectedOptionID: ""
})
}
addAnswer() {
const answers = <FormArray>this.examForm["controls"]["answers"];
answers.push(this.initAnswer())
console.log(this.examForm);
}
addOption(i) {
const options = <FormArray>this.examForm["controls"]["answers"]["controls"][i]["controls"]["selectedOptionIDs"]
options.push(this.initOptions())
}
ngOnInit() {
this.activatedRoute.paramMap
.subscribe(params => {
this.software = params['params']['software'];
this.examSimulatorService.getExam(this.software).subscribe(response =>
this.questions = response["questions"]["questionList"]);
})
setTimeout(() => this.buildForm(), 200)
}
onSubmit(values) {
//this.examSimulatorService.addQuestion(values).subscribe(
// (responses) => {
// console.log(responses);
// });
//this.options.clear();
console.log(values);
}
}

Instead of using your own model you can use full help from the Reactive form. The final model is not exactly you required but you can make workaround from it. You can see the working example at here https://stackblitz.com/edit/angular-1gtfmf
Component
export class ExamComponent implements OnInit {
#Input() name: string;
questionsList;
examForm: FormGroup;
dataModel: any; //active model
constructor(
private examSimulatorService: QuestionService,
private formBuilder: FormBuilder
) { }
get question(): FormGroup {
return this.formBuilder.group(
{
questionID: "",
description: "",
options: this.formBuilder.array([])
}
);
}
get option(): FormGroup {
return this.formBuilder.group({
optionID: "",
response: "",
selected: false
});
}
ngOnInit() {
this.dataModel = Object.create(null);
this.examForm = this.formBuilder.group({
email: ['', [Validators.required]],
questions: this.formBuilder.array([])
});
this.examSimulatorService.getAllQuestion().subscribe(response => {
this.questionsList = response.data;
this.loadForm(this.questionsList);
console.log(this.questionsList);
});
this.examForm.valueChanges.subscribe(data => {
this.dataModel = data;
});
}
loadForm(data) {
for (let ques = 0; ques < data.length; ques++) {
const questionsFormArray = this.examForm.get("questions") as FormArray;
questionsFormArray.push(this.question);
for (let opt = 0; opt < data[ques].options.length; opt++) {
const optionsFormsArray = questionsFormArray.at(ques).get("options") as FormArray;
optionsFormsArray.push(this.option);
}
}
this.examForm.controls.questions.patchValue(data);
}
showSavedValue() {
return this.dataModel;
}
showValue() {
return this.examForm.getRawValue();
}
onSubmit(values) {
console.log(values);
}
}
Html
<form [formGroup]="examForm" (ngSubmit)="onSubmit(examForm.value)">
<div>
<label>Email:</label>
<input class="form-control" id="email" type="text" formControlName="email">
</div>
<div formArrayName="questions">
<div *ngFor="let question of examForm.get('questions').controls;let questionIndex=index" [formGroupName]="questionIndex">
<label>{{questionIndex+1}} </label> {{examForm.value.questions[questionIndex].description}}
<div formArrayName="options">
<div *ngFor="let option of question.get('options').controls; let optionIndex=index" [formGroupName]="optionIndex">
<input type="checkbox" formControlName="selected" value="" /> {{examForm.value.questions[questionIndex].options[optionIndex].response}}
</div>
</div>
</div>
<div class="block-content block-content-full block-content-sm bg-body-light font-size-sm">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</div>
</form>
<pre> {{showSavedValue() | json }}
<pre>{{showValue() | json}}</pre>

Related

My codes are not doing get,set,post so what is my codes error how can i fix it?

these are my .ts codes i write these becasue i want to get product details and delete
import { Component, OnInit } from '#angular/core';
import {FormGroup,FormBuilder, FormControl, Validators} from "#angular/forms"
import { ToastrService } from 'ngx-toastr';
import { Product } from 'src/app/models/product';
import { ProductService } from 'src/app/services/product.service';
import { LocalStorageService } from 'src/app/services/local-storage.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-product-delete',
templateUrl: './product-delete.component.html',
styleUrls: ['./product-delete.component.css']
})
export class ProductDeleteComponent implements OnInit {
products: Product[] = [];
dataLoaded = false;
deleteProductForm:FormGroup;
product :Product
productId :number;
constructor(private formBuilder:FormBuilder,
private productService:ProductService
, private toastrService:ToastrService
,private router:Router,
private localStorageService:LocalStorageService) { }
ngOnInit(): void {
this.createdeleteProductForm();
}
createdeleteProductForm(){
this.deleteProductForm = this.formBuilder.group({
productId:["", Validators.required],
})
}
getbyid() {
Number(localStorage)
Number(this.productService)
this.productService.getbyid(Number(localStorage.getItem("productId"))).subscribe(
(response) => {
this.products = response.data;
this.dataLoaded = true;
this.deleteProductForm.setValue({
productId: this.product,
categoryId: this.product.categoryId,
productName: this.product.productName,
unitPrice: this.product.unitPrice
});
},
(responseError) => {
this.toastrService.error(responseError.error);
}
)
}
deleteProduct() {
if (this.deleteProductForm.valid) {
let productModel = Object.assign({}, this.deleteProductForm.value);
productModel.productId=parseInt(productModel.productId);
this.productService.delete(productModel).subscribe(
(response) => {
this.toastrService.success('Lütfen tekrar giriş yapınız');
this.router.navigate(['/login']);
},
(responseError) => {
this.toastrService.error(responseError.error);
}
);
} else {
this.toastrService.error('Bir hata oluştu.');
}
}
}
these are my html codes i trying to do when user sign in a productId after that click the button delete the product in that ıd
<div class="card">
<div class="card-header"><h5 class="title">Ürün Sil</h5></div>
<div class="card-body">
<form [formGroup]="deleteProductForm">
<div class="mb-3">
<label for="productId">ÜrünId'si</label>
<div class="form-group">
<input type="number"
id="productId"
formControlName="productId" class="form-control"
placeholder="productId"/>
</div>
<div class="card-footer" style="background-color: rgb(4, 62, 255)">
<button
class="btn btn-lg btn-outline-success float-end"
(click)="deleteProduct()"
>
Sils
</button>
</div>
and these are my service
delete(product:Product):Observable<ResponseModel>{
let newPath = this.apiUrl + 'products/delete';
return this.httpClient.post<ResponseModel>(newPath, product );
}
getbyid(productId:number) : Observable<ListResponseModel<Product>> {
let newPath = this.apiUrl + 'products/getbyid?productId=' + productId;
return this.httpClient.get<ListResponseModel<Product>>(newPath);
}
what i'm going for is that when the user goes on a productId click the button, I want to delete the data including the Id first, but what's the null time on main at the moment?
note:Value cannot be null. says back-end
in html POST https://localhost:44314/api/products/delete
[HTTP/2 500 Internal Server Error 9591ms gives this error
First of all, have you checked the value of product in the call of delete ?
Also, maybe it's the httpClient.delete you need since it's the best way to delete an object to the back end. I suggest this:
https://angular.io/guide/http#making-a-delete-request

Angular App deosn't dispay data from table althgouh the exist in the console

One of the most important things to make a website friendly is the response time, and pagination comes for this reason. For example, this bezkoder.com website has hundreds of tutorials, and we don’t want to see all of them at once. Paging means displaying a small number of all, by a page.
here is the class for the "Dossier entity":
export class Dossier {
id?: any;
title?:string;
creationDate?:string;
statusDossier?:string;
documentNumber?:number;
activityTitle?:string;
published?:boolean;
}
This is a kind of server-side paging, where the server sends just a single page at a time. ngx-pagination supports this scenario, so We actually only need to use tutorials and totalItems when working with this library.
This service will use Angular HttpClient to send HTTP requests.
services/dossier.service.ts
import { Injectable } from '#angular/core';
import {HttpClient} from "#angular/common/http";
import {Observable} from "rxjs";
import {Dossier} from "../models/dossier.model";
const baseUrl = 'http://localhost:8080/api/dossiers';
#Injectable({
providedIn: 'root'
})
export class DossierService {
constructor(private http: HttpClient) { }
getAll(params: any): Observable<any> {
return this.http.get<any>(baseUrl, { params });
}
get(id: any): Observable<Dossier> {
return this.http.get(`${baseUrl}/${id}`);
}
create(data: any): Observable<any> {
return this.http.post(baseUrl, data);
}
update(id: any, data: any): Observable<any> {
return this.http.put(`${baseUrl}/${id}`, data);
}
delete(id: any): Observable<any> {
return this.http.delete(`${baseUrl}/${id}`);
}
deleteAll(): Observable<any> {
return this.http.delete(baseUrl);
}
findByTitle(title: any): Observable<Dossier[]> {
return this.http.get<Dossier[]>(`${baseUrl}?title=${title}`);
}
}
We can customize the label displayed on the “previous”/”next” link using previousLabel/nextLabel, and enable “responsive” to hide individual page links on small screens.
For pagination, we’re gonna use DossierService.getAll() methods.
components/dossiers-list/dossiers-list.component.ts
export class DossiersListComponent implements OnInit {
dossiers: Dossier[] = [];
currentDossier: Dossier = {};
currentIndex = -1;
title = '';
page = 1;
count = 0;
pageSize = 3;
pageSizes = [3, 6, 9];
constructor(private dossierService: DossierService) { }
ngOnInit(): void {
this.retrieveDossiers();
}
getRequestParams(searchTitle: string, page: number, pageSize: number): any {
let params: any = {};
if (searchTitle) {
params[`title`] = searchTitle;
}
if (page) {
params[`page`] = page - 1;
}
if (pageSize) {
params[`size`] = pageSize;
}
return params;
}
retrieveDossiers(): void {
const params = this.getRequestParams(this.title, this.page, this.pageSize);
this.dossierService.getAll(params)
.subscribe({
next: (data) => {
const { dossiers, totalItems } = data;
this.dossiers = dossiers;
this.count = totalItems;
console.log(data);
},
error: (err) => {
console.log(err);
}
});
}
handlePageChange(event: number): void {
this.page = event;
this.retrieveDossiers();
}
handlePageSizeChange(event: any): void {
this.pageSize = event.target.value;
this.page = 1;
this.retrieveDossiers();
}
refreshList(): void {
this.retrieveDossiers();
this.currentDossier = {};
this.currentIndex = -1;
}
setActiveTutorial(dossier: Dossier, index: number): void {
this.currentDossier = dossier;
this.currentIndex = index;
}
removeAllDossiers(): void {
this.dossierService.deleteAll()
.subscribe({
next: res => {
console.log(res);
this.refreshList();
},
error: err => {
console.log(err);
}
});
}
searchTitle(): void {
this.page = 1;
this.retrieveDossiers();
}
}
and finally here is the html file:
<div class="list row">
<div class="col-md-8">
<div class="input-group mb-3">
<input
type="text"
class="form-control"
placeholder="Search by title"
[(ngModel)]="title"
/>
<div class="input-group-append">
<button
class="btn btn-outline-secondary"
type="button"
(click)="searchTitle()"
>
Search
</button>
</div>
</div>
</div>
<div class="col-md-12">
<pagination-controls
previousLabel="Prev"
nextLabel="Next"
[responsive]="true"
(pageChange)="handlePageChange($event)"
></pagination-controls>
</div>
<div class="col-md-6">
<h4>Tutorials List</h4>
<ul class="list-group">
<li
class="list-group-item"
*ngFor="
let tutorial of dossiers | paginate : {
itemsPerPage: pageSize,
currentPage: page,
totalItems: count
};
let i = index
"
[class.active]="i == currentIndex"
(click)="setActiveTutorial(tutorial, i)"
>
{{ tutorial.title }}
</li>
</ul>
</div>
<div class="col-md-6">
<!-- <app-tutorial-details-->
<!-- [viewMode]="true"-->
<!-- [cu]="currentDossier"-->
<!-- ></app-tutorial-details>-->
</div>
<div class="mt-3">
<button class="m-3 btn btn-sm btn-danger" (click)="removeAllDossiers()">
Remove All
</button>
Items per Page:
<select (change)="handlePageSizeChange($event)">
<option *ngFor="let size of pageSizes" [ngValue]="size">
{{ size }}
</option>
</select>
</div>
</div>

Sending input value with formArray values?

I have a FormArray that can input new fields and can send the value of the whole form on button click, however I am trying to add an input that is tied to the name held within the same object data, but I cannot seem to get it to display let along send with the rest of the updated data...
Here is my blitz
html
<form [formGroup]="myForm">
<div formArrayName="companies">
<!-- I am wanting to update and send the name of the input also... -->
<input formControlName="name"/>
<div *ngFor="let comp of myForm.get('companies').controls; let i=index">
<legend><h3>COMPANY {{i+1}}: </h3></legend>
<div [formGroupName]="i">
<div formArrayName="projects">
<div *ngFor="let project of comp.get('projects').controls; let j=index">
<legend><h4>PROJECT {{j+1}}</h4></legend>
<div [formGroupName]="j">
<label>Project Name:</label>
<input formControlName="projectName" /><span><button (click)="deleteProject(comp.controls.projects, j)">Delete Project</button></span>
</div>
</div>
<button (click)="addNewProject(comp.controls.projects)">Add new Project</button>
</div>
</div>
</div>
</div><br>
<button (click)="submit(myForm.value)">send</button>
</form>
.ts
export class AppComponent {
data = {
companies: [
{
name: "example company",
projects: [
{
projectName: "example project",
}
]
}
]
}
myForm: FormGroup;
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
companies: this.fb.array([])
})
this.setCompanies();
}
addNewProject(control) {
control.push(
this.fb.group({
projectName: ['']
}))
}
deleteProject(control, index) {
control.removeAt(index)
}
setCompanies() {
let control = <FormArray>this.myForm.controls.companies;
this.data.companies.forEach(x => {
control.push(this.fb.group({
name: x.name,
projects: this.setProjects(x) }))
})
}
setProjects(x) {
let arr = new FormArray([])
x.projects.forEach(y => {
arr.push(this.fb.group({
projectName: y.projectName
}))
})
return arr;
}
submit(value) {
console.log(value);
}
}
Because you are using a controlArray you will need to move the input within the scope of the [formGroupName]="i" as formControlName="name" is a child of [formGroupName]="i".
<legend><h3>COMPANY {{i+1}}: </h3></legend>
<div [formGroupName]="i">
<input formControlName="name"/>

How to post an object to an array in a json using Angular 6

So I'm testing Angular 6 functionality out for fun to learn it and running a json-server to load a db.json to a localhost server to acquire via service calls which you can see here
{
"customers": {
"testingTitle": "Testing Title",
"trainData":[
{
"id": 1,
"name": "Test Name 1",
"email": "customer001#email.com",
"tel": "0526252525"
},
{
"id": 2,
"name": "Test Name 2",
"email": "customer002#email.com",
"tel": "0527252525"
},
{
"id": 3,
"name": "Customer003",
"email": "customer003#email.com",
"tel": "0528252525"
},
{
"id": 4,
"name": "123",
"email": "123",
"tel": "123"
}
]
}
I have a test.service.ts as followed which picks up the service:
import { Injectable } from '#angular/core';
import {HttpClient, HttpResponse, HttpErrorResponse, HttpHeaders, HttpParams} from '#angular/common/http';
import { Observable } from 'rxjs/Rx';
import { catchError, map } from 'rxjs/operators';
import 'rxjs/add/observable/throw';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
class Test {
testingTitle: string;
trainData:[
{
id : number;
name: string;
email: string;
tel: string;
}
];
}
#Injectable({providedIn: 'root'})
export class TestService {
constructor(private http: HttpClient) {}
public getAllTests(): Observable<Test[]>{
const params = new HttpParams().set('_page', "*").set('_limit', "*");
return this.http.get<Test[]>("http://localhost:3000/customers", {params}).pipe(map(res => res));
}
public postTests(object) {
return this.http.post("http://localhost:3000/customers", object).subscribe(data => {console.log("POST Request is successful ", data);},error => {console.log("Error", error);});
}
}
I have my test.ts which controls my calls etc.
import { Component, OnInit } from '#angular/core';
import { HttpClient } from "#angular/common/http";
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import {FormBuilder, FormControl, FormGroup} from "#angular/forms";
import {TestService} from "./test.service";
class Customer {
id : number;
name: string;
email: string;
tel: string;
}
#Component({
selector: 'sample-template',
templateUrl: './test.component.html'})
export class TestComponent implements OnInit {
testForm: FormGroup;
testForm2: FormGroup;
public test: any;
name: string = '';
email: string = '';
tel: string = '';
public id: any;
constructor(private httpClient:HttpClient, private fb: FormBuilder, private TestService: TestService) {}
loadTasks(): void{
this.TestService.getAllTests().subscribe(response => {this.test = response;
console.log(this.test)})
}
ngOnInit() {
let trainData = [];
this.loadTasks();
this.testForm = this.fb.group({
testCd: 'Select'
});
this.testForm2 = this.fb.group({
id: this.id,
name: this.name,
email: this.email,
tel: this.tel
})
}
changeDropdown(formControl: FormControl, option: string): void {
formControl.patchValue(option);
console.log(option);
}
submitForm(){
let last:any = this.test[this.test.length-1];
this.id = last.id+1;
console.log(this.id);
this.testForm2.value.id = this.id;
console.log(this.testForm2.value);
this.TestService.postTests(this.testForm2.value);
}
}
And my html page which includes the following:
<label class="modelo-label">{{test?.testingTitle}}</label>
<form [formGroup]="testForm">
<div class="dropdown modelo-dropdown">
<label for="testCd" class="modelo-label">Testing</label>
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" role="button" id="testCd" aria-haspopup="true" aria-expanded="true">{{testForm.get('testCd').value}}</button>
<div class="dropdown-menu modelo-dropdown-menu" aria-labelledby="testCd">
<a class="dropdown-item" *ngFor="let tests of test?.trainData; let i = index" id="tests.name" (click)="changeDropdown(testForm.get('testCd'), tests.name)">{{tests.name}}</a>
</div>
</div>
<form [formGroup]="testForm2" (ngSubmit)="submitForm()">
<div class="row">
<div class="col-12 col-sm-4 group">
<input type="text" id="name" formControlName="name" class="modelo-text-input"
[ngClass]="{'ng-not-empty' : testForm2.get('name').value.length !== 0}">
<label for="name">Name</label>
</div>
</div>
<div class="row">
<div class="col-12 col-sm-4 group">
<input type="text" id="email" formControlName="email" class="modelo-text-input"
[ngClass]="{'ng-not-empty' : testForm2.get('email').value.length !== 0}">
<label for="email">Email</label>
</div>
</div>
<div class="row">
<div class="col-12 col-sm-4 group">
<input type="text" id="tel" formControlName="tel" class="modelo-text-input"
[ngClass]="{'ng-not-empty' : testForm2.get('tel').value.length !== 0}">
<label for="tel">Telephone #</label>
</div>
</div>
<div class="col-1 group generateButton">
<button class="btn btn-primary" type="submit">Submit Info</button>
</div>
</form>
My Question is, I'm have everything set up for a post and what I'm trying to do is post testForm2.value to the json but under "trainData":[{}] that's within the JSON. I'm able to do so if I just drop all other objects inside the json and have just the array after "customers":... What exactly am I missing? I'm actually confusing myself right now and I may be overthinking this by alot. The post I have currently in this code works if I have just the array after "customers":.... so instead of me passing object which is the testForm2.value what else do I need to do? I hope this makes sense.
You have some strange things in your code. First :
In you API
return this.http.get<Test[]>("http://localhost:3000/customers", {params}).pipe(map(res => res));
I think what you want to do here is : (the pipe is useless you dont use it and it's not an array)
return this.http.get<Test>("http://localhost:3000/customers",{params});
In your component you want to push the update trainData list
submitForm(){
const lastTrainData = this.test.trainData[this.test.trainData.length-1];
const newTrainData = this.testForm2.value;
newTrainData.id = lastTrainData.id + 1;
this.test.trainData.push(newTrainData);
this.TestService.postTests(this.test);
}

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