Solucionado (ver solução)
Solucionado
(ver solução)
5
respostas

Usando modal com conteúdo dinâmico a partir de outros componentes

Ao contrário da aula que usa o jquery, estou utilizando ngx-modialog para criar dialogs na minha aplicação. No momento, consegui implementar a funcionalidade como serviço. Ou seja, passando o primeiro parâmetro "Título" e o segundo "Conteúdo", o dialog é exibido com sucesso.

this._modal.openDialog("Título Aqui", "Contéudo do dialog aqui");

A minha intenção é utilizar esse serviço passando o template de um componente no segundo parâmetro da função .openDialog. Exemplo:

import { MeuComponentQueTemTemplate } from '../myCompoenents/mycomponentwithtemplate.component';
@Component({
    (...)
})
export class ComponentQueChamaDialog{
    (...)
    acao() {
        this._modal.openDialog("Título do Dialog", <Template de MeuComponentQueTemTemplate será enviado>);
    }    
}

Há alguma maneira de ser feito isso?
5 respostas

Fala Wellington Aparecido, tudo bom?

Consegue me mandar seu código para eu dar uma olhada e testar essa implementação?

Olá Mario, tudo certo. Consegui mesclar duas soluções para atingir o objetivo. Primeiramente utilizei o ngx-modialog para relacionar a exibição de um dialog customizável com algum componente. E por fim, estruturei um módulo com um serviço servindo de ponte para a renderização do componente no dialog.

Aqui está o código do módulo, onde declaro meu serviço e o entryComponents (esse a propósito ainda não entendi o conceito, mas foi necessário para o código funcionar rs)

import { NgModule } from '@angular/core';
import { ModialogService } from './modialog.service';
import { ModalModule } from 'ngx-modialog';
import { BootstrapModalModule } from 'ngx-modialog/plugins/bootstrap';
import { ModialogComponent } from './modialog.component';
@NgModule({
    declarations: [ModialogComponent],
    exports: [],
    imports: [
        ModalModule.forRoot(),
        BootstrapModalModule
    ],
    providers: [ModialogService],
    entryComponents: [ModialogComponent]

})
export class ModialogModule{}

Aqui está o código do serviço. Nele temos a função 'openDialog' que recebe o componente que será renderizado no corpo do dialog e retorna uma promisse. Essa função é a que chamamos em qualquer lugar para montagem de um dialog.

import { Component, ViewContainerRef, Injectable } from '@angular/core';
import { Overlay } from 'ngx-modialog';
import { Modal } from 'ngx-modialog/plugins/bootstrap';
import { ModialogComponent } from './modialog.component';
import { BSModalContext } from 'ngx-modialog/plugins/bootstrap';
import { overlayConfigFactory } from "ngx-modialog";

@Injectable()
export class ModialogService{

    constructor(private modal: Modal) {}

    openDialog(componentOnBody) {
        let dialog = this.modal.open(ModialogComponent, overlayConfigFactory({component:componentOnBody}, BSModalContext));
        return dialog.then(promisse => promisse);
    }
}

Aqui é o código do componente que recebe os parâmetros da função 'openDialog' e faz a substituição do template pelo componente passado.

import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver, ComponentFactory, ComponentRef, AfterViewInit } from '@angular/core';
import { DialogRef, ModalComponent, CloseGuard } from 'ngx-modialog';
import { BSModalContext } from 'ngx-modialog/plugins/bootstrap';

export class CustomModalContext extends BSModalContext{
    public component;
    public ref;
}
@Component({
    selector: 'modialog-template',
    templateUrl: './modialog.component.html'
})
export class ModialogComponent implements ModalComponent<CustomModalContext>, CloseGuard, AfterViewInit{
    context: CustomModalContext
    @ViewChild('modalBody', {read: ViewContainerRef}) container;
    private componentRef: ComponentRef<any>;

    constructor(public dialog: DialogRef<CustomModalContext>, private _resolver: ComponentFactoryResolver){
        this.context = dialog.context;
        this.dialog.setCloseGuard(this);
    }

    closeModal(){
        this.dialog.close();
        this.dialog.destroy();
    }

    beforeDismiss(): boolean{
        return true;
    }

    beforeClose(): boolean{
        return true;
    }

    createComponentOnBody(){
        this.container.clear();
        const factory: ComponentFactory<any> = this._resolver.resolveComponentFactory(this.context.component);
        this.componentRef = this.container.createComponent(factory);
        this.context.ref = this.componentRef;
        /*for(var prop in variables){
            console.log(prop + " = " + variables[prop]);
            this.componentRef.instance.prop = variables[prop];
        }*/
        //this.componentRef.instance.type = 'Danget';
    }
    ngOnDestroy() {

        this.componentRef.destroy(); 

    }

    ngAfterViewInit(){
        this.createComponentOnBody();
    }
}

Template do modialog.componente.ts

<div class="modal-dialog modal-lg" style="padding:0px;margin:0px;">
    <div class="modal-content">
        <div class="modal-header">
            <h4 class="modal-title">Modal Header</h4>
        </div>
        <div class="modal-body">
            <ng-template #modalBody></ng-template>
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal" (click)="closeModal()">Close</button>
        </div>
    </div>
</div>

Um exemplo de como utilizar.

//Importando serviço
import { ModialogService } from '../ngx-modialog-example/modialog.service';

@Component({
(...)
})
export class App{
    (...)
    //Inicializando serviço
    constructor(private _modal: ModialogService){}

    callModialog(){
        //Chamando a abertura do dialog inserindo o componente 'AlertComponent' como parâmetro
        this._modal.openDialog(AlertComponent).then(ref=>console.log(ref))
        });
    }
}

Uma questão que ainda não consegui solucionar é: Imaginemos que preciso chamar um componente usando a solução acima, porém esse componente temos a necessidade de passar os parâmetros de @Input(). Como seria possível?

Passar o parâmetro não é um problema, já que quando chamamos o 'openDialog', passamos o módulo que queremos renderizar no body do dialog. Então, poderíamos passar também o parâmetro que contém esses dados, por exemplo um objeto que contém as referências '{var1: "Olá", var2: "Oi"}'. Com esses dados, no método 'createComponentOnBody(), seria possível renderizar o body também com os @Input() conforme o código.

createComponentOnBody(params){
        this.container.clear();
        const factory: ComponentFactory<any> = this._resolver.resolveComponentFactory(this.context.component);
        this.componentRef = this.container.createComponent(factory);
        this.context.ref = this.componentRef;
        for(var prop in params){
            //Os argumentos passados são criados na classe 'CustomModalContext' e aqui preenchidos.
            console.log(prop + " = " + params[prop]);
            this.componentRef.instance.prop = params[prop];
        }
    }

Infelizmente a propriedade recebe vazio durante o looping, ao tentar vincular a propriedade recebida com a referência do componente dentro dialog. =/

Outra maneira seria receber a variável 'componentRef' que é criada no método 'createComponentOnBody()' e retornar a mesma no resultado da promisse. Assim, o componente que iniciar a chamada do dialog setaria os demais argumentos do componente passado. Mais ou menos assim:

callModialog(){
        this._modal.openDialog(AlertComponent).then(ref=>{
            //Com a referência retornada na promisse, poderíamos setar o parâmetro 'oi' com o valor 'Olá'.
            ref.instance.oi = 'Olá';
            console.log(ref);
        });
    }

Claro que isso ainda não funcionou, porque não consegui retornar a variável 'componentRef' como resposta da promisse.

Wellington, separado aqui ta meio dificil de bolar uma solução, pode subir no github essa ideia?

solução!

Olá pessoal, Depois de algum tempo acabei esquecendo esse tópico (sorry) e algum tempo depois me deparei com a mesma situação. Por um acaso encontrei uma página que responde perfeitamente o tópico.

http://jasonwatmore.com/post/2017/01/24/angular-2-custom-modal-window-dialog-box