Feature request: Support Injection of inputs to Modal

Related to #846
Its a good start that now Modal dialogs can use components, but its still not enough if the component cant be passed some content which can be displayed/used inside the dialog.

We are using ui-bootstrap with JHipster With ng1 version there was resolve which solved this and it enabled us to open Modal dialogs right from onEnter of a route which was nice. But now we dont see a way to do this here

This is becoming a blocker for our Angular2 migration would appreciate a fix/timeline/workaround or advice

6 thoughts on “Feature request: Support Injection of inputs to Modal

  1. To help everyone decide between various options presented in the design doc, here is a quick recap of what is considered (code from the API / user perspective). The list of options goes as follows:

    1. Expose an instance of a component
    2. Use resolve and expose resolved values on an instance of NgbActiveModal
    3. Use resolve and bind resolved values to component’s inputs.

    To see how those options would play in practice let’s assume that we’ve got the following components that we want to use as content:

    class MyContent {
        @Input() foo;
        @Input('bar') _bar;
        @Output() out = new EventEmmiter(); 
    
        constructor(private activeModal: NgbActiveModal) {}  
    
        someMethod() {
           // I need value of foo and _bar here for some reason...
        } 
    }

    Option 1: Expose an instance of a component

      const contentComponentInstance = modalService.open(MyContent).component;
      contentComponentInstance.foo = 'Foo value';
      contentComponentInstance._bar = 5;
    
      //I can even subscribe to outputs!
      contentComponentInstance.out.subscribe(() => {
        // do sth with output
      });

    Option 2: Use resolve and expose resolved values on an instance of NgbActiveModal

      modalService.open(MyContent, {
         resolve: {
             'foo': 'Foo value',
             '_bar': 5
         }  
      });

    and inside MyContent component:

    someMethod() {
         // I need value of foo and _bar here for some reason...
         console.log(this.activeModal.resolve.foo);
    } 

    In this case:

    • speaking about outputs doesn’t make sense really
    • inputs are not used at all
    • we are close to how router works (although working exactly like router would be an overkill IMO)

    Option 3: Use resolve and bind resolved values to component’s inputs

      modalService.open(MyContent, {
         resolve: {
             'foo': 'Foo value',
             '_bar': 5
         }  
      });

    and inside MyContent component:

    someMethod() {
         // I need value of foo and _bar here for some reason...
         console.log(this.foo);
    } 

    In this case:

    • speaking about outputs doesn’t make sense really
    • one can bind to any instance variable and not only inputs so modal is “forced” to break encapsulation
  2. @deepu105 and All – after loooooong deliberation I’ve decided to go with option (1). Here is the reasoning:

    • (2) and (3) is too limiting in the sense that it doesn’t allow interactions with @Outputs, methods and lifecycle hooks
    • It is not immediately obvious how async and changing outputs should be handled in case of (2) and (3)
    • Material design is doing (1) and I would love to work on / share ui-toolkit with them
    • (1) is the most flexible, simplest to code and doesn’t close doors to having more sugar in the future.

    Thnx to all who taken time to review / comment on this issue. PR is here: #903

  3. There is an issue with Option 1, the issue is that the view has already been rendered and and lets say if we have use Forms in this then passing the data after initialisation is of no use.
    We might need to configure something in constructor or ngOnInit method.

    Please suggest how you can address this concern.

  4. In the case of forms, you could create a method on the modal component that builds your form and then call it from the parent after assigning any needed inputs.

    // ParentComponent.ts
    const modalComponentInstance = modalService.open(ModalComponent).componentInstance;
    // Set input
    modalComponentInstance.initialFormValues = {...yourInitialFormValues}
    modalComponentInstance.buildMyForm();
    
    // ModalComponent.ts
    myForm: FormGroup;
    buildMyForm() {
      this.myForm = new FormGroup({....})
    }
    
  5. Another useful technqiue is to create a custom injector
    https://angular.io/api/core/Injector

    this.modal.open(MyModal, {
      injector: Injector.create([{
        provide: MyModalOptions, useValue: { foo: "bar" }
      }], this.injector)
    }) 
    
    @Component({ ... })
    class MyModal {
      constructor(private options: MyModalOptions) {
      }
    }