Eu quero criar dinamicamente um modelo. Isso deve ser usado para criar um ComponentType
tempo de execução e colocá-lo (até substituí- lo ) em algum lugar dentro do componente de hospedagem.
Até o RC4 eu estava usando ComponentResolver
, mas com o RC5 recebo a seguinte mensagem:
ComponentResolver is deprecated for dynamic compilation.
Use ComponentFactoryResolver together with @NgModule/@Component.entryComponents or ANALYZE_FOR_ENTRY_COMPONENTS provider instead.
For runtime compile only, you can also use Compiler.compileComponentSync/Async.
Encontrei este documento ( Criação de componente dinâmico síncrono angular 2 )
E entenda que eu posso usar
- Tipo de dinâmica
ngIf
comComponentFactoryResolver
. Se eu passar componentes conhecidos dentro de@Component({entryComponents: [comp1, comp2], ...})
- eu posso usar.resolveComponentFactory(componentToRender);
- Compilação em tempo real, com
Compiler
...
Mas a questão é como usar isso Compiler
? A nota acima diz que eu deveria ligar: Compiler.compileComponentSync/Async
- e como?
Por exemplo. Quero criar (com base em algumas condições de configuração) esse tipo de modelo para um tipo de configuração
<form>
<string-editor
[propertyName]="'code'"
[entity]="entity"
></string-editor>
<string-editor
[propertyName]="'description'"
[entity]="entity"
></string-editor>
...
e em outro caso este ( string-editor
é substituído por text-editor
)
<form>
<text-editor
[propertyName]="'code'"
[entity]="entity"
></text-editor>
...
E assim por diante (número / data / referência diferente editors
por tipos de propriedade, ignorou algumas propriedades para alguns usuários ...) . isto é, por exemplo, a configuração real pode gerar modelos muito mais diferentes e complexos.
O modelo está mudando, então não posso usar ComponentFactoryResolver
e passar os existentes ... Preciso de uma solução com o Compiler
.
fonte
$compile
que realmente pode fazer que esses métodos não possam - estou criando um aplicativo no qual eu apenas quero compilar o HTML conforme ele aparece na página de terceiros e nas chamadas ajax. Não consigo remover o HTML da página e colocá-lo no meu próprio modelo. SighRespostas:
EDIT - relacionado a 2.3.0 (07/12/2016)
Tópico semelhante é discutido aqui Equivalente a $ compile no Angular 2 . Precisamos usar
JitCompiler
eNgModule
. Leia mais sobreNgModule
no Angular2 aqui:Em poucas palavras
Há um exemplo / exemplo de trabalho ativo (modelo dinâmico, tipo de componente dinâmico, módulo dinâmico
JitCompiler
, ... em ação)O principal é:
1) criar o modelo
2) encontrar
ComponentFactory
no cache - vá para 7)3) - criar
Component
4) - criar
Module
5) - compilar
Module
6) - retornar (e armazenar em cache para uso posterior)
ComponentFactory
7) usar o Target e
ComponentFactory
criar uma instância de dinâmicoComponent
Aqui está um trecho de código (mais sobre isso aqui ) - Nosso Construtor personalizado está retornando apenas construído / armazenado em cache
ComponentFactory
e a exibição Espaço reservado de destino consome para criar uma instância doDynamicComponent
É isso - em poucas palavras. Para obter mais detalhes .. leia abaixo
.
TL&DR
Observe um desentupidor e volte para ler os detalhes, caso algum trecho exija mais explicações
.
Explicação detalhada - Angular2 RC6 ++ e componentes de tempo de execução
Abaixo da descrição desse cenário , iremos
PartsModule:NgModule
(suporte de pequenos pedaços)DynamicModule:NgModule
, que conterá nosso componente dinâmico (e fará referênciaPartsModule
dinamicamente)Component
tipo (somente se o modelo tiver sido alterado)RuntimeModule:NgModule
. Este módulo conterá oComponent
tipo criado anteriormenteJitCompiler.compileModuleAndAllComponentsAsync(runtimeModule)
para obterComponentFactory
DynamicComponent
trabalho - do espaço reservado Exibir Destino eComponentFactory
@Inputs
a nova instância (alternar deINPUT
paraTEXTAREA
edição) , consumir@Outputs
NgModule
Nós precisamos de um
NgModule
s.Haverá um módulo para todos os componentes pequenos, por exemplo
string-editor
,text-editor
(date-editor
,number-editor
...)O segundo será o módulo para o nosso manuseio de material dinâmico. Ele conterá componentes de hospedagem e alguns provedores .. que serão singletons. Para isso, publicaremos de maneira padrão - com
forRoot()
Por fim, precisaremos de um módulo adhoc, de tempo de execução ... mas isso será criado posteriormente, como parte do
DynamicTypeBuilder
trabalho.O quarto módulo, módulo de aplicativo, é aquele que mantém declara os provedores de compilador:
Leia (leia) muito mais sobre o NgModule :
Um construtor de modelos
Em nosso exemplo, processaremos detalhes desse tipo de entidade
Para criar um
template
, neste plunker usamos este construtor simples / ingênuo.Um truque aqui é - ele cria um modelo que usa algum conjunto de propriedades conhecidas, por exemplo
entity
. Essas propriedades devem ser parte do componente dinâmico, que criaremos a seguir.Para facilitar um pouco, podemos usar uma interface para definir propriedades que nosso construtor de modelos pode usar. Isso será implementado pelo nosso tipo de componente dinâmico.
Um
ComponentFactory
construtorO mais importante aqui é ter em mente:
Então, estamos tocando o núcleo da nossa solução. O Construtor 1) criará
ComponentType
2) criaráNgModule
3) compilaráComponentFactory
4) armazenará em cache para reutilização posterior.Uma dependência que precisamos receber:
E aqui está um trecho de como obter
ComponentFactory
:E aqui estão dois métodos, que representam a maneira realmente legal de criar classes / tipos decorados em tempo de execução. Não apenas
@Component
mas também o@NgModule
Importante:
ComponentFactory
usado hospedando o componenteA peça final é um componente que hospeda o destino do nosso componente dinâmico, por exemplo
<div #dynamicContentPlaceHolder></div>
. Nós obtemos uma referência a ele e usamosComponentFactory
para criar um componente. Em poucas palavras, e aqui estão todas as peças desse componente (se necessário, abra o êmbolo aqui )Vamos primeiro resumir as instruções de importação:
Acabamos de receber construtores de modelos e componentes. A seguir estão as propriedades necessárias para o nosso exemplo (mais nos comentários)
Nesse cenário simples, nosso componente de hospedagem não possui nenhum
@Input
. Portanto, não precisa reagir às mudanças. Mas, apesar desse fato (e estar pronto para as próximas mudanças) - precisamos introduzir algum sinalizador se o componente já foi (em primeiro lugar) iniciado. E só então podemos começar a mágica.Finalmente, usaremos nosso construtor de componentes, e ele é apenas compilado / armazenado em cache
ComponentFacotry
. Nosso espaço reservado alvo será solicitado para instanciar oComponent
com essa fábrica.pequena extensão
Além disso, precisamos manter uma referência ao modelo compilado ... para podermos usá-
destroy()
lo adequadamente , sempre que o alterarmos.feito
É isso mesmo. Não se esqueça de Destruir qualquer coisa que foi construída dinamicamente (ngOnDestroy) . Além disso, certifique-se de armazenar em cache dinâmico
types
emodules
se a única diferença é o modelo deles.Confira tudo em ação aqui
fonte
type.builder.ts
como você apontou, eu gostaria que qualquer usuário entendesse como colocar tudo isso em contexto ... espero que isso poderia ser útil;)EDIT (26/08/2017) : A solução abaixo funciona bem com Angular2 e 4. Atualizei-a para conter uma variável de modelo e manipulador de cliques e testei-a com Angular 4.3.
Para Angular4, ngComponentOutlet, conforme descrito na resposta de Ophir, é uma solução muito melhor. Mas, no momento, ele ainda não suporta entradas e saídas . Se [this PR] ( https://github.com/angular/angular/pull/15362] for aceito, seria possível através da instância do componente retornada pelo evento create.
Ng-dynamic-component pode ser o melhor e o mais simples solução completamente, mas ainda não testei isso.
A resposta da @Long Field está no local! Aqui está outro exemplo (síncrono):
Ao vivo em http://plnkr.co/edit/fdP9Oc .
fonte
ngAfterViewInit
chamada com aconst template
não funciona. Mas se a sua tarefa consistia em reduzir a abordagem descrita acima detalhado (criar modelo, criar o componente, criar módulo, compilá-lo, criar fábrica .. criar instância) ... você provavelmente fez issoDevo ter chegado tarde à festa, nenhuma das soluções aqui me pareceu útil - muito bagunçada e parecia uma solução alternativa demais.
O que acabei fazendo é usar
Angular 4.0.0-beta.6
o ngComponentOutlet .Isso me deu a solução mais curta e mais simples, tudo escrito no arquivo do componente dinâmico.
my-component
- o componente no qual um componente dinâmico está renderizandoDynamicComponent
- o componente a ser construído dinamicamente e é renderizado dentro do meu componenteNão se esqueça de atualizar todas as bibliotecas angulares para ^ Angular 4.0.0
Espero que isso ajude, boa sorte!
ATUALIZAR
Também funciona para o angular 5.
fonte
2019 junho resposta
Boas notícias! Parece que o pacote @ angular / cdk agora tem suporte de primeira classe para portais !
Até o momento da redação deste artigo, eu não achava os documentos oficiais acima particularmente úteis (principalmente no que diz respeito ao envio de dados e ao recebimento de eventos dos componentes dinâmicos). Em resumo, você precisará:
Etapa 1) Atualize seu
AppModule
Importe
PortalModule
do@angular/cdk/portal
pacote e registre seu (s) componente (s) dinâmico (s) dentroentryComponents
Etapa 2. Opção A: Se você NÃO precisar passar dados e receber eventos de seus componentes dinâmicos :
Veja em ação
Etapa 2. Opção B: Se você precisar passar dados e receber eventos de seus componentes dinâmicos :
Veja em ação
fonte
Portal
abordagem diferengTemplateOutlet
engComponentOutlet
? GlDecidi compactar tudo o que aprendi em um arquivo . Há muito o que fazer aqui, especialmente em comparação com antes do RC5. Observe que esse arquivo de origem inclui o AppModule e AppComponent.
fonte
Eu tenho um exemplo simples para mostrar como fazer componente dinâmico angular 2 rc6.
Digamos, você tem um template html dinâmico = template1 e deseja carregar dinamicamente, primeiro empacote no componente
aqui template1 como html, pode estar contém componente ng2
Na rc6, é necessário que o @NgModule envolva esse componente. O @NgModule, assim como o módulo no anglarJS 1, desacopla diferentes partes da aplicação ng2, portanto:
(Aqui importe o RouterModule, como no meu exemplo, existem alguns componentes de rota no meu html, como você pode ver mais adiante)
Agora você pode compilar o DynamicModule como:
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule).then( factory => factory.componentFactories.find(x => x.componentType === DynamicComponent))
E precisamos colocar acima em app.moudule.ts para carregá-lo, consulte meu app.moudle.ts. Para obter mais detalhes, consulte: https://github.com/Longfld/DynamicalRouter/blob/master/app/MyRouterLink.ts e app.moudle.ts
e veja a demonstração: http://plnkr.co/edit/1fdAYP5PAbiHdJfTKgWo?p=preview
fonte
No angular 7.x, usei elementos angulares para isso.
Instalar @ angular-elements npm i @ angular / elements -s
Crie serviço acessório.
Observe que a tag do elemento personalizado deve ser diferente com o seletor de componente angular. em AppUserIconComponent:
e, nesse caso, o nome da tag personalizada, usei "user-icon".
ou assim:
(no modelo):
Observe que, no segundo caso, você deve passar objetos com JSON.stringify e depois analisá-lo novamente. Não consigo encontrar uma solução melhor.
fonte
document.createElement(tagName);
Resolvido isso na versão final do Angular 2 simplesmente usando a diretiva dynamicComponent da ng-dynamic .
Uso:
Onde template é seu modelo dinâmico, o contexto pode ser definido como qualquer modelo de dados dinâmico ao qual você deseja que seu modelo se ligue.
fonte
Quero acrescentar alguns detalhes sobre este excelente post de Radim.
Peguei essa solução e trabalhei nela um pouco e rapidamente encontrei algumas limitações. Vou apenas descrevê-las e depois dar a solução para isso também.
Fiz outra pergunta com base neste post, sobre como obter essas limitações, que podem ser encontradas aqui:
compilação dinâmica de modelo recursivo em angular2
Vou descrever as respostas para essas limitações, caso você tenha o mesmo problema que eu, pois isso torna a solução muito mais flexível. Seria incrível ter o plunker inicial atualizado com isso também.
Para habilitar o aninhamento de detalhes dinâmicos, você precisará adicionar DynamicModule.forRoot () na instrução de importação no type.builder.ts
Além disso, não era possível usar
<dynamic-detail>
dentro de uma das partes o editor de string ou o editor de texto.Para permitir que você precise alterar
parts.module.ts
edynamic.module.ts
Dentro
parts.module.ts
Você precisará adicionarDynamicDetail
oDYNAMIC_DIRECTIVES
Além disso,
dynamic.module.ts
você teria que remover o dynamicDetail, pois agora eles fazem parte das partesUm plunker modificado em funcionamento pode ser encontrado aqui: http://plnkr.co/edit/UYnQHF?p=preview (Eu não resolvi esse problema, sou apenas o mensageiro :-D)
Por fim, não foi possível usar templatesurls nas peças criadas nos componentes dinâmicos. Uma solução (ou solução alternativa. Não sei se é um bug angular ou o uso incorreto da estrutura) foi criar um compilador no construtor em vez de injetá-lo.
Em seguida, use o
_compiler
para compilar, e templateUrls também serão ativados.Espero que isso ajude alguém!
Atenciosamente Morten
fonte
Seguindo a excelente resposta de Radmin, é necessário um pequeno ajuste para todos que estão usando o angular-cli versão 1.0.0-beta.22 e superior.
COMPILER_PROVIDERS
não pode mais ser importado (para obter detalhes, consulte angular-cli GitHub ).Portanto, a solução alternativa é não usar
COMPILER_PROVIDERS
eJitCompiler
naproviders
seção, mas usarJitCompilerFactory
'@ angular / compiler' como este dentro da classe do construtor de tipos:Como você pode ver, não é injetável e, portanto, não tem dependências com o DI. Essa solução também deve funcionar para projetos que não usam angular-cli.
fonte
Eu mesmo estou tentando ver como atualizar o RC4 para o RC5 e, portanto, me deparei com essa entrada e uma nova abordagem para a criação dinâmica de componentes ainda me traz um pouco de mistério, então não vou sugerir nada sobre o resolvedor de fábrica de componentes.
Mas, o que posso sugerir é uma abordagem um pouco mais clara da criação de componentes nesse cenário - basta usar a opção no modelo que criaria o editor de strings ou o editor de texto de acordo com alguma condição, como esta:
E, a propósito, "[" na expressão [prop] tem um significado, isso indica uma ligação de dados unidirecional; portanto, você pode e deve omitir esses, caso saiba que não precisa vincular propriedade à variável.
fonte
switch
/case
contiver poucas decisões. Mas imagine que o modelo gerado possa ser realmente grande ... e diferir para cada entidade, diferir por segurança, diferir por status da entidade, por cada tipo de propriedade (número, data, referência ... editores) ... Nesse caso, resolver isso no modelo html comngSwitch
criaria umhtml
arquivo muito, muito muito grande .Este é o exemplo dos controles dinâmicos de formulário gerados no servidor.
https://stackblitz.com/edit/angular-t3mmg6
Este exemplo é dinâmico. Os controles de formulário estão no componente add (é aqui que você pode obter os controles de formulário do servidor). Se você vir o método addcomponent, poderá ver os controles de formulários. Neste exemplo, não estou usando material angular, mas funciona (estou usando @ work). Este é o alvo para o angular 6, mas funciona em todas as versões anteriores.
É necessário adicionar JITComplierFactory para AngularVersion 5 e superior.
obrigado
Vijay
fonte
Nesse caso específico, parece que usar uma diretiva para criar dinamicamente o componente seria uma opção melhor. Exemplo:
No HTML em que você deseja criar o componente
Eu abordaria e projetaria a diretiva da seguinte maneira.
Portanto, em seus componentes, texto, string, data, o que for - qualquer que seja a configuração que você passou no HTML no
ng-container
elemento, estaria disponível.A configuração,,
yourConfig
pode ser a mesma e definir seus metadados.Dependendo da configuração ou do tipo de entrada, a diretiva deve agir de acordo e dos tipos suportados, renderizará o componente apropriado. Caso contrário, ele registrará um erro.
fonte
Com base na resposta de Ophir Stern, aqui está uma variante que funciona com o AoT no Angular 4. O único problema que tenho é que não posso injetar nenhum serviço no DynamicComponent, mas posso conviver com isso.
nota: eu não testei com o Angular 5.
Espero que isto ajude.
Felicidades!
fonte