Angular: Proposta: Capacidade necessária para adicionar diretivas aos elementos do host na declaração do componente.

Criado em 23 mai. 2016  ·  114Comentários  ·  Fonte: angular/angular

Tenho pesquisado o Angular 2 e encontrei um obstáculo em potencial para estender certos tipos de componentes.

No exemplo a seguir, tenho um componente de botão e uma diretiva que aplicará estilos com base em eventos de toque. Haverá muitos outros objetos além do botão que herdarão exatamente o mesmo comportamento de toque. Eu explorei minhas opções e estou perplexo:

  • Estenda diretamente um TouchClass. Isso parece menos do que ideal, já que o texto digitado não oferece suporte a herança de várias classes, e eu também gostaria de expor o comportamento aos consumidores para uso em suas próprias classes.
  • Herança de múltiplas classes falsas por meio de uma interface. Isso parece um hack e exige que eu redeclarar um shim api em todas as classes que estou tentando misturar. https://www.stevefenton.co.uk/2014/02/TypeScript-Mixins-Part-One/
  • Crie uma função auxiliar que faça isso por meio de um serviço diretamente em elementRef.nativeElement no construtor do componente. Eu realmente não quero fazer isso, pois afirma nos documentos que nativeElement será nulo ao ser executado em um trabalhador, e esse recurso é o que estou mais animado.

Sem me aprofundar muito, presumo que o componentMetadata está disponível durante o tempo de compilação dos componentes e que a propriedade host pode ser verificada em busca de diretivas adicionais que podem ser adicionadas dinamicamente e compiladas ao mesmo tempo. Isso permitiria que você fizesse mixins de maneira angular: usando diretivas composíveis para estender a funcionalidade e sem interromper a projeção da vista. Pequeno exemplo abaixo.

Comportamento atual
Declarar uma diretiva em componentMetadata.host a trata como um atributo regular

Comportamento esperado / desejado
A diretiva declarada no host seria processada em tempo de compilação.

/**
 * App
 */
@Component({
    selector: 'app-component',
    template: '<g-btn>TEST</g-btn>',
    directives: [gBtn, gTouch]
})

export class AppComponent {
    constructor() {

    }
}

/**
 * Touch Directive
 * Will be used in lots and lots of components
 */
@Directive({
    selector: '[g-touch]',
    host: { 
        '(touchstart)': '...',
        '(touchend)': '...',
        '(touchmove)': '...',
        '(touchcancel)': '...'
    }
})

export class gTouch {
    constructor() {

    }
}

/**
 * Simple button component
 */
@Component({
    selector: 'g-btn',
    template: '<ng-content></ng-content>',
    host: {
        'role': 'button',
        // WOULD LOVE FOR THIS TO COMPILE THE DIRECTIVE!
        // right now it just adds an attribute called g-touch
        'g-touch': ' ' 
    }
})

export class gBtn {

    constructor() {

    }
}

Algumas idéias de como isso poderia funcionar:

// Option 1: just scan the host properties for directives.
// This would be my ideal, simple and understandable
@Component({
    selector: 'g-btn',
    template: '<ng-content></ng-content>',
    host: {
        'role': 'button',
        'g-touch': true // or {prop: 'foo'} or string
    }
})

// Option 2: definitely more declarative using a hostDirectives property
// more declarative, albeit more annoying to have to reimport the touch class
@Component({
    selector: 'g-btn',
    template: '<ng-content></ng-content>',
    hostDirectives: gTouch,
    host: {
        'role': 'button',
        'g-touch': true
    }
})

// Option 3: declare host directives as its own thing, still just
// use keys pointing to bool, obj, or string
@Component({
    selector: 'g-btn',
    template: '<ng-content></ng-content>',
    hostDirectives: {
        'g-touch': {someOption: someOption}
    },
    host: {
        'role': 'button',
    }
});

// Option 4: Not a huge fan of this one, but understandable if
// people want to keep one host property
@Component({
    selector: 'g-btn',
    template: '<ng-content></ng-content>',
    host: {
        'role': 'button',
        _directives: {
            'g-touch': true
        }
    }
});

Obrigado a todos, o Angular 2 está ótimo !. Avise-me se estiver faltando alguma coisa.

core directive matching host and host bindings feature

Comentários muito úteis

O trabalho da Ivy

Todos 114 comentários

Atualmente, estou desenvolvendo um grande cliente e, portanto, estou tentando dividir todos os problemas relacionados à GUI em diretivas Angular2 reutilizáveis. Isso sempre me leva ao mesmo problema, como james apontou perfeitamente.
Isso realmente tem que funcionar de alguma forma em prol de uma boa arquitetura modular e dinâmica. O exemplo de toque é apenas um dos muitos cenários em que isso é necessário. por exemplo, arrastar e soltar, observar redimensionamento, etc. etc. etc.
Fez outro exemplo como êmbolo:
https://plnkr.co/edit/J65THEMic0yhObt1LkCu?p=info

Existe alguma chance dessa funcionalidade ser adicionada em breve?

Aqui está uma pergunta StackOverflow relacionada a isso: http://stackoverflow.com/questions/37148080/use-angular2-directive-in-host-of-another-directive

@ Andy1605 , você já encontrou uma maneira de contornar isso? Eu meio que tabulei o trabalho com NG2 por causa disso durante os RCs. Adoraria retomá-lo, mas esse problema específico me impede de construir padrões extensíveis de IU.

Também acho que o Angular está faltando um recurso essencial aqui. Deve ser possível para um componente declarar (múltiplas) diretivas de atributo para seu host. Não poder fazer isso é um grande obstáculo para o meu projeto também.
Alguém sabe se isso será implementado no futuro ou se há razões pelas quais não pode ser feito?

Propus uma solução para esse problema (embora para a versão 1 do angular) aqui: https://github.com/angular/angular.js/issues/15270.

Minha ideia é, em vez de apenas ter a capacidade de adicionar diretivas, a estrutura de compilação teria um novo ponto de extensibilidade chamado "hostTransforms" (no caso de angular 1, "nodeTransforms") que teria acesso à declaração de componente não modificada e não filtrada e o nó host do componente original não compilado sempre que um componente é encontrado pela primeira vez pelo compilador e está sendo preparado para inserção no DOM. Dessa forma, um desenvolvedor pode estender decoradores de componente com propriedades personalizadas e, em seguida, usar nodeTransforms para converter essas propriedades personalizadas em algo com o qual a estrutura angular esteja familiarizada, pouco antes da compilação. Verifique o thread de solicitação de recurso para exemplos.

Estou mais familiarizado com o código-fonte angular do que com o código-fonte angular 2, então não tenho certeza se o processo de implementação seria o mesmo aqui. Mas como essa parece ser uma solicitação bastante popular, adoraria vê-la implementada no angular 2 e portada para trás, ou implementada no angularjs e portada para frente (isso é uma coisa?).

+1

Devo concordar, um recurso que nos permite adicionar diretivas de atributo de contribuição para o host seria ótimo. Eu sei que poderia usar esse recurso agora, implementando uma forma mais "angular", como adicionar a funcionalidade de arrastar / soltar aos meus componentes de IU.

Que tal criar uma nova tag semelhante a <ng-container> que permite aplicá-los no modelo do componente em vez da propriedade de metadados host ? Algo como <ng-host [attributeDirective]> para indicar que as diretivas são adicionadas ao componente host.

@jjstreet sua proposta parece semelhante a replace: true (obviamente não idêntica, mas semelhante), que foi descontinuada há algum tempo. Mas talvez replace: true sido descontinuado por um motivo que não se aplica aqui.

@ pkozlowski-opensource Podemos obter algum tipo de resposta da equipe ng2 sobre isso?

Estou pronto para qualquer maneira de conseguir isso. Sugeri a propriedade host porque ela tem acesso ao escopo local do componente e já adiciona atributos ao próprio componente. As diretivas parecem uma extensão natural desse comportamento.

+1 este recurso é necessário para ter um código limpo e reutilizável nos componentes da IU

+1

Podemos obter algum tipo de resposta da equipe ng2 sobre isso? Mesmo que seja apenas para dizer que você não vai fazer isso, ou para dizer que é uma boa ideia, mas não uma prioridade atual, gostaria apenas de ouvir algum tipo de contribuição.

Eu gostaria de adicionar outro caso de uso para isso. Isso permitiria que o ng2-mobx (https://github.com/500tech/ng2-mobx) se livrasse do componente de embalagem e parecesse muito mais limpo.

Eu adoraria ter isso também. Atualmente preciso fazer a diretiva routerLink . Eu adoraria reutilizar um angular e apenas fornecer a ele os parâmetros preparados pela diretiva mi.

Portanto, em vez de <a [routerLink]="repeatedCodeToGetLink()"> eu teria <a [myRouterLink]> e aplicaria dinamicamente [routerLink] com os parâmetros resolvidos.

Muito animado com as perspectivas disso!

Precisamos disso por um tempo. Na verdade, um pouco antes de saber que havia um problema em aberto para ele, perguntei no estouro de pilha sobre essencialmente esse recurso.

Eu forneci um exemplo elaborado de como poderíamos usar esse recurso para resolver https://github.com/angular/flex-layout/issues/162 que temos aberto há algum tempo. ( Veja o exemplo e a explicação aqui )

Estamos realmente ansiosos por qualquer feedback. Vejo que este problema é o terceiro mais manuseado de todos os problemas em aberto neste repo. Esperançosamente, podemos ver isso no próximo lançamento ou antes (dedos cruzados)!

/ cc @tbosch @IgorMinar @mhevery @jelbourn @hansl @ThomasBurleson

@jjstreet acho que sua sugestão

<ng-host myDirective="foo"></ng-host> 

... ficaria bem com outra proposta separada que foi feita há algum tempo por motivos distintos do que estamos discutindo aqui.

Veja https://github.com/angular/angular/issues/7297

Atualmente, eu trabalho em torno disso adicionando diretiva no componente pai e, em seguida, adiciono ouvinte no host com @HostListener.

Parent.html
<my-component myDirective>

Component.ts
@HostListener('myEvent') handler() { // do stuff }

Mas seria mais limpo se pudéssemos adicionar atributos diretamente no host ...

Aqui está como tenho lidado com isso, mas realmente acho que implementar esse recurso desde o início seria a melhor solução.

Apenas um lembrete mensal de que estamos aguardando comentários positivos ou negativos sobre isso da equipe Angular.

@tbosch - Qualquer opinião pública sobre a prioridade desta questão. Também afeta @angular/flex-layout .

@fadzic você não pode simplesmente adicionar a diretiva ao elemento host ao fazer isso ...

Component.ts
@HostBinding('attr.myHilite') myHilite = new myHiliteDirective()

ou assim se você precisar de parâmetros como ElementRef ou Renderer2
@HostBinding('attr.myHilite') myHilite = new myHiliteDirective(this.elementRef, this.renderer)

Também tenho a necessidade de adicionar uma diretiva ao elemento host e fui redirecionado para este problema. Consegui fazer o que precisava usando o código acima. Não sou de forma alguma um especialista no uso do angular, mas essa solução alternativa parece funcionar até agora. Se alguém tiver problemas com essa abordagem, me avise. Obrigado.

@btinoco que não funciona porque nenhum método de ciclo de vida é chamado. Você teria que conectar manualmente tudo em cada componente que usa a diretiva, em vez de apenas ter o Angular conectado para você.

@hccampos obrigado pelo ngOnInit da minha diretiva não foi executado (a menos que eu use a diretiva no meu componente manualmente) ou chamo a diretiva ngOnInit() do meu componente ngOnInit() . Mais uma vez, obrigado por me informar disso.

@btinoco - sim. é uma questão sutil, mas desagradável. Um que @ angular / flex-layout espera seja consertado em breve. ;-)

Alguma notícia da equipe Angular sobre isso? Já se passou 1 ano desde que o problema foi aberto ...

Encontrar esta descrição detalhada sobre este problema foi muito legal,
então, não encontrar feedback da equipe Angular foi muito chato :(

Em relação às soluções já funcionando:

Esta solicitação de recurso se parece muito com mixins. Na verdade, ponto 2 na descrição
deste recurso realmente corresponde ao oficial
documentação do TypeScript, veja aqui .
No angular, isso se torna um pouco pior, pois misturar uma classe com @Input() s, você
tem que redeclará-los na classe base.

Outra solução que já funciona hoje seria fazer com que o componente contenha um elemento wrapper e aplicar as diretivas nele.
Por exemplo, se o componente continha um modelo como <wrapper g-touch>...

Sobre "Criar uma função auxiliar que faça isso por meio de um serviço diretamente em elementRef.nativeElement":
Sim, também parece uma boa ideia. Eu não me importaria com WebWorkers agora,
porque ainda são experimentais e faltam alguns recursos maiores para produção,
e quase nenhuma biblioteca funcionaria no WebWorkers.
Veja, por exemplo, também nossa biblioteca de materiais que acessa o DOM diretamente.

Em relação à Opção 1) da proposta:

A semântica atual para associações de propriedade de host é,
que definam uma propriedade myDir no elemento HTML subjacente,
mas não qualquer diretiva. No entanto, se host também pode introduzir diretivas, os usuários podem escrever o seguinte
e ficaria confuso por que isso não atualiza a propriedade na diretiva myDir :

@Component({
  host: {
    '[myDir]': true
  },
  template: '...'
})
class MyComp {}

Em relação à Opção 1) e Opção 3):
A introdução de algum tipo de vinculação host entre diretivas no mesmo elemento pode:

  • leva a um ciclo no gráfico de vinculação de dados, que o Angular não suporta e, portanto,
    levam a erros difíceis de depurar devido a valores desatualizados / erros "A expressão foi alterada após verificação".
  • levam a uma sobrecarga de desempenho adicional, em comparação com as diretivas que se injetam
    e comunicar-se diretamente.

Em relação à Opção 2) da proposta:

  • sim, ter que se referir à classe gTouch parece estranho, como todas as outras diretivas
    são acionados por meio de NgModule s.

@ThomasBurleson vamos falar offline sobre seu caso de uso com mais detalhes ...

@tbosch Eu gostaria de propor outra opção: introduzir uma tag angular nativa, vamos chamá-la de <ng-host> .

Nota: @mhevery propôs a introdução de uma tag <ng-host> em https://github.com/angular/angular/issues/7546 , no entanto, embora eu esteja usando o mesmo nome de tag aqui, o que eu sou propor é separado e destina-se especificamente a abordar a questão que foi levantada aqui.

A tag ng-host não seria implementada como uma diretiva / classe de componente regular, mas, em vez disso, seria um código de estrutura "mágico" ... semelhante a ng-content , ng-container , etc. .,
A tag serviria simplesmente como um "ponteiro" para o host do componente de uma forma análoga ao seletor css

Para evitar cenários ambíguos, cada componente só poderia ter, no máximo, um bloco <ng-host> e deveria ser a marca / nó raiz do modelo desse componente.

Aqui está como alguém o usaria:

// Option 5: Use `<ng-host>`.. Very declarative and intuitive
@Component({
  selector: 'g-btn',
  template: `
    <!-- 
      Besides comments, having dom code inside the template but outside of a declared 
      ng-host code block would raise an error (hopefully at compile-time) .
    -->

    <ng-host role="button" g-touch> 
      <ng-content></ng-content>
    </ng-host>
  `
})

A propósito, @tbosch , obrigado por responder. Nós realmente apreciamos seu envolvimento e feedback sobre este assunto.

Os pensamentos de todos os outros sobre esta funcionalidade sendo específicos para componentes, ou também faria sentido se uma diretiva pudesse aplicar uma diretiva diferente para seu host? O caso de uso em que comecei a me inscrever neste problema envolvia algumas diretivas de terceiros que A) queríamos isolar de nosso código, caso quiséssemos mudar mais tarde e B) queria aplicar alguma funcionalidade padrão a cada instância sem ter que duplicar a configuração sempre que a usamos.

Por exemplo, uma diretiva de dica de ferramenta, que será aplicada em um grande número de elementos em nosso aplicativo, e queremos padronizar o atraso e appendToBody (não suporta um objeto de configuração centralizado). Como ele não suportava um objeto de configuração central, tivemos que colocar três ou quatro atributos em todos os lugares que queríamos usá-lo. E mais tarde, acabamos nos afastando dessa biblioteca (começamos a usar dicas de ferramentas de material) e tivemos que substituir manualmente todas as dicas de ferramentas. Se tivéssemos sido capazes de criar nossa própria diretiva que a "envolvesse", teria sido tão simples quanto alterar essa diretiva para aplicar [mdTooltip] ao seu host em vez de à outra biblioteca.

@MikeMatusz Parece que eu também tinha isso em mente, aqui está meu snippet de https://github.com/angular/flex-layout/issues/162#issuecomment -290350270.

@Directive({
  selector: 'fxLayoutFullPage',
  hostDirectives: [LayoutDirective],
  host: { 
    'fxLayout': 'column', 
    'style': 'min-height:100vh; background-color:yellow'
  }, 
}) class LayoutFullPageDirective {}

Seria possível criar um decorador de propriedade que instancia uma diretiva?
Por exemplo:
@HostDirective(LayoutDirective) myLayoutDirective: LayoutDirective;

Isso funcionaria tanto para componentes quanto para diretivas, forneceria uma referência de instância para interação e não se perderia ao herdar do componente / diretiva.
Acho que fica mais complicado se você também quiser fornecer ligações de entrada e de evento.

Onde fica isso? Sou bastante novo no Angular2 / 4 e o que quero fazer é criar uma diretiva que apenas aplique várias outras diretivas ao mesmo tempo. Então, em vez de:

<button directiveA directiveB directiveC>BUTTON TEXT</button>

Posso apenas escrever:

<button customDirectiveABC>BUTTON TEXT</button>

Parece que isso deve ser fácil - composição básica / SECAGEM. Mas, pelo que posso dizer, não é possível?

@soynog , sinto exatamente o mesmo. Eu também gostaria de saber onde isso está.

Eu esperava poder fazer diálogos arrastáveis ​​usando Angular Material e angular2-draggable (já que angular / material # 1206 ainda não é suportado) onde gostaria de adicionar dinamicamente uma diretiva a md-dialog-container que MdDialog serviço cria, mas parece muito mais difícil obter o comportamento do compilador Angular 1.x aqui para diretivas dinâmicas.

@tbosch , @ThomasBurleson , o caso de uso offline que você discutiu estava relacionado aos problemas ou objetivos que Thomas levantou no angular / material # 1206 por acaso? Estou apenas tentando entender as mudanças de comportamento entre os frameworks 1.6.xe 2+.

Há alguma atualização sobre esse problema? Ganhou força no começo, mas acho que não está mais recebendo atenção.

Sim, alguma atualização sobre isso?

Isso é algo de que eu preciso tanto, espero que esta proposta seja levada adiante.

Isso seria legal, percebi hoje que não consigo aplicar diretivas de forma programática / dinâmica, fiquei triste.

+1
O mesmo para mim :)
Estou procurando uma maneira de envolver a vinculação de várias diretivas em uma diretiva personalizada que faça tudo o que preciso. Por exemplo :

<my-cmp [myDirective]="content"
        [isOpen]="myCondition"
        customProp2="customClass"
        customProp1="customText">
 ...
</my-cmp>

Seria bom se eu pudesse criar uma diretiva que envolvesse todas essas coisas para que eu pudesse reutilizá-las sem copiar / colar todas as linhas.

<my-cmp myCustomDirective>
</my-cmp>

E em minha diretiva personalizada

<ng-host [myDirective]="content"
        [isOpen]="myCondition"
        customProp2="customClass"
        customProp1="customText">
</ng-host>

chegando no segundo aniversário desta edição! honestamente, esse recurso seria tão, tão útil, que nos permite criar componentes e diretivas altamente combináveis ​​sem ter que criar um milhão de invólucros. apenas componha o componente que você precisa a partir das diretivas que você possui. simples, limpo, eficaz.

@IgorMinar - De qualquer forma, podemos colocar esse recurso no radar para os próximos aprimoramentos?

Gostaria de saber se esse recurso seria considerado um padrão ruim ou não. Qualquer um?

@darkbasic - AFAIU, sem esse recurso, um desenvolvedor precisaria introduzir um elemento wrapper (em ng-container ) simplesmente para adicionar diretivas pai à visualização e ao conteúdo do modelo.

Não, eu não acho que ser capaz de ter controle total de seu próprio componente sem ter que usar invólucros é um padrão ruim. É uma necessidade.

@bradlygreen algum comentário?

Este recurso é o pedido mais popular (senão o 5 mais popular) entre todas as questões em aberto deste repo ... Em toda a Internet, estamos vendo relatórios (apoiados por dados empíricos) do declínio do Angular como a estrutura de fato ... Eu acho uma das coisas que impulsionam isso é o sentimento de que a comunidade não está sendo ouvida. A competição; vue.js e react, estão ganhando terreno e ultrapassaram o angular porque, embora não necessariamente implementem tudo que todos desejam, eles pelo menos fornecem feedback contínuo sobre os itens mais populares solicitados. É tão frustrante esperar tanto tempo e não ouvir nada .. nem mesmo um simples "não, não faremos".

(Consulte a seção de estruturas Js "Deslizamentos angulares" )

... embora eu ache que algumas opiniões sobre Angular / Vue / React / ... são influenciadas por diferentes fatores ... esta característica concreta seria realmente digna de alguma forma de implementação (mesmo as circunstâncias são um pouco mais complicadas do que apenas um solução com uma lista simples de diretivas aplicadas) ... então a posição concreta da equipe principal Angular seria muito bem-vinda ... 🥇

Sem HEC específico, mas estamos trabalhando para tornar essa categoria de coisas muito mais fácil no renderizador em 2018.

Espero que as coisas melhorem drasticamente em 2018. Estamos perdendo

Ver:

@somombo, este artigo foi confirmado como uma besteira há muito tempo

Pessoas que realmente conhecem suas coisas zombam do autor e nenhum deles o leva a sério, os gostos são de react, vue fanboys, naturalmente.

Então o fato é que essa questão aqui é uma prioridade muito baixa para o time angular, na verdade é a prioridade mais baixa possível.

Consulte a lista de prioridades publicada em AngularHQ (procure o número de problema 8785)

Este é o caso, apesar de esse problema ter gerado muita discussão e interesse por parte da comunidade, conforme demonstrado pelo número de comentários.

Se você é alguém que se preocupa com este problema e realmente gostaria de vê-lo implementado, então, em vez de esperar ... bem, honestamente _potencialmente nunca_, talvez você possa preencher a Pesquisa Angular Anual Oficial e deixar claro que você se sente assim deve ser uma prioridade mais alta e gostaria de vê-lo preenchido mais cedo ou mais tarde.

Não se esqueça de agradecer a nossa Equipe Angular por todo o excelente trabalho que realizaram!

Eu também gostaria de votar neste recurso. Isso tem sido a causa de muitas tentativas de contornar esse problema.

@somombo, por favor, não leia muito sobre a prioridade no AngularHQ ainda. a fórmula de prioridade não está totalmente desenvolvida. tendo dito isso, acho que devemos revisitar esta solicitação de recurso após o lançamento da v6. Receio que não tenhamos largura de banda para isso mais cedo e trabalhar nisso entraria em conflito com um trabalho já em andamento na área do compilador / núcleo.

Este não é um pedido de solução rápida. Suspeito que será necessário um esforço considerável para fazê-lo corretamente, mas as coisas em que estamos trabalhando para a v6 devem tornar isso muito mais fácil de implementar.

O trabalho da Ivy

@IgorMinar e @mhevery Não posso enfatizar o suficiente o quão grato estou (e o resto de nós também, tenho certeza) por você ter nos dado este feedback concreto sobre quais são seus pensamentos e o que precisa acontecer primeiro antes que este problema possa ser devidamente endereçados.

Nem sempre está claro para nós, leigos, o que é uma "solução rápida" e o que não é. No entanto, exceto o fato de que este não é um tipo de solução rápida e tem que ser feito da maneira certa, estou especialmente grato que você também sinta que este será um recurso útil para o Angular.

Sabemos que vocês estão muito ocupados e não podem responder dessa forma a todas as questões.
Portanto, você terá nossa sincera gratidão sempre que o fizer. Estamos animados e ansiosos para o angular v6 e além!

Obrigado por todo o excelente trabalho!

Você pode fazer com que sua classe de componente estenda ou implemente a classe de diretiva. Se você está tentando aplicar a diretiva nos bastidores, provavelmente deve haver apenas lógica no componente.

export class gBtn extends gTouch

@NateMay , que só permite estender uma única classe. Este problema é mais sobre a composição de várias partes da funcionalidade usando diretivas.

@NateMay dois problemas com isso - primeiro, você só pode estender uma única classe e, segundo, você acabou de quebrar a injeção de dependência.

Apenas adicionando meus dois centavos. Estou construindo um SPA multicamadas com layout angular, material e flexível, usando estados aninhados de @ uirouter / angular. Então, a incapacidade de aplicar facilmente as diretivas flexíveis aos elementos componentes é muito limitante.

Então, um voto para este recurso, por favor.

+1 para este recurso adicionado

É possível adicionar uma diretiva a um ng-container , que não aparecerá no DOM.

Eu precisava disso para a API do observador de interseção (que gera eventos quando os elementos entram / saem da janela de visualização). Eu tenho uma diretiva intersector , que tem eventos enter() e leave() quando o elemento se torna visível / oculto. Tenho certos componentes que precisam usar essa API internamente, mas não queria adicionar um DIV extra no modelo.

Então, o que fiz foi o seguinte em component.html :

<ng-container intersector (enter)="weCameOnScreen()" (leave)="byeBye()">
     ... components normal template ...
</ng-container>

Em seguida, o construtor da diretiva intersector.directive.ts injeta o ElementRef .

    constructor(private intersectorElementRef: ElementRef) { ... }

Para um elemento DOM normal, você apenas operaria em intersectorElementRef.nativeElement , mas para ng-container o nó é na verdade um nó de comentário. Então, eu apenas verifico se é um comentário e, se for, subo um nível.

public ngAfterViewInit(): void 
{
    // if the directive is applied to an ng-container must go a level up
    this.domElement = (this.intersectorElementRef.nativeElement.nodeType == 8) ? this.intersectorElementRef.nativeElement.parentElement : this.intersectorElementRef.nativeElement;

   registerIntersector(this.domElement ...);

Isso não vai funcionar para todas as situações, mas estou bem com isso por agora. Eu acredito que no compilador IVY eles podem não estar mais usando comentários - então isso pode falhar. O importante é que eu tenho uma única diretiva que posso usar em nós DOM ou no que é efetivamente um ' @HostBinding ' falso para a diretiva.

Eu realmente esperava que fosse possível adicionar diretivas dinamicamente. Eu quero ser capaz de encapsular as diretivas de nível inferior em diretivas de "ordem superior", mais abstratas. Eu fiz a seguinte pergunta sobre estouro de pilha e queria saber se haverá uma solução para isso no futuro: https://stackoverflow.com/questions/51608645/abstract-away-leaflet-directive-in-custom-directive

como @mhevery disse .. precisamos ser pacientes e esperar a versão completa ivy (ng v7.0.0?) para pousar. Aparentemente, será muito mais fácil para eles implementar com ivy ... Depois da ivy, devemos lembrar a equipe da importância desse recurso para nós, para que não se esqueçam dele 😉

Inscrevendo-se nisso. Também preciso ser capaz de anexar dinamicamente uma diretiva a um componente que criei com resolveComponentFactory / createComponent.

const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentItem.component);

const componentRef = viewContainerRef.createComponent(componentFactory);
(<DynamicComponent>componentRef.instance).data = componentItem.data;
(<DynamicComponent>componentRef.instance).cssClassList = componentItem.cssClassList;
// Add directive to new component here
// componentRef.addDirective(someDirective)

Qualquer atualização???
Encontrei outro caso de uso em que estou usando uma diretiva de terceiros.
Em alguns cenários, preciso remover / adicionar diretiva em um elemento HTML dinamicamente.
isso é possível de alguma forma ou ainda pendente a solução?

@micronyks ... na verdade não é possível adicionar uma diretiva dinamicamente. Temos que esperar por Ivy, que deve adicionar a possibilidade de criar tal recurso.

@ mlc-mlapis mas algum plano quando chegará o IVY? em qual versão?

@micronyks ... Angular 7 por programação.

Pessoal, sejamos razoáveis ​​aqui, o Angular Team está se esforçando para trabalhar em vários recursos enormes que são altamente exigidos (PWAs, SSR, Ivy e especialmente Custom Elements) o último sendo um recurso de prioridade muito alta, como eu pude entender, porque muito de grandes empresas (como a Microsoft) sempre pedem por isso, e há uma razão para isso. Para obter Custom Elements eficientes, eles precisam do Ivy, assim que terminarem com o Ivy, como @mhevery disse, o mecanismo permitirá diretivas dinâmicas com mais facilidade.

Enquanto isso, em vez de continuar exigindo esse recurso (que eu também preciso desesperadamente), podemos ajudar a equipe Angular a acelerar o processo, testando os betas, relatando bugs, ajudando com os documentos, etc.

Vamos lembrar que o Angular Team não é nem tão grande, é apenas uma dúzia de pessoas tentando fazer uma estrutura incrível para todos, mas isso leva tempo.

... sim, é necessário ter um pouco de paciência agora e esperar até o momento em que possamos ajudar mais com Ivy ... quando o compilador será concluído e alguns documentos de design de detalhes disponíveis.

@avatsaev Posso concordar com tudo o que você disse. Você não deve exigir coisas aqui. Mas você pode explicar os problemas com os quais está lidando ao trabalhar com o Angular.

Não estou nem perto de ser um desenvolvedor Angular muito experiente, mas algumas coisas parecem erradas ou não são explicadas com clareza suficiente.

Eu me deparei com esse problema porque eu quero encapsular um componente / diretiva de terceiros, sem ter uma abstração com vazamento. Parte disso é possibilitar a existência de diretivas dinâmicas. O que me surpreende é que é bastante complicado conseguir tal coisa.

Estou construindo um gerador de formulários, usando Angular Material e Flex-Layout, que leva uma configuração JSON e gera um formulário. Esse recurso me ajudaria a aplicar as diretivas de layout flexível ao componente host no tempo de execução. Eu sinto que um dos maiores ativos do Angular é a capacidade de gerar código em tempo de execução a partir de uma configuração. Isso ajudará muito a tornar o código mais versátil. Só queria apresentar um bom caso de uso. Impaciente ;)

Esse é o meu caso de uso exato

@NateMay aqui está minha implementação, se você quiser dar uma olhada.

@NateMay aqui está minha implementação, se você quiser dar uma olhada.

você poderia explicar? Eu acho que você quer dizer dynamic-field.directive

O dynamic-field.directive faz coisas sofisticadas, mas há muitas outras coisas acontecendo também. Acabei de adicionar CONTRIBUTING.md na pasta raiz, que tem instruções para configurar localmente. Tenha cuidado ao usar em qualquer coisa que esteja em produção por alguns meses. Estou fazendo grandes mudanças enquanto trabalho para uma implementação estável.

+1

De longe, minhas soluções alternativas são, todas elas têm suas desvantagens.

  1. has-it , defina uma nova propriedade de membro como essa diretiva dentro de minha classe de componente, passe todos os argumentos do construtor necessários para essa diretiva ao chamar sua função de construtor (por exemplo, ElementRef, ViewContainerRef, TemplateRef ... quaisquer variáveis ​​injetáveis ​​que ele solicitar) , e chame manualmente seu retorno de chamada de ciclo de vida se houver, como ngInit() ngAfterViewInit() na função de retorno de chamada de ciclo de vida correspondente do componente atual.
@component(...)
class MyComponent {
   theDirective: TargetDirective;
   constructor(el: ElementRef) {
       this.theDirective = new TargeDirective(el);
   }

  ngOnInit() {
     this.theDirective.ngOnInit();
  }
  ...
}
  1. Envolva tudo no modelo de componentes dentro de um modelo ng de nível superior,
    <ng-template><div targetDirective>....</div></ng-template> renderiza-os em ngAfterViewInit() como:
const vf = this.viewContainerRef.createEmbeddedView(this.templateRef);
vf.detectChanges();

Dessa forma, o Angular cria outro element com essa diretiva e o conteúdo real do meu componente dentro dele logo após o elemento do componente original na árvore DOM.

<my-component></my-component>
<div targetDirective>....</div>

Assim é como <route-outlet> faz.

Existem efeitos colaterais óbvios

Alguém pode confirmar se isso agora é possível com Ivy? Se sim, alguém tem um exemplo?

Vamos lembrar que o Angular Team não é nem tão grande, é apenas uma dúzia de pessoas tentando fazer uma estrutura incrível para todos, mas isso leva tempo.

Poderia ser maior com uma comunidade de colaboradores.

No entanto, a chance de uma correção contribuída para isso ser aceita é muito baixa.

Então, em vez disso, estamos de volta a uma dúzia de pessoas.

Alguém pode confirmar se isso agora é possível com Ivy? Se sim, alguém tem um exemplo?

Como nenhuma palavra ainda, pensei em fornecer a coisa mais próxima que consegui encontrar, que é um artigo de um tempo atrás sobre a implementação de mixins com Ivy: https://blog.nrwl.io/metaprogramming-higher-order-components -and-mixins-with-angular-ivy-75748fcbc310

De acordo com o artigo, acho que uma possível solução para o problema original deste tópico é usar o novo recurso chamado ... "recursos".

... Você pode imaginar que é um pesadelo tentar pesquisar no Google qualquer coisa sobre esse recurso. Esperando que eles liberem alguma documentação oficial da Ivy em breve! :)

@nayfin também constrói designer / construtor de formas visuais
E depois de alguns meses de trabalho para ficar preso no fato, não tenho como implantar a diretiva para adicionar dinamicamente div me deixa louco .... Deveria ser tão indiferente chamar algum MyDirectiveFactory :: apply (HTMLElement)

Esse recurso seria extremamente bem-vindo, pois sempre estou fazendo um único div para anexar diretivas de nível superior. Além disso, se eu quiser quaisquer diretivas de layout flexível, tenho que fazer aquele div único também e seria bom se eu pudesse anexá-los diretamente ao elemento host em vez de ter que fazer isso.

Seria muito legal poder adicionar diretivas dinamicamente, como:

const msked = componentFactory.createDirective(MaskedInputDirective);
msked.textMaskConfig = {};
this.elementRef.directives.add(msked);

Além disso, se eu quiser quaisquer diretivas de layout flexível, tenho que fazer aquele div único também e seria bom se eu pudesse anexá-los diretamente ao elemento host em vez de ter que fazer isso.

@tsteuwer Você sempre pode usar o seletor: host em seu scss para aplicar propriedades de estilo ao elemento host.

Mas sim, eu também gostaria de poder aplicar diretivas ao elemento host. seria útil para tornar o elemento host rolável e aplicar CdkScrollable do CDK de material angular.

Envolva tudo no template de componentes dentro de um template ng de nível superior

Uma alternativa um pouco mais inteligente é usar https://github.com/trotyl/angular-contrib e adicionar

host: { ngNoHost: '' }

Este projeto corrige o renderizador e renderiza os filhos dos elementos com o atributo ngNoHost, sans parent.

É claro que tem muitas das mesmas desvantagens.

Pena que ainda está aberto depois de 3 anos. As diretivas vinculadas ao elemento host realmente melhorariam a capacidade de reutilização do código.

Além disso, se eu quiser quaisquer diretivas de layout flexível, tenho que fazer aquele div único também e seria bom se eu pudesse anexá-los diretamente ao elemento host em vez de ter que fazer isso.

@tsteuwer Você sempre pode usar o seletor: host em seu scss para aplicar propriedades de estilo ao elemento host.

Mas sim, eu também gostaria de poder aplicar diretivas ao elemento host. seria útil para tornar o elemento host rolável e aplicar CdkScrollable do CDK de material angular.

Não é o ideal, mas você pode criar o CdkScrollable programaticamente desta forma:
this.scrollable = novo CdkScrollable (this.elementRef, this.scrollDispatcher, this.zone);
this.scrollable.ngOnInit ();

Você também deve destruí-lo manualmente:
if (this.scrollable) {
this.scrollable.ngOnDestroy ();
}

https://github.com/angular/angular/issues/8785#issuecomment -361004682 IgorMinar o trabalho de Ivy torna isso mais viável. Mas sim, após a v6.

@mhevery Dando seguimento a seu comentário: point_up_2 :, agora que Ivy foi finalmente totalmente aterrado, podemos muito por favor têm esse recurso em (ou antes) a libertação de v10? Se não, por favor, atualize-nos sobre quais outras considerações podem impedir isso ainda mais.

Alguma mudança?

Se esse recurso específico estivesse na pesquisa Angular https://twitter.com/angular/status/1252646001162088448?s=20 , aposto que seria a entrada mais votada.

Existem vários recursos que seriam os mais votados, este com certeza, mas também usando Observáveis ​​para @output e muitos outros. Infelizmente, no ritmo atual, eles nunca serão implementados.

Se esse recurso específico estivesse na pesquisa Angular, aposto que seria a entrada mais votada.

Ótima ideia @princemaple!

Embora não seja o ideal, isso pode ser abordado na seção de "comentários extras" da pesquisa (da pergunta 2)
Onde diz:

"How else should we improve Angular for you?"

Então, basicamente, todos os interessados ​​em ver esse recurso, basta responder à pesquisa e torná-lo explicitamente conhecido, que você se preocupa muito em ver o "Problema # 8785" implementado e resolvido.

Aqui está o link direto para a pesquisa:
https://goo.gle/angular-survey-2020

Que a força esteja com você! 🙂

Eu também tenho lutado para saber como adicionar programaticamente mais funcionalidade aos componentes e, honestamente, acho que algumas das propostas aqui parecem as MELHORES maneiras de abordar esses problemas específicos.

Falei com membros da equipe angular sobre esse artigo

Alguém pode confirmar se isso agora é possível com Ivy? Se sim, alguém tem um exemplo?

Como nenhuma palavra ainda, pensei em fornecer a coisa mais próxima que consegui encontrar, que é um artigo de um tempo atrás sobre a implementação de mixins com Ivy: https://blog.nrwl.io/metaprogramming-higher-order-components -and-mixins-with-angular-ivy-75748fcbc310

E basicamente ficou com a impressão de que se tratava de um hacking com os componentes internos do angular e, na verdade, não foi projetado para o consumo típico do usuário.

Não tenho certeza se existe algum motivo técnico que nos impeça de fazer isso, mas acho que se tivéssemos os recursos para fazer isso, iria melhorar drasticamente meu dia a dia com angular.

“Aumentamos drasticamente nosso investimento em trabalhar com a comunidade. Nas últimas três semanas, nossa contagem de problemas abertos diminuiu em mais de 700 problemas em estrutura, ferramentas e componentes. Abordamos mais de 2.000 problemas e planejamos fazer grandes investimentos nos próximos meses, trabalhando com a comunidade para fazer ainda mais. ” - @StephenFluin
do anúncio do

Acho que isso significa que veremos esse problema eliminado na v11. 🤞😏

Qual a melhor maneira de "trabalhar com a comunidade" (e acalmá-la) do que trabalhar para adicionar um de seus recursos mais solicitados !? (este 😉)

Escute-os!

Apenas para definir as expectativas, o que você está pedindo não é uma quantidade trivial de trabalho e as estruturas de dados atuais não foram realmente projetadas para isso. Portanto, para dar suporte a algo assim, seria necessária uma grande engenharia.

@mhevery como é diferente de aplicá-los do pai no modelo?

@ k3nsei É necessário vê-lo do ponto de vista de NgModule , que na verdade é o elemento chave que cria a infraestrutura para todos os seus componentes.

@ mlc-mlapis Temos @HostBinding e @HostListener, então talvez @HostDirective seja uma boa escolha para essa funcionalidade. Eu vi palestras de que Ivy apis permite essas funcionalidades.

Para mim, o ponto-chave é ter alguma API de composição que nos permitiria ter mais classes desacopladas com capacidade de ter extensões / características com junks reutilizáveis ​​de funcionalidade. Por exemplo, como selecionável, expansível / recolhível.

@ k3nsei Pode ser, mas não tenho certeza se não é muito dinâmico e tem menos desempenho do que estruturas estritamente estáticas.

"Apenas para definir as expectativas, o que você está pedindo não é uma quantidade trivial de trabalho e as estruturas de dados atuais não foram realmente projetadas para isso. Portanto, dar suporte a algo como isso exigiria alguma engenharia importante." - https://github.com/angular/angular/issues/8785#issuecomment -654391378

Obrigado por sua resposta oportuna @mhevery.

Acho que falarei pela comunidade ao dizer que não estamos perdendo de forma alguma que este será um grande desafio. Se não fosse, tenho certeza que agora teríamos feito algumas bibliotecas de terceiros que conseguem isso corretamente (de alguma forma). [que eu saiba não existe].

Além disso, nem é preciso dizer, mas, por favor, deixe-nos saber se há algum fruto mais baixo (ou outro) que possamos ajudar a contribuir para isso.

Agradecemos sinceramente e valorizamos sua comunicação sincera e esperamos continuar a fazer parte do diálogo sobre o que precisamos versus o que é realista / pragmático para adicionar.

Embora, segundo meu entendimento, Ivy torne isso mais fácil do que antes.

@mhevery

O trabalho da Ivy

Embora meu entendimento seja que Ivy torna isso mais fácil do que antes

Meu novo entendimento é "mais fácil" ainda não significa "fácil"

Meu novo entendimento é "mais fácil" ainda não significa "fácil"

Ivy: Aquilo em que você passou dois anos e ainda não aborda nenhuma das questões angulares mais populares.

Ivy: Aquilo em que você passou dois anos e ainda não aborda nenhuma das questões angulares mais populares.

@pauldraper Acho que nossos problemas não são os "problemas" deles, visto que este em particular nem está em seu radar (consulte o Roadmap https://angular.io/guide/roadmap).

Para mim, pessoalmente, acho que é hora de procurar em outro lugar por um projeto que não seja apenas de código aberto, mas um projeto cuja direção a comunidade (e os usuários) tenham uma influência real .

@pauldraper Acho que nossos problemas não são os "problemas" deles, visto que este em particular nem está em seu radar (consulte o Roadmap https://angular.io/guide/roadmap).

@somombo Estou desapontado com o fato de que esse problema ainda está aberto depois de todos esses anos, mas não posso concordar com você O primeiro ponto no roteiro é explicitamente sobre como lidar com questões abertas do github. Listar todos eles no roteiro não faria muito sentido, não é? Este problema é um dos mais votados (ou o mais votado) e espero que finalmente seja resolvido.

O primeiro ponto no roteiro é explicitamente sobre como lidar com problemas abertos do github. Listar todos eles no roteiro não faria muito sentido, não é? Este problema é um dos mais votados (ou o mais votado) e espero que finalmente seja resolvido.

não, isso é apenas uma ilusão, leia através de https://github.com/angular/angular/issues/5689 não há absolutamente nenhuma indicação de que eles querem abordar qualquer uma das questões mais votadas, além de "formulários fortemente digitados" no futuro

@pauldraper Acho que nossos problemas não são os "problemas" deles, visto que este em particular nem está em seu radar (consulte o Roadmap https://angular.io/guide/roadmap).

@somombo Estou desapontado com o fato de que esse problema ainda está aberto depois de todos esses anos, mas não posso concordar com você O primeiro ponto no roteiro é explicitamente sobre como lidar com questões abertas do github. Listar todos eles no roteiro não faria muito sentido, não é? Este problema é um dos mais votados (ou o mais votado) e espero que finalmente seja resolvido.

Ao mesmo tempo .. Parei de esperar .. este problema tem sido literalmente um grande bloqueador para mim. Portanto, o fato de que nem parece que está sendo planejado para um futuro próximo significa que é hora de eu seguir em frente pessoalmente. Boa sorte para todos os outros.

Eu gostaria de ver renomeado para "Suporte para adicionar diretivas a diretivas". Embora esse nome possa ser confuso, acho importante que esse recurso funcione em diretivas e não se limite a componentes. Outros nomes para o recurso podem ser "diretivas implícitas" ou "diretivas anexadas", o que significa que quando você usa um determinado componente ou diretiva em um modelo, ele puxa as diretivas implícitas / anexadas no elemento host.

Eu quis isso muitas vezes, principalmente porque a composição é potencialmente uma forma mais limpa de reutilização no Angular, em comparação com a herança. A herança pode ser desajeitada porque não há herança múltipla, os parâmetros do construtor precisam ser transmitidos e alguns recursos do Angular funcionam quando herdados, e outros precisam ser "reconectados" em cada classe folha.

Eu imagino "diretivas implícitas / anexadas" funcionando como uma forma local de componente ou local de diretiva de instanciação de diretiva, o que resulta na diretiva sendo instanciada no elemento host sem exigir que o seletor de diretiva exista na marcação do modelo.

Aqui está um exemplo:

@Directive({
  selector: 'app-popup',
  attachDirectives: [
    FocusAreaDirective
  ]
})
export class PopupDirective {

  // Attached directives can be injected, just like template-declared directives today
  constructor(public focusArea: FocusAreaDirective) {
  }

}

As propriedades @Input() e @Output() na diretiva anexada podem ser usadas no modelo e os métodos de ciclo de vida na diretiva anexada devem ser chamados como parte do ciclo de vida do componente host. A diretiva anexada também pode ser vinculada ao elemento host. Basicamente, ela atua exatamente como uma diretiva declarada por modelo hoje, mas não precisa ser declarada no modelo.

Eu acho que esse recurso forneceria um benefício significativo para o Angular, permitindo arquiteturas de componentes mais limpas / simples por meio da composição de diretivas.

Hoje, você tem 2 opções se quiser uma forma semelhante de reutilização de diretiva:

  • Exigir que um conjunto de diretivas relacionadas sempre seja declarado junto na marcação do modelo; e detectar e lançar erros se as diretivas necessárias não estiverem presentes no construtor. Não há como exigir que as diretivas necessárias sejam declaradas no mesmo elemento. Isso é confuso do ponto de vista de criação de modelo e documentação, e não é uma API ou contrato forte devido à redundância, mas por outro lado não é horrível.
  • Instancie manualmente as diretivas anexadas na diretiva do host e encaminhe os parâmetros do construtor, as propriedades @ Input / @Output , as ligações do host e os métodos de ciclo de vida para as diretivas internas. Isso é uma bagunça frágil para os autores de componentes, mas é possível com um conjunto simples de componentes relacionados. É muito mais agradável para a autoria de modelos.

Em outras palavras, a ausência do recurso às vezes cria uma compensação desnecessária entre o uso de componente limpo + simples e autoria de componente limpo + simples.

@johncrim
Observe que, em um caso do mundo real, sua diretiva personalizada teria entradas e você gostaria de transformá-las e passá-las como entradas para a diretiva anexada. Talvez isso pudesse ser feito com uma sintaxe semelhante aos atributos host nas opções do decorador de diretiva.

@amakhrov : Bom argumento - excluí as entradas do meu exemplo para maior clareza. Na maioria dos casos em que preciso disso, não preciso transformar as entradas (ou saídas) para as diretivas anexadas - elas (idealmente) atuam como unidades combináveis ​​e seus valores de entrada (ou saída) podem ser vinculados a partir do modelo usando a diretiva pai (ou componente).

Nos casos em que há conflitos de nomenclatura ou problemas de clareza de nomenclatura (o que eu tentaria evitar ao projetar diretivas para composição), ou quando as entradas ou saídas precisam ser transformadas, isso poderia ser resolvido facilmente injetando a diretiva anexada no pai e criar novas propriedades de entrada ou saída no pai que delegam às diretivas anexadas.

Eu estou corrigido.
Este problema agora está listado na seção "Futuro" do roteiro oficial. Consulte https://angular.io/guide/roadmap#support -adding-directives-to-host-elements

Suporte a adição de diretivas a elementos de host

Uma solicitação de recurso de longa data é adicionar a capacidade de adicionar diretivas aos elementos do host. O recurso permitirá que os desenvolvedores aumentem seus próprios componentes com comportamentos adicionais sem usar herança. O projeto exigirá um esforço substancial em termos de definição de APIs, semântica e implementação.

Como acabei de notar, não tenho certeza de quando foi adicionado, mas devo admitir que esta é uma ótima notícia e um gesto significativo e tranquilizador. Vou continuar cruzando os dedos.

Obrigado à equipe por colocá-lo aí! 🙏🏾

Esta página foi útil?
0 / 5 - 0 avaliações