Field not empty but the modal is displayed - Angular - javascript

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>

Related

How to render updated data in all open tabs in angular

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>

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()
});
}

How to use one component for validation other two components?

I have three components: GalleryAddComponent to add a new element, GalleryItemComponent, to edit an element, FieldsComponent, the form I want to use in the components: GalleryAddComponent and GalleryItemComponent. All components are inside the GalleryComponent. But when I go to the component GalleryAddComponent to add a new element I get the error: ERROR TypeError: Cannot read property 'controls' of undefined. Also in the component: GalleryItemComponent.
Help solve this problem so that the editing and adding logic works correctly.
template of GalleryAddComponent
<div class="card">
<div class="card-body">
<form [formGroup]="angForm" novalidate>
<app-fields [formGroup]="angForm"></app-fields>
<div class="form-group but-group">
<button (click)="addPost(title.value, url.value); angForm.reset(title.value, url.value)"
[disabled]="angForm.pristine || angForm.invalid"
class="btn btn-primary">Add
</button>
<a routerLink="/" class="btn btn-danger">Back</a>
</div>
</form>
</div>
</div>
code of GalleryAddComponent
export class GalleryAddComponent implements OnInit {
angForm: FormGroup;
isAdded: boolean = false;
constructor(private fb: FormBuilder, private galleryService: GalleryService) {}
ngOnInit() {
this.angForm = this.fb.group({
title: ['', Validators.required],
url: ['', Validators.required]
});
}
addPost(title: string, url: string): void {
this.galleryService.add(title, url).subscribe(res => {
this.isAdded = true;
});
}
}
template of GalleryItemComponent
<div class="card" *ngIf="toggleEdit">
<h4>Edit your post</h4>
<div class="card-body">
<form [formGroup]="angForm" novalidate>
<app-fields [formGroup]="angForm"></app-fields>
<div class="form-group but-group">
<input type="button"
(click)="updatePost(title.value, url.value)"
[disabled]=" angForm.invalid"
class="btn btn-primary" value="Update Post">
</div>
</form>
</div>
</div>
code of GalleryItemComponent
export class GalleryItemComponent implements OnInit {
pic: Picture;
angForm: FormGroup;
constructor(private route: ActivatedRoute,
private galleryService: GalleryService, private fb: FormBuilder) {}
ngOnInit() {
this.angForm = this.fb.group({
title: ['', Validators.required],
url: ['', Validators.required]
});
this.showPost();
}
showPost(): void {
this.route.params.subscribe(params => {
this.galleryService.getPicture(params['id']).subscribe(res => {
this.pic = res;
this.angForm.setValue({title: res.title, url: res.url})
})
})
}
updatePost(title: string, url: string): void {
this.route.params.subscribe(params => {
this.galleryService.update(title, url, params['id']).subscribe(res => {
if (res.id === this.pic.id) {
this.pic.title = title;
this.pic.url = url;
}
});
});
}
}
template of FieldsComponent
<div [formGroup]="formGroup">
<div class="form-group">
<label class="col-md-4">Picture Title</label>
<input type="text" class="form-control" formControlName="title" minlength="1" #title/>
</div>
<div *ngIf="angForm.controls['title'].invalid && (angForm.controls['title'].dirty || angForm.controls['title'].touched)"
class="alert alert-danger">
<div *ngIf="angForm.controls['title'].errors.required">
Title is required.
</div>
</div>
<div class="form-group">
<label class="col-md-4">Picture Address (url)</label>
<input type="url" class="form-control" formControlName="url" #url pattern="https?://.+"
title="Include http://"/>
</div>
<div *ngIf="angForm.controls['url'].invalid && (angForm.controls['url'].dirty || angForm.controls['url'].touched)"
class="alert alert-danger">
Address(url) is required.
<div *ngIf="angForm.controls['url'].errors.required ">
</div>
</div>
</div>
code of FieldsComponent
export class FieldsComponent implements OnInit {
#Input() formGroup: FormGroup;
constructor() {}
ngOnInit() {}
}
You are getting this error because you are referencing angForms.controls in your FieldsComponent which only has one variable: formGroup.
Simply replace angForms in the template HTML code with formGroup and the issue should be resolved.
<div [formGroup]="formGroup">
<div class="form-group">
<label class="col-md-4">Picture Title</label>
<input type="text" class="form-control" formControlName="title" minlength="1" #title />
</div>
<div *ngIf="formGroup.controls['title'].invalid && (formGroup.controls['title'].dirty || formGroup.controls['title'].touched)"
class="alert alert-danger">
<div *ngIf="formGroup.controls['title'].errors.required">
Title is required.
</div>
</div>
<div class="form-group">
<label class="col-md-4">Picture Address (url)</label>
<input type="url" class="form-control" formControlName="url" #url pattern="https?://.+" title="Include http://" />
</div>
<div *ngIf="formGroup.controls['url'].invalid && (formGroup.controls['url'].dirty || formGroup.controls['url'].touched)"
class="alert alert-danger">
Address(url) is required.
<div *ngIf="formGroup.controls['url'].errors.required ">
</div>
</div>
</div>
I think the easy solution here, is to set up the formgroup in the parent component, and pass it on to the child components as input, hence it is given to the child component before the child component html is loaded.
The other solution I would recommend is to use the Resolve Technique offered by Angular, this allows you to load all data before a component is loaded with a quite straight-forward setup.
Here are some references:
https://www.techiediaries.com/angular-router-resolve/
https://alligator.io/angular/route-resolvers/
Cannot read property 'controls' of undefined. This error can come only coz instead of formGroup you have used angForm. Try replacing it as shared by #Wrokar. Also what I understand is you want to show consolidated error messages. So instead of doing it in html for each formcontrol, you should do it in component.ts and make it more generic, by subscribing to value change of each control like below, and show the consolidated error message.
for (const field in this.formGroup.controls) { // 'field' is a string
const control = this.form.get(field); // 'control' is a FormControl
control.valueChanges.subscribe(
(res) => {
// check if it is pristine, dirty, invalid, touched, pattern match if
any
// you can access control.errors
// create the consolidated list and append it in the list of error
messages
}
)
}
Its better to make it generic coz, tomorrow your form can have additional fields and then you dont have to change you FieldsComponent.

Trouble using multiple bootstrap modals (ngx-bootstrap and Angular)

I really have a problem using multiple bootstrap modals within Angular 4. Maybe you can help me out of this. The code is working fine with just one modal. I am not be able to use the buttons/form of the first loaded modal. The second is working fine. After opening and closing the second modal, also the first modal works... strange. Is there something wrong?
<!-- ADD MODAL-->
<div bsModal #addModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myAddModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Add Dimension</h4>
<button type="button" class="close" (click)="addModal.hide()" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="formGroupInput">TAG</label>
<input type="text" class="form-control" id="formGroupExampleInput" placeholder="Example input" aria-describedby="keyHelp"
value="" disabled>
<small id="keyHelp" class="form-text text-muted">Notice that this field is a surrogate key!</small>
</div>
<div class="form-group">
<label for="formGroupInput2">Name</label>
<input type="text" class="form-control" id="formGroupExampleInput2" placeholder="Another input" value="">
</div>
<div class="form-group">
<label for="formGroupInput3">Description</label>
<input type="text" class="form-control" id="formGroupExampleInput2" placeholder="Another input" value="">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" id="dfsdfsdfsdf" class="btn btn-secondary" (click)="addModal.hide()">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
<!-- EDIT MODAL-->
<div bsModal #editModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Edit {{currentItem?.NAME}}</h4>
<button type="button" class="close" (click)="editModal.hide()" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="formGroupInput">TAG</label>
<input type="text" class="form-control" id="formGroupExampleInput" placeholder="Example input" aria-describedby="keyHelp"
value="{{currentItem?.TAG}}" disabled>
<small id="keyHelp" class="form-text text-muted">You'll need to delete this entry to change this key value!</small>
</div>
<div class="form-group">
<label for="formGroupInput2">Name</label>
<input type="text" class="form-control" id="formGroupExampleInput2" placeholder="Another input" value="{{currentItem?.NAME}}">
</div>
<div class="form-group">
<label for="formGroupInput3">Description</label>
<input type="text" class="form-control" id="formGroupExampleInput2" placeholder="Another input" value="{{currentItem?.COMM}}">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="editModal.hide()">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
import { Component, OnInit, ViewChild } from '#angular/core';
import { DataService } from "../data.service";
import { ModalDirective } from 'ngx-bootstrap/modal/modal.component';
#Component({
templateUrl: './dimensions.component.html',
styleUrls: ['./dimensions.component.scss']
})
export class DimensionsComponent implements OnInit {
#ViewChild('editModal') public editModal: ModalDirective;
#ViewChild('addModal') public addModal: ModalDirective;
currentItem;
openModal(item: any) {
this.currentItem = item;
this.editModal.show();
}
openAddModal() {
this.addModal.show();
}
//Attributes
public currentVar;
subscription;
dimensionData;
rows;
constructor(private _dataService: DataService) {
}
Thank you for your time guys!
Update: By switching the modal sequence inside the code the problem also occures but just in terms of the other modal.
bsModal directive is not really suited for nested modals, please convert to usage of bsModal service, see example here:
https://valor-software.com/ngx-bootstrap/#/modals#service-nested
So after a few fixes, production use and testings I can say I have found a solution to a nested ngx-bootstrap modal problems. I have written a service-wrapper for a BsModalService.
modalIndexing = {};
hasInited = false;
constructor(
router: Router,
private modalService: BsModalService,
) {
router.events
.pipe(filter((event: NavigationEvent) => (event instanceof NavigationStart)))
.subscribe(() => this.terminateAll());
}
// Just a modal dude
resolveModal(component, position: 'center' | 'static' = 'center', initialState?: any): void {
const config: ModalOptions = {
class: modalClassDict[position] + ' ss-modal',
initialState
};
// For nested launches, wait until previous hide to open new
setTimeout(() => {
this.modalService.show(component, config);
}, initialState.hasTimeout ? 600 : 0);
}
// Modal dude that will return something
// if you want to of cause, no pressure
resolveModalSub(component, position: 'center' | 'static' = 'center', initialState?: any): Observable<any> {
const config: ModalOptions = {
class: modalClassDict[position] + ' ss-modal',
initialState
};
const bsModalRef = this.modalService.show(component, config);
bsModalRef.content.onClose = new Subject<any>();
const modalSub = bsModalRef.content.onClose.pipe(take(1));
this.healthCheck(modalSub);
return modalSub;
}
// This sneaky little bastard will return something if it hides!
resolveModalSubCheckHide(component, position: 'center' | 'static' = 'center', initialState?: any): Observable<any> {
const config: ModalOptions = {
class: modalClassDict[position] + ' ss-modal',
initialState
};
const bsModalRef = this.modalService.show(component, config);
bsModalRef.content.onClose = new Subject<any>();
const modalSub = bsModalRef.content.onClose.pipe(take(1));
this.healthCheck(modalSub, true);
return modalSub;
}
// This thingy does some pseudo-indexing and cleans up the mess you left
private healthCheck(modalSub, needToCheckHide = false) {
// The only property ngx-bootstrap modal service
// is able to return is the number of openned modals
// so we are heading out from it
const index = this.modalService.getModalsCount();
this.modalIndexing[index] = {
modalSub,
needToCheckHide
};
// First modal initiates onHide sub
if (!this.hasInited) {
this.hasInited = true;
this.modalService.onHide
.subscribe(() => {
if (_.keys(this.modalIndexing).length) {
// This event emits after the modal is closed so its a +1
const indexedSub = this.modalIndexing[this.modalService.getModalsCount() + 1];
// Handeles the case when you need to know if modal was closed
if (indexedSub.needToCheckHide) {
indexedSub.modalSub.next(false);
}
// Completes sub of a value return and cleans itself from dictionary
if (indexedSub.modalSub) {
indexedSub.modalSub.complete();
}
delete this.modalIndexing[this.modalService.getModalsCount() + 1];
}
});
}
}
// Termiantes all modal windows in user navigates via back / forward
private terminateAll() {
_.keys(this.modalIndexing).forEach(index => this.modalService.hide(index));
}
Basic usage:
const data = {
// your initial data
};
this.dialogService.resolveModalSubCheckHide(YourModalComponentToDisplay, 'center', data)
.subscribe((value) => {
// processing
});
Features:
Return value handling as subject
Performance friendly
Doesn`t leave any uncompleted subjects
Can return a value if hidden
Handles unlimited nested levels
Can be queued to launch modals
Auto close on manual navigation
Hope this helps somebody <3.

Dialog with form in angular2

I use ng2-bootstrap-modal
add sample form to example Confirm Dialog ng2-bootstrap-modal
change template
<div class="modal-dialog">
<div class="modal-content">
<form [formGroup]="loginForm" (ngSubmit)="doLogin($event)">
<div class="modal-header">
<button type="button" class="close" (click)="close()">×</button>
<h4 class="modal-title">{{title || 'Confirm'}}</h4>
</div>
<div class="modal-body">
<p>{{message || 'Are you sure?'}}</p>
<div>
<input formControlName="email" type="email" placeholder="Your email">
<input formControlName="password" type="password" placeholder="Your password">
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">OK</button>
<button type="button" class="btn btn-default" (click)="close()" >Cancel</button>
</div>
</form>
</div>
change ts
import { Component, OnInit, } from '#angular/core';
import { DialogComponent, DialogService } from "ng2-bootstrap-modal";
import { FormBuilder, Validators } from '#angular/forms'
export interface ConfirmModel {
title:string;
message:string;
}
#Component({
selector: 'app-defproducts-edt',
templateUrl: './defproducts-edt.component.html'
//, styleUrls: ['./defproducts-edt.component.css']
})
export class DefproductsEdtComponent extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel, OnInit {
title: string;
message: string;
public loginForm = this.fb.group({
email: ["", Validators.required],
password: ["", Validators.required]
});
constructor(dialogService: DialogService, public fb: FormBuilder) {
super(dialogService);
}
confirm() {
// we set dialog result as true on click on confirm button,
// then we can get dialog result from caller code
this.result = true;
this.close();
}
doLogin(event) {
console.log(event);
console.log(this.loginForm.value);
this.close();
}
ngOnInit() {
}
}
I want to get result data in marked place //** **//
let disposable = this.dialogService.addDialog(DefproductsEdtComponent, {
title:'Confirm title',
message:'Confirm message'})
.subscribe((isConfirmed)=>{
//We get dialog result
if(isConfirmed) {
// **HOW This place get data from form on dialog? **//
//alert();
} else {
//alert('declined');
}
});
}
ng2-bootstrap documentation says that it can return specified type
abstract class DialogComponent ....
/**
* Dialog result
* #type {T2}
*/
protected result:T2
I guess that I have to pass this type into constructor
and then get a result in subscribe method.
I got no idea how to do it.
Looking forward to any replies.
Thank you in advance.
Just change type of result from boolean to string, in your case.
export class DefproductsEdtComponent extends DialogComponent<ConfirmModel, boolean>

Categories

Resources