ngx-bootstrap modal: How to get a return value from a modal?
Asked Answered
T

9

50

In my Angular 4 app, let's assume that I'm inside a service.

At some point, I want to ask the user for a confirmation, currently I'm doing it with just a confirm(...) request:

const result = confirm('Are you sure?');

What if instead I would like to show an ngx-bootstrap modal with, let's say, two buttons "Yes" or "No" and obtain a similar result?


EDIT: in my case, I solved my issue by playing with Subjects. Here you can find my solution, in case it can be useful for someone else. However that solution does not solve this question which is about returning a value from a modal, so I leave it open.

Tankage answered 25/9, 2017 at 15:2 Comment(3)
Did you try something so far?Huang
in my service I'm able to show the modal using this.modalService.show(this.confirmModal) but I don't know how to receive back the result of what the user clicked in the modalTankage
@ShinDarth Just now only posted my answer.. i hope this will useful for youDanged
D
139

Try like this :

myexample it's working correctly. hope this will help you

home.module.ts

import { ModalModule } from 'ngx-bootstrap';

@NgModule({
    imports: [
        ModalModule.forRoot()
    ]
})

home.component.html

<button class="btn btn-primary" (click)="openConfirmDialog()">Open Confirm box
</button>

home.component.ts

import { BsModalService } from 'ngx-bootstrap/modal';
import { BsModalRef } from 'ngx-bootstrap/modal/modal-options.class';

export class HomeComponent {
    public modalRef: BsModalRef;
    constructor(
        private homeService: HomeService,
        private modalService: BsModalService
    ) { }

    openConfirmDialog() {
        this.modalRef = this.modalService.show(HomeModalComponent);
        this.modalRef.content.onClose.subscribe(result => {
            console.log('results', result);
        })
    }
}

home-modal.component.html

<div class="alert-box">
    <div class="modal-header">
        <h4 class="modal-title">Confirm</h4>
        <button type="button" class="close" aria-label="Close" (click)="bsModalRef.hide()">
            <span aria-hidden="true">&times;</span>
        </button>
    </div>
    <div class="modal-body">
        Are you sure want to delete this node?
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-secondary" (click)="onConfirm()">Yes</button>
        <button type="button" class="btn btn-secondary" (click)="onCancel()">No</button>        
    </div>
</div>

home-modal.component.ts

import { Subject } from 'rxjs/Subject';
import { BsModalRef } from 'ngx-bootstrap/modal';

export class HomeModalComponent {
    public onClose: Subject<boolean>;

    constructor(private _bsModalRef: BsModalRef) { }

    public ngOnInit(): void {
        this.onClose = new Subject();
    }

    public onConfirm(): void {
        this.onClose.next(true);
        this._bsModalRef.hide();
    }

    public onCancel(): void {
        this.onClose.next(false);
        this._bsModalRef.hide();
    }
}
Danged answered 25/9, 2017 at 17:32 Comment(9)
thanks for your answer, but how would you use it inside a service? For example inside a CanDeactivate method of a Guard that it is supposed to return true or false, without waiting?Tankage
I need to replace this with a modal: canDeactivate() { return confirm( 'are you sure?' ); } }Tankage
the BsModalRef path has changed from ngx-bootstrap/modal to ngx-bootstrap/modal/bs-modal-ref.serviceMezzorelievo
Great! How can I customize Are you sure want to delete this node? , I means passing it as param to openConfirmDialogUndercoat
How are you able to do modalService.show(HomeModalComponent) since HomeModalComponent is the export from another class and therefore in another file ?Ungodly
@RezaRahmati, to customize message you can in home-modal.component.html change test to {{message}} and then to call show like this: const config = { message: 'Are you sure want to delete this node?'}; this.modalService.show(HomeModalComponent, { initialState: config });Cly
What happen with Subject subscribed inside 'openConfirmDialog' if user clicks on backdrop?Cooperman
We need to unsubscribe ones we go to another screen: something like: ngOnDestroy() { this.bsModalRef.content.onClose.unsubscribe(); } To avoid memory leaksViscount
Doesn't cover dismiss by ESC key or click on backdrop.Glasgo
H
10

I used the solution from @Chandru, however to return a true or false, instead of:

openConfirmDialog() {
    this.modalRef = this.modalService.show(HomeModalComponent);
    this.modalRef.content.onClose.subscribe(result => {
        console.log('results', result);
    })
}

I simply used:

openConfirmDialog() {
    this.modalRef = this.modalService.show(HomeModalComponent);
    return this.modalRef.content.onClose;
}
Hoagland answered 21/5, 2018 at 23:20 Comment(0)
I
4

I understand that most of the answers above are completely valid, but that the main goal is be able to invoke the confirmation dialog in this way...

  async openModalConfirmation() {
    const result = await this.confirmationSvc.confirm('Confirm this...');
    if (result) {
      console.log('Yes!');
    } else {
      console.log('Oh no...');
    }
  }

Note that this is mostly syntactic sugar to simplify the use of a promise and the asynchronous stuff.

I think it's what the OP was looking for and probably can be reworked to support returning any other data type (apart from a boolean).

The rest of the code below (not including the template to keep this short), pretty straightforward..

ModalConfirmationService

import { ModalConfirmationComponent } from './component';

@Injectable()
export class ModalConfirmationService {

  constructor(private bsModalService: BsModalService) {}

  confirm(message: string): Promise<boolean> {
    const modal = this.bsModalService.show(ModalConfirmationComponent, { initialState: { message: message }});

    return new Promise<boolean>((resolve, reject) => modal.content.result.subscribe((result) => resolve(result) ));
  }
}

ModalConfirmationComponent

import { Component, Input, Output, EventEmitter} from '@angular/core';
import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
import { Subject } from 'rxjs/Subject';

@Component({
  templateUrl: './component.html'
})
export class ModalConfirmationComponent {
  @Input() message: string;
  result: Subject<boolean> = new Subject<boolean>();

  constructor(public modalRef: BsModalRef) { }

  confirm(): void {
    this.result.next(true);
    this.modalRef.hide();
  }

  decline(): void {
    this.result.next(false);
    this.modalRef.hide();
  }
}
Iseabal answered 24/5, 2018 at 10:41 Comment(0)
L
3

@ShinDarth You can add this function in your service and call this funcion whenever required.

In your Service, create this function

    openConfirmDialogBox() {
        this.modalRef = this.modalService.show(DemoModalComponent);
        this.modalRef.content.action.take(1)
            .subscribe((value) => {
                console.log(value) // here value passed on clicking ok will be printed in console. Here true will be printed if OK is clicked
                return value;
             }, (err) => {
                 return false;
        });
    }

In your demo-modal.component.ts, create an EventEmitter

 @Output() action = new EventEmitter();
 public onClickOK() {
    this.action.emit(true); //Can send your required data here instead of true
 }
 public onClickCANCEL() {
    this.action.emit(false); //Can send your required data here instead of true
 }

I hope this would help you

Lacrimatory answered 24/5, 2018 at 7:13 Comment(0)
C
1

Try this:

home.component.ts

import { BsModalService } from 'ngx-bootstrap/modal';
import { BsModalRef } from 'ngx-bootstrap/modal';

export class HomeComponent {
 public modalRef: BsModalRef;
 constructor(
    private modalService: BsModalService
 ) { }

 openConfirmDialog() {
    this.modalRef = this.modalService.show(HomeModalComponent);

    this.modalRef.content.onClose = new Subject<boolean>();

    this.modalRef.content.onClose.subscribe(result => {
        console.log('results', result);
     })
 }
}

and

home-modal.component.ts

import { BsModalRef } from 'ngx-bootstrap/modal';
export class HomeModalComponent {

 constructor(private bsModalRef: BsModalRef) {

 }

 public ngOnInit(): void {
 }

 public onConfirm(): void {
    this.bsModalRef.content.onClose.next(true);
    this.bsModalRef.hide();
 }

 public onCancel(): void {
    this.bsModalRef.content.onClose.next(false);
    this.bsModalRef.hide();
 }
}
Crosscut answered 6/10, 2019 at 11:52 Comment(0)
C
1

If you are using a later version of Angular you might get an error due to the location of BsModalRef that had moved:

Use this location:

import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';

rather than:

import { BsModalRef } from 'ngx-bootstrap/modal/modal-options.class';
Civies answered 30/10, 2019 at 10:4 Comment(0)
S
1

I do not know if that suits you, but I used a service with Subject. In parent component it is enough to subscribe and unsubscribe, in modal component, when user clicks confirm button, then just emit value using next.

I'm sorry for not presenting a full example, but pseudocode below should present the idea.

Service:

export class SomeService{
    yourSubject: new Subject<string>();
}

Parent component:

...
ngOnInit(){
    this.bsModalRef = this.someService.yourSubject.subscribe ( val => ...);
}
ngOnDestroy(){
    this.bsModalRef.unsubscribe();
}
...

Modal component:

...
onSaveAction(){
    this.someService.yourSubject.next(val);
}
...

Ps. of course you need to provide service in proper place and inject it into components :-)

Schweiz answered 7/11, 2019 at 11:31 Comment(0)
C
1

Try this!!!! Create modal options with a yes function and a no function in the options. Receive args as @Input...

import { Component, OnInit, Input } from '@angular/core';
import { BsModalRef } from 'ngx-bootstrap/modal';

@Component({
    selector: 'confirm-box',
    templateUrl: './confirm-box.component.html',
    styleUrls: ['./confirm-box.component.css']
})
export class ConfirmBoxComponent implements OnInit {
    private _args: any;

    public get args(): any {
        return this._args;
    }

    @Input()
    public set args(value: any) {
        this._args = value;
    }

    constructor(private activeModal: BsModalRef) {
    }

    ngOnInit(): void {
    }

    public no(): void {
        this.args.noFunction();
        this.activeModal.hide();
    }

    public yes(): void {
        this.args.yesFunction();
        this.activeModal.hide();
    }
}

And then the component or service that launches the dialog:

import { BsModalService, BsModalRef, ModalOptions } from 'ngx-bootstrap/modal';
import { ConfirmBoxComponent } from '../../dialogs/confirm-box/confirm-box.component';

export class AreYouSure {    
    private activeModal: BsModalRef;

    constructor(private modalService: BsModalService) {
          //Wait five seconds before launching the confirmbox
          setTimeout(()=>{
             this.tryLaunchConfirm();
          }, 5000);
    }
    private tryLaunchConfirm(): void {
        const config: ModalOptions = {
            initialState: {
                args: {
                    title: "Confirm This",
                    message: "Are you really sure???",
                    yesFunction: () => { console.log("YES!!!! was clicked."); },
                    noFunction: () => { console.log("NO!!!! was clicked."); }
                }
            }
        }
        this.activeModal = this.modalService.show(ConfirmBoxComponent, config);

    }
}


Cynthea answered 22/9, 2020 at 22:53 Comment(0)
T
0

Try with below option which is working for me. callbackOnModelWindowClose is the return value.

@Output() callbackOnModelWindowClose: EventEmitter<null> = new EventEmitter();

const initialState = {
          isModelWindowView: true, bodyStyle: 'row', gridDataList: this.scheduleList
        };

this.modalRef = this.modalService.show(YourComponent,
          Object.assign({}, this.modalConfig, { class: 'modal-dialog-centered', initialState });

this.modalRef.content.callbackOnModelWindowClose.take(1).subscribe(() => {
            your code here..
          });
Tolson answered 10/12, 2018 at 15:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.