How to render updated data in all open tabs in angular - javascript

I am building a demo App using Angualr,Node,express and socketio. I have a table with server side pagination and a button (model) to add records in table. The idea is when I create new record it should update the mongodb collection and emit an event and reload the fresh data in all the open tabs. This app is working fine in one tab(view) but if i see other tab the component has the fresh data but its not reflecting on the view. It looks like even though the variable in component has new value then also angular is not rendering the view in all the tabs. Can any one help me on this please?
component.ts
import { Component, OnInit } from '#angular/core';
import { StockService } from './stock.service'
import {NgbModal, ModalDismissReasons} from '#ng-bootstrap/ng-bootstrap';
import {io} from 'socket.io-client';
const socket= io('http://localhost:5000',{transports: ['websocket', 'polling', 'flashsocket']})
#Component({
selector: 'app-stock',
templateUrl: './stock.component.html',
styleUrls: ['./stock.component.css']
})
export class StockComponent implements OnInit {
POSTS: any;
page = 1;
total=1
itemPerPage=5
loading: boolean | undefined;
public record={
Open:0,
Close:0
}
constructor(private stockService: StockService,private modalService: NgbModal) {
}
closeResult: string='';
open(content:any) {
this.modalService.open(content, {ariaLabelledBy: 'modal-basic-title'}).result.then((result) => {
this.closeResult = `Closed with: ${result}`;
}, (reason) => {
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
});
}
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}`;
}
}
saveData(modal:any){
this.stockService.createPost(this.record) .subscribe(
response => {
},
error => {
console.log(error);
});
modal.close('Data saved')
}
ngOnInit(): void {
socket.on('stockUpdated',async()=>{
this.fetchPosts(this.page,this.itemPerPage);
})
this.fetchPosts(this.page,this.itemPerPage);
}
fetchPosts(page:number,limit:number): void {
this.loading = true;
this.stockService.getAllPosts(page,limit)
.subscribe(
response => {
this.POSTS = response.docs;
this.total=response.totalDocs
this.loading = false;
console.log('POSTS: ',this.POSTS)
console.log('total: ',this.total)
},
error => {
console.log(error);
});
}
getPage(event:any){
this.page = event;
this.fetchPosts(this.page,this.itemPerPage);
}
}
service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
const endpoint = 'http://localhost:5000/api/stock';
#Injectable({
providedIn: 'root'
})
export class StockService {
constructor(private http: HttpClient) { }
getAllPosts(page:number=1,limit:number=10): Observable<any> {
console.log('params: ',page)
return this.http.get(`${endpoint}?page=${page}&limit=${limit}`);
}
createPost(params:any=1): Observable<any> {
return this.http.post(endpoint, params );
}
}
html
<div class="container">
<h3 class="text-center mt-5 mb-5">
Stock table
</h3>
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Open</th>
<th>Close</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let post of POSTS | paginate : { id: 'server', itemsPerPage: itemPerPage, currentPage: page, totalItems: total };
let i = index" >
<th scope="row">{{post.Date}}</th>
<td>{{post.Open}}</td>
<td>{{post.Close}}</td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-center">
<div class="spinner" [ngClass]="{ 'hidden': !loading }"></div>
<pagination-controls (pageChange)="getPage($event)" id="server"></pagination-controls>
</div>
<br>
<button class="btn btn-lg btn-outline-primary" (click)="open(mymodal)">Add New</button>
</div>
<ng-template #mymodal let-modal>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">Add new record</h4>
<button type="button" class="close" aria-label="Close" (click)="modal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="recipient-name" class="col-form-label">Open:</label>
<input name='open' [(ngModel)]="record.Open" type="text" class="form-control" id="recipient-name">
</div>
<div class="form-group">
<label for="recipient-name" class="col-form-label">Close:</label>
<input name='close' [(ngModel)]="record.Close" type="text" class="form-control" id="recipient-name">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="modal.close('Cancel click')">Cancel</button>
<button type="button" class="btn btn-primary" data-dismiss="modal" (click)="saveData(modal)">Save</button>
</div>
</ng-template>

Related

Getting POST 403 error (Invalid CORS request) while creating a new entry (Angular 12)

API works fine on postman, but shows post 403 error on running the application. I am guessing the error is on the UI side. Please help me correct this!
I'm sharing the screenshots of the headers and response page of network
Headers section
Response section
Here's the relevant code
entry.service.ts
{
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
const AUTH_API = 'http://localhost:8080/Disaster-Analysis-0.0.1-SNAPSHOT%20(5)/';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable({
providedIn: 'root'
})
export class EntryService {
constructor(private http: HttpClient) { }
public getEntry() {
return this.http.get(AUTH_API + 'oldEntry', httpOptions);
}
public addEntry(entry:any) {
return this.http.post(AUTH_API + 'newEntry',entry, httpOptions);
}}
add-entry.component.ts
{
import { Component, OnInit } from '#angular/core';
import { FormControl, FormGroup } from '#angular/forms';
import { EntryService } from '../_services/entry.service';
#Component({
selector: 'app-add-entry',
templateUrl: './add-entry.component.html',
styleUrls: ['./add-entry.component.css']
})
export class AddEntryComponent implements OnInit {
alert = false;
addNewEntry = new FormGroup({
pincode: new FormControl(''),
majorDamageInSubdivision_A: new FormControl(''),
minorDamageInSubdivision_A: new FormControl(''),
majorDamageInSubdivision_B: new FormControl(''),
minorDamageInSubdivision_B: new FormControl('')
})
constructor(private entry: EntryService) { }
ngOnInit(): void {
}
createEntry() {
this.alert = true;
// this.addEntry.reset()
this.entry.addEntry(this.addNewEntry.value).subscribe((result: any) => {
console.log("Get data from service", result)
})
console.log(this.addNewEntry.value);
}
closeAlert() {
this.alert = false;
}
}
add-entry.component.html
{
<div class="jumbotron">
<h3 class='text-center' >Add Entries Form</h3>
</div>
<div *ngIf="alert" class="alert alert-success alert-dismissible fade show" role="alert">
<strong>Success!</strong> Data added successfully!
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<form [formGroup]="addNewEntry" (ngSubmit)="createEntry()">
<div class="form-group">
<label >Pincode</label>
<input type="text" class="form-control" placeholder="Enter Pincode" formControlName="pincode">
<br>
<label>Major Damage Score in Subdivision A</label>
<input type="text" class="form-control" formControlName="majorDamageInSubdivision_A">
<br>
<label>Minor Damage Score in Subdivision A</label>
<input type="text" class="form-control" formControlName="minorDamageInSubdivision_A">
<br>
<label>Major Damage Score in Subdivision B</label>
<input type="text" class="form-control" formControlName="majorDamageInSubdivision_B">
<br>
<label>Minor Damage Score in Subdivision B</label>
<input type="text" class="form-control" formControlName="minorDamageInSubdivision_B">
<br>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
}

Field not empty but the modal is displayed - Angular

I am learning Angular, I created in my page 3 fields: Article, Quantity, Limit.
If each field is empty, a modal appears !
For now, I have no problem, the modal appears like I want.
Now, I have two questions please:
1- If my fields are completed, and that the user clicks on OK, my modal appears ?? How to I change this ?
2- You think that it is possible for example that the field quantity to be completed obligatorily. For example, if the field quantity is completed and no the fields article and limit. There is no modal to display.
I am stuck on these two questions...
component.html
<br><br>
<label for='quantity'>Article</label>
<div class="input-group">
<input type="text" class="form-control" id="article" name="article">
</div>
<label for='quantity'>Quantity</label>
<div class="input-group">
<input type="number" class="form-control" id="quantity" name="quantity">
</div>
<label for='quantity'>Limit</label>
<div class="input-group">
<input type="number" class="form-control" id="orderLimit" name="orderLimit">
</div>
<br>
<div class="row">
<div class="col-12">
<button class="btn btn-lg btn-outline-primary" (click)="open(mymodal)"> ok </button>
</div>
</div>
<!-- Modal -->
<ng-template #mymodal let-modal>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">Error message</h4>
<button type="button" class="close" aria-label="Close button" aria-describedby="modal-title" (click)="modal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="table-responsive">
<table class="table table-striped">
<thead>
</thead>
<tbody>
<td>Fields cannot be empty</td>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="modal.close('Save click')">Ok</button>
</div>
</ng-template>
component.ts
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'appBootstrap';
closeResult: string | undefined;
constructor(private modalService: NgbModal) {}
open(content: any) {
this.modalService
.open(content, { ariaLabelledBy: 'modal-basic-title' })
.result.then(
result => {
this.closeResult = `Closed with: ${result}`;
},
reason => {
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
}
);
}
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}`;
}
}
}
I can put my code below if you want:
https://stackblitz.com/edit/angular-ivy-kr7cyd?file=src/app/app.component.ts
1- If my fields are completed, and that the user clicks on OK, my
modal appears ?? How to I change this ?
No, It should not appear as you mentioned the modal should only open when the fields are empty.
2- You think that it is possible for example that the field quantity
to be completed obligatorily. For example, if the field quantity is
completed and no the fields article and limit. There is no modal to
display.
You can do that if you want. You can use ngModel and bind each input property that can help you track their values as soon as the user updates it. When you open or close the modal you can do it in the component method instead closing modal using the view reference variable.
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'appBootstrap';
article = 0, quantity = 0, orderLimit = 0;
closeResult: string | undefined;
constructor(private modalService: NgbModal) {}
open(content: any) {
this.modalService
.open(content, { ariaLabelledBy: 'modal-basic-title' })
.result.then(
result => {
this.closeResult = `Closed with: ${result}`;
},
reason => {
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
}
);
}
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}`;
}
}
// this should be checked everytime when user clicks 'Ok'
openModal() {
// check if the values are '0'
// make sure the values come in as Number
if (orderLimit && article && quantity) {
// show modal
} else {
// no need to show modal
}
}
}
<label for='quantity'>Article</label>
<div class="input-group">
<input type="text" class="form-control" id="article" name="article" [(ngModel)]="article">
</div>
<label for='quantity'>Quantity</label>
<div class="input-group">
<input type="number" class="form-control" id="quantity" [(ngModel)]="quantity" name="quantity">
</div>
<label for='quantity'>Limit</label>
<div class="input-group">
<input type="number" class="form-control" id="orderLimit" [(ngModel)]="orderLimit" name="orderLimit">
</div>

Only first line of database code is returning

I am creating a website which calls a list of items from an external API.
The list has an "Order" button alongside it which when pressed, opens a modal with an order form.
The form is submitted, the API correctly receives the form data. But I am trying to send the row information to the API aswell to inform what has actually been ordered.
The problem is that only the first row returned is ever sending no matter which line is "Ordered".
My html code file is:
<head>
</head>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<div class="body">
<h1 class="header">Carpet Clearance</h1>
<h2 class="header">
The Mega Day carpet clearance list
</h2>
</div>
<table class="table">
<th>
<td>Range</td>
<td>Size</td>
<td>Colour</td>
<td>Cost</td>
<td>Value</td>
<td>Order</td>
<tr *ngFor="let row of carpet;">
<td class="td2">{{row.ID}}</td>
<td>{{row.Range}}</td>
<td>{{row.Size}}</td>
<td>{{row.Colour}}</td>
<td>{{row.Cost}}</td>
<td>{{row.Value}}</td>
<td><button onclick="document.getElementById('id01').style.display='block'" class="btn">Order Item</button>
<div id="id01" class="w3-modal">
<div class="w3-modal-content w3-animate-bottom w3-card-4">
<header class="w3-container w3-teal">
<span onclick="document.getElementById('id01').style.display='none'"
class="w3-button w3-display-topright">×</span>
<h2 class="body">Order this item</h2>
</header>
<div class="modal">
<p class="form">To order this range, please enter your customer account number, delivery address and Rep ID.</p>
<form class="form" [formGroup]="checkoutForm" (ngSubmit)="onSubmit(checkoutForm.value, row)">
<div>
<label for="Rep ID">
Rep ID<br>
</label>
<input id="Rep" type="text" formControlName="Rep">
</div>
<div>
<label for="Account">
Account Number<br>
</label>
<input id="Account" type="text" formControlName="Account">
</div>
<div>
<label for="address">
Address<br>
</label>
<input id="Address" type="text" formControlName="Address">
</div>
<button class="button" type="submit">Order</button>
</form>
</div>
</div>
</div>
</td>
</tr>
</table>
My TS file is:
import { Component, OnInit } from '#angular/core';
import { ApiService } from '../../shared/api.service';
import { FormBuilder } from '#angular/forms';
#Component({
selector: 'app-carpet',
templateUrl: './carpet.component.html',
styleUrls: ['./carpet.component.scss']
})
export class CarpetComponent implements OnInit {
checkoutForm;
carpet = [];
loadingIndicator = true;
reorderable = true;
columns = [
{ prop: 'ID', summaryFunc: () => null },
{ prop: 'Range', summaryFunc: () => null },
{ prop: 'Colour', summaryFunc: () => null },
{ prop: 'Size', summaryFunc: () => null },
{ prop: 'Cost', summaryFunc: () => null },
{ prop: 'Value', summaryFunc: () => null }
];
constructor(private apiService: ApiService,
private formBuilder: FormBuilder
) {
this.checkoutForm = this.formBuilder.group({
Rep: '',
Account: '',
Address: ''
});
}
ngOnInit() {
this.apiService.getCarpet()
.subscribe((carpet: any[]) => {
this.carpet = carpet;
this.loadingIndicator = false;
});
}
onSubmit(customerData, row) {
const data = {
customerData, row
};
this.apiService.sendFormData(data)
.subscribe();
this.checkoutForm.reset();
console.warn('Your order has been submitted', customerData);
}
}
Can anyone point me towards the error here?

I have an issue when I want to edit my product list and i am getting this error Cannot read property 'title' of null

I have an issue after my data is stored in db and I want to edit it. It is not showing data and I am getting cannot read property error.
Html
admin-products.component.html
<p>
<a routerLink="/admin/products/new" class="btn btn-primary">New Products</a>
</p>
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of products$ | async">
<td>{{ p.title }}</td>
<td>{{ p.price }}</td>
<td><a [routerLink]="['/admin/products/', p.$key]">Edit</a></td>
</tr>
</tbody>
</table>
product-form.component.html
If I check error link in console it is showing an error at all input field. I think mostly error may b here ?
<div class="row">
<div class="col-md-6">
<form #f="ngForm" (ngSubmit)="save(f.value)">
<div class="form-group">
<label for="title">Title</label>
<input
#title="ngModel"
[(ngModel)]="product.title"
name="title"
id="title"
type="text"
class="form-control"
required
/>
<div class="alert alert-danger" *ngIf="title.touched && title.invalid">
Title is required.
</div>
</div>
<div class="form-group">
<label for="price">Price</label>
<div class="input-group-prepend">
<span class="input-group-text"> $ </span>
<input
#price="ngModel"
[(ngModel)]="product.price"
name="price"
id="price"
type="number"
class="form-control"
required
[min]="0"
/>
</div>
<div class="alert alert-danger" *ngIf="price.touched && price.invalid">
<div *ngIf="price.errors.required">Price is required.</div>
<div *ngIf="price.errors.min">Price should be 0 or higher.</div>
</div>
</div>
<div class="form-group">
<label for="category">Category</label>
<select
#category="ngModel"
[(ngModel)]="product.category"
name="category"
id="category"
class="form-control"
required
>
<option value=""></option>
<option *ngFor="let c of categories$ | async" [value]="c.$key">
{{ c.name }}
</option>
</select>
<div
class="alert alert-danger"
*ngIf="category.touched && category.invalid"
>
Category is required
</div>
</div>
<div class="form-group">
<label for="imageUrl">Image Url</label>
<input
#imageUrl="ngModel"
[(ngModel)]="product.imageUrl"
name="imageUrl"
id="imageUrl"
type="text"
class="form-control"
required
url
/>
<div
class="alert alert-danger"
*ngIf="imageUrl.touched && imageUrl.invalid"
>
<div *ngIf="imageUrl.errors.required">Image is required</div>
<div *ngIf="imageUrl.errors.url">Please enter a vaild URL</div>
</div>
</div>
<button class="btn btn-primary">Save</button>
</form>
</div>
<div class="col-md-6">
<div class="card" style="width: 18rem;">
<img
class="card-img-top"
[src]="product.imageUrl"
*ngIf="product.imageUrl"
/>
<div class="card-body">
<h5 class="card-title">{{ product.title }}</h5>
<p class="card-text">{{ product.price | currency: 'USD':true }}</p>
</div>
</div>
</div>
</div>
product-form.component.ts
import { Component } from '#angular/core';
import { CategoryService } from 'src/app/category.service';
import { ProductService } from 'src/app/product.service';
import { Router, ActivatedRoute } from '#angular/router';
import 'rxjs/add/operator/take';
#Component({
selector: 'app-product-form',
templateUrl: './product-form.component.html',
styleUrls: ['./product-form.component.css']
})
export class ProductFormComponent {
categories$;
product = {};
constructor(
private router: Router,
private route: ActivatedRoute,
private categoryService: CategoryService,
private productService: ProductService
) {
this.categories$ = categoryService.getCategories();
// tslint:disable-next-line:prefer-const
let id = this.route.snapshot.paramMap.get('id');
// tslint:disable-next-line:curly
if (id)
this.productService.get(id).valueChanges().take(1).subscribe(p => (this.product = p));
}
save(product) {
this.productService.create(product);
this.router.navigate(['/admin/products']);
}
}
product-service.ts
import { Injectable } from '#angular/core';
import { AngularFireDatabase, AngularFireList } from 'angularfire2/database';
#Injectable()
export class ProductService {
products$: AngularFireList<any[]>;
constructor(private db: AngularFireDatabase) {
this.products$ = this.db.list('/products');
}
create(product) {
return this.products$.push(product);
}
getAll() {
return this.db.list('/products').valueChanges();
}
get(productId) {
return this.db.object('/products/' + productId);
}
}
I have tried this method by using *ngIf , [ngModel]="product?.title"
<input *ngIf="title">
Have you tried to wrap your form with ng-container ?
product-form.component.ts
#Component({
selector: 'app-product-form',
templateUrl: './product-form.component.html',
styleUrls: ['./product-form.component.css']
})
export class ProductFormComponent {
categories$;
product = null;
constructor(
private router: Router,
private route: ActivatedRoute,
private categoryService: CategoryService,
private productService: ProductService
) {
this.categories$ = categoryService.getCategories();
// tslint:disable-next-line:prefer-const
let id = this.route.snapshot.paramMap.get('id');
// tslint:disable-next-line:curly
if (id)
this.productService.get(id).valueChanges().take(1).subscribe(p => (this.product = p));
}
save(product) {
this.productService.create(product);
this.router.navigate(['/admin/products']);
}
}
product-form.component.html
<div class="row">
<div class="col-md-6">
<ng-container *ngIf="product">
<form #f="ngForm" (ngSubmit)="save(f.value)">
<!-- YOUR CODE -->
</form>
</ng-container>
</div>
</div>
Try this,
export class ProductFormComponent {
categories$;
product = {
"title": '',
"price": '',
"category": '',
"imageUrl": '',
};
constructor(
private router: Router,
private route: ActivatedRoute,
private categoryService: CategoryService,
private productService: ProductService
) {
this.categories$ = categoryService.getCategories();
// tslint:disable-next-line:prefer-const
let id = this.route.snapshot.paramMap.get('id');
// tslint:disable-next-line:curly
if (id)
this.productService.get(id).valueChanges().take(1).subscribe(p => (this.product = p));
}
save(product) {
this.productService.create(product);
this.router.navigate(['/admin/products']);
}
}
The reason it is failing because
this.productService.get(id).valueChanges().take(1).subscribe(p => (this.product = p));
the above line will take time to get the response from the firebase, In the meantime, the HTML component will be rendered before that, so it is trying to access product object in the HTML component, since there is no title, price, category, and imageUrl in product object it will cause the error. So assign the empty values, later it is updated once you receive the response

How do you add elements and access values dynamically in Angular?

I am working on a small project that allows the user to add dynamic fields. On clicking a button I have been able to create input fields dynamically. I am trying to access the value of each field and push it to the service. On the other hand, another component should make the number of div depending upon the number of input fields created by the user and each div should contain a title depending upon the user input in the input field.
register.component.html
<h2>Demo App</h2>
<form [formGroup]="myForm">
<button (click)="addRooms()">Add Room </button>
<div formArrayName="addRoom">
<div *ngFor="let r of Rooms.controls; let i=index" [formGroupName]="i">
<mat-form-field>
<input matInput placeholder="Enter A Room Name" formControlName="roomName" (keyup)="abc()"/>
</mat-form-field>
<button (click)="deleteRoom(i)">Delete</button>
</div>
</div>
<input type="button" (click)="getRoomValues()" value="Get">
</form>
register.component.ts
import { Component, OnInit} from '#angular/core';
import { RegisterModel } from '../models/register.model';
import {FormGroup, FormBuilder, Validators, FormArray, FormControl } from '#angular/forms';
import { Router } from '#angular/router';
#Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit, AfterViewInit {
myForm: FormGroup;
room: FormGroup;
constructor(private formBuilder:FormBuilder, private r:Router, private _ele:ElementRef, private _render: Renderer) { }
ngOnInit() {
this.myForm = this.formBuilder.group({
addRoom: this.formBuilder.array([]),
tst: new FormControl()
});
}
getVal(){
console.log(this.myForm.value.tst);
}
get Rooms(){
return this.myForm.get('addRoom') as FormArray;
}
addRooms(){
this.room = this.formBuilder.group({
roomName:new FormControl()
})
this.Rooms.push(this.room);
console.log(this.Rooms);
}
abc(){
console.log(this.room.value.roomName);
}
deleteRoom(i){
this.Rooms.removeAt(i);
}
get roomNames(){
return this.myForm.get('roomNames') as FormArray;
}
getRoomValues(){
console.log(this.myForm.value.addRoom)
}
Below is the code sample for couple of fields with dynamic Add & Remove functionality.
<form [formGroup]="linksForm" class="form-horizontal">
<div class="form-group">
<label class="col-md-2 control-label">Links
</label>
<div class="col-md-10">
<div formArrayName="links" *ngFor="let item of linksForm.get('links').controls; let i = index;">
<div [formGroupName]="i">
<div class="col-md-5">
<input class="form-control col-md-5" formControlName="name" placeholder="Name">
</div>
<div class="col-md-5">
<input type="url" pattern="https?://.+" placeholder="http://example.com" class="form-control col-md-5"
formControlName="link">
</div>
<div class="col-md-2">
<button class="btn btn-warning btn-xs m-t-sm" type="button" (click)="removeItem(i)">
<i name="save" class="fa fa-trash"></i>
</button>
<button *ngIf="i == linksForm.get('links').controls.length - 1" class="btn btn-primary btn-xs m-t-sm" type="button" (click)="addItem()">
<i name="save" class="fa fa-plus"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</form>
TS : Declare below variables in class :
linksForm: FormGroup;
links: FormArray;
Inside ngOnInit() initialize form with at least one row :
this.linksForm = new FormGroup({
'links': new FormArray([this.createItem()])
});
Add below functions for Add / Remove :
addItem(): void {
this.links = this.linksForm.get('links') as FormArray;
this.links.push(this.createItem());
}
removeItem(index: any) {
this.links.removeAt(index);
if (this.links.length == 0) {
this.addItem();
}
}
createItem(): FormGroup {
return new FormGroup({
id: new FormControl(),
name: new FormControl(),
link: new FormControl(),
createdAt: new FormControl()
});
}

Categories

Resources