question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Modal component data binding pattern is impractical

See original GitHub issue

Bug description:

The pattern used to pass data to modal components is impractical and I think it could be enhanced to avoid common mistakes.

As described in the docs, you can pass data to your modal as follows:

const modalRef = this.modalService.open(NgbdModalContent);
modalRef.componentInstance.name = 'World';

This works fine, but has two problems:

  1. It causes an ExpressionChangedAfterItHasBeenCheckedError error when the template uses name in structural directives like *ngIf, *ngFor etc.

    Workaround:

Inject public cdref: ChangeDetectorRef in your modal component constructor and doing:

const modalRef = this.modalService.open(NgbdModalContent);
modalRef.componentInstance.name = 'World';
modalRef.componentInstance.cdref.detectChanges();
  1. With this pattern, the modal component’s lifecycle hooks are executed before the binding is done. In other words, ngOnInit is executed before 'World' is binded to property name of modalRef.

Workaround:

Ignore lifecycle hooks and define a custom method bindData(data: any) as follows:

export class NgbdModalContent implements OnInit {
    @Input() name;

    constructor(
        public activeModal: NgbActiveModal,
        public cdref: ChangeDetectorRef,
    ) {

    }

    bindData(data: { name?: any }): void {
        // Bind your data here
        this.name = data.name;
        // Force change detection to avoid ExpressionChangedAfterItHasBeenCheckedError
        this.cdref.detectChanges();

        /*
         * Here you can put your logic
         * Your logic must be placed AFTER triggering change detection, otherwise
         * directives and components will not be updated correctly.
         */
    }

    ngOnInit(): void {
        // This callback is not really useful as it is executed before bindings are applied
    }
}

then just bind the data like so:

const modalRef = this.modalService.open(NgbdModalContent);
(modalRef.componentInstance as NgbdModalContent).bindData({name: 'World'});

Be aware that this will work only in the order I mentioned:

  1. Bind the data
  2. Trigger change detection
  3. Execute your own logic

Version of Angular, ng-bootstrap, and Bootstrap:

Angular: 6.0.6

ng-bootstrap: 3.0.0

Bootstrap: 4.1.1

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:17
  • Comments:5 (1 by maintainers)

github_iconTop GitHub Comments

5reactions
marcinmajkowskicommented, May 27, 2022

I agree using componentInstance this way has many pitfalls and I do not encourage it in codebases I work on.

This is how I work around this:

export class MyModalData {
  constructor(readonly name: string) {}
}

@Component(/* */)
export class MyModalComponent implements OnInit {
  constructor(private modalData: MyModalData) {}
  
  ngOnInit(): void {
    // can access this.modalData here
  }
}
this.ngbModal.open(MyModalComponent, {
  injector: Injector.create({
    providers: [{ provide: MyModalData, useValue: new MyModalData('foo') }],
  }),
});

This way passed data is typed, there is no outside component instance modifications and init lifecycle hook can be used for initialization as expected. Forgetting to provide the data results with error which is a nice touch.

Additionaly, in some cases using opening component injector as parent when creating modal component injector may be useful.

4reactions
dKabcommented, Feb 18, 2019

It’s also worth mentioning that ngOnChanges doesn’t get called at all.

Read more comments on GitHub >

github_iconTop Results From Across the Web

cant bind data between object to a modal - Stack Overflow
i'm trying to bind data between a modal that it content is a form for editing data. i'm using the ng-model directive ,...
Read more >
ion-popover - Ionic Framework
With a two way data binding, the popover needs to concern itself with both the boolean value as well as the existence of...
Read more >
How to build a Reusable Vuejs modal component - Medium
I'm going to build a Modal component and reusable in every page. ... We need to add the new data binding in the...
Read more >
S N O Text Tags Dominant Topic Topic Terms URL Label No. of ...
accordingly frame modal dialog java;swing;design- patterns;jframe. 1 output, text, document, format, button, copy, account, original,.
Read more >
US8127237B2 - Active business client - Google Patents
The UI component enables functionality in the client device related to ... generating the business context data includes binding a data component to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found