Angular: Propuesta: se necesita la capacidad de agregar directivas a los elementos del host en la declaración del componente.

Creado en 23 may. 2016  ·  114Comentarios  ·  Fuente: angular/angular

He estado investigando Angular 2 y me he encontrado con un obstáculo potencial para extender ciertos tipos de componentes.

En el siguiente ejemplo, tengo un componente de botón y una directiva que aplicará estilos basados ​​en eventos táctiles. Habrá muchos otros objetos además del botón que heredarán exactamente el mismo comportamiento táctil. He explorado mis opciones y estoy perdido:

  • Extienda directamente una TouchClass. Esto parece menos que ideal, ya que el mecanografiado no admite la herencia de clases múltiples, y también me gustaría exponer el comportamiento a los consumidores para que lo utilicen en sus propias clases.
  • Falsa herencia de clases múltiples a través de una interfaz. Esto parece un truco y requiere que vuelva a declarar una API de shim en cada clase en la que estoy tratando de mezclarme. https://www.stevefenton.co.uk/2014/02/TypeScript-Mixins-Part-One/
  • Cree una función auxiliar que lo haga a través de un servicio directamente en elementRef.nativeElement en el constructor del componente. Realmente no quiero hacer esto ya que establece en los documentos que nativeElement será nulo cuando se ejecute en un trabajador, y esa capacidad es algo que me entusiasma más.

Sin profundizar demasiado, supongo que el componentMetadata está disponible durante el tiempo de compilación de los componentes, y que la propiedad del host podría escanearse en busca de directivas adicionales que podrían agregarse dinámicamente y compilarse al mismo tiempo. Esto le permitiría hacer mezclas de forma angular: usar directivas componibles para extender la funcionalidad y hacerlo sin romper la proyección de la vista. Breve ejemplo a continuación.

Comportamiento actual
Declarar una directiva en componentMetadata.host la trata como un atributo regular

Comportamiento esperado / deseado
La directiva declarada en el host se procesará en tiempo de compilación.

/**
 * 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() {

    }
}

Algunas ideas de cómo podría funcionar esto:

// 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
        }
    }
});

Gracias a todos, ¡Angular 2 se ve genial !. Avísame si me estoy perdiendo algo.

core directive matching host and host bindings feature

Comentario más útil

@IgorMinar el trabajo de Ivy hace que esto sea más factible. Pero sí, pasado v6.

Todos 114 comentarios

Actualmente estoy desarrollando un cliente grande y, por lo tanto, estoy tratando de desglosar todos los problemas relacionados con la GUI en directivas Angular2 reutilizables. Esto siempre me lleva al mismo problema, como James señaló perfectamente.
Esto realmente tiene que funcionar de alguna manera en aras de una buena arquitectura modular y dinámica. El ejemplo táctil es solo uno de los muchos escenarios en los que es necesario. por ejemplo, arrastrar y soltar, cambiar el tamaño de la observación, etc., etc.
Hice otro ejemplo como plunker:
https://plnkr.co/edit/J65THEMic0yhObt1LkCu?p=info

¿Existe alguna posibilidad de que esta funcionalidad se agregue pronto?

Aquí hay una pregunta de StackOverflow relacionada con esto: http://stackoverflow.com/questions/37148080/use-angular2-directive-in-host-of-another-directive

@ Andy1605 , ¿alguna vez encontraste una forma de evitar esto? En cierto modo, dejé de trabajar con NG2 debido a esto durante los RC. Me encantaría recuperarlo, pero este problema en particular me impide crear patrones de interfaz de usuario extensibles.

También siento que a Angular le falta una característica esencial aquí. Debería ser posible que un componente declare (múltiples) directivas de atributos para su host. No poder hacer eso también es un obstáculo importante para mi proyecto.
¿Alguien sabe si esto se implementará en el futuro o si hay razones por las que no se puede hacer?

He propuesto una solución a este problema (aunque para la versión angular 1) aquí: https://github.com/angular/angular.js/issues/15270.

Mi idea es que, en lugar de solo tener la capacidad de agregar directivas, el marco de compilación tendría un nuevo punto de extensibilidad llamado "hostTransforms" (en el caso de angular 1, "nodeTransforms") que tendría acceso a la declaración del componente sin modificar ni filtrar y el nodo host del componente original sin compilar cada vez que el compilador encuentra por primera vez un componente y lo prepara para su inserción en el DOM. De esta manera, un desarrollador podría extender los decoradores de componentes con propiedades personalizadas y luego usar nodeTransforms para convertir esas propiedades personalizadas en algo con lo que el marco angular esté familiarizado, justo antes de la compilación. Consulte el hilo de solicitud de funciones para ver ejemplos.

Estoy más familiarizado con el código fuente angular que con el código fuente angular 2, por lo que no estoy seguro de si el proceso de implementación sería el mismo aquí. Pero dado que esta parece ser una solicitud bastante popular, me encantaría verla implementada en angular 2 y backported, o implementada en angularjs y forwardported (¿es eso una cosa?).

+1

Debo estar de acuerdo, una característica que nos permite agregar directivas de atributos contribuyentes al host sería genial. Sé que podría usar esa característica ahora mismo implementando una forma más "angular" agregando funcionalidad de arrastrar / soltar a mis componentes de interfaz de usuario.

¿Qué hay de crear una nueva etiqueta similar a <ng-container> que le permita aplicarlos en la plantilla del componente en lugar de la propiedad de metadatos host ? Algo como <ng-host [attributeDirective]> para indicar que las directivas se agregan al componente de host.

@jjstreet su propuesta suena similar a replace: true (obviamente no idéntica, pero similar), que fue obsoleta hace un tiempo. Pero quizás replace: true quedó en desuso por una razón que no se aplicaría aquí.

@ pkozlowski-opensource ¿Podríamos obtener una respuesta de algún tipo del equipo de ng2 sobre esto?

Estoy listo para cualquier forma de lograr esto. Sugerí la propiedad del host porque tiene acceso al alcance local del componente y ya agrega atributos al componente en sí. Las directivas parecen una extensión natural de este comportamiento.

+1 esta función es necesaria para tener un código limpio y reutilizable en los componentes de la interfaz de usuario

+1

¿Podríamos obtener algún tipo de respuesta del equipo de ng2 sobre esto? Incluso si es solo para decir que no lo va a hacer, o para decir que es una buena idea pero no una prioridad actual, me gustaría escuchar algún tipo de información.

Me gustaría agregar otro caso de uso para esto. Permitiría que ng2-mobx (https://github.com/500tech/ng2-mobx) se deshaga del componente de envoltura y se vea mucho más limpio.

Me encantaría tener esto también. Actualmente lo necesito para hacer una directiva routerLink . Me encantaría reutilizar uno angular y simplemente proporcionarle los parámetros preparados por la directiva mi.

Entonces, en lugar de <a [routerLink]="repeatedCodeToGetLink()"> , tendría <a [myRouterLink]> y aplicaría dinámicamente [routerLink] con parámetros resueltos.

¡Realmente emocionado por las perspectivas de esto!

Hemos necesitado esto por un tiempo. De hecho, hace un tiempo antes de saber que había un problema abierto, pregunté en el desbordamiento de la pila sobre esencialmente esta característica.

He proporcionado un ejemplo elaborado de cómo podríamos usar esta función para resolver https://github.com/angular/flex-layout/issues/162 que hemos tenido abierto durante un tiempo. ( Vea el ejemplo y la explicación aquí )

Realmente estamos ansiosos por recibir comentarios, veo que este problema es el tercero más aprobado de todos los problemas abiertos en este repositorio. ¡Ojalá podamos ver esto en el próximo lanzamiento o antes (dedos cruzados)!

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

@jjstreet creo que tu sugerencia

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

... iría bien con otra propuesta separada que se hizo hace un tiempo por razones distintas a las que estamos discutiendo aquí.

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

Actualmente soluciono esto agregando una directiva en el componente principal y luego agrego un oyente en el host con @HostListener.

Parent.html
<my-component myDirective>

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

Pero sería más limpio si pudiéramos agregar atributos directamente en el host ...

Así es como he estado lidiando con esto, pero realmente creo que implementar esta función desde cero sería la mejor solución.

Solo un recordatorio mensual de que estamos esperando comentarios positivos o negativos sobre esto del equipo de Angular.

@tbosch : cualquier pensamiento público sobre la prioridad de este problema. También afecta a @angular/flex-layout .

@fadzic ¿no puede simplemente agregar la directiva al elemento host haciendo esto ...

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

o así si necesita parámetros como ElementRef o Renderer2
@HostBinding('attr.myHilite') myHilite = new myHiliteDirective(this.elementRef, this.renderer)

También tengo la necesidad de agregar una directiva al elemento de host y me redirigieron a este problema. Pude hacer lo que necesitaba usando el código anterior. De ninguna manera soy un experto en el uso de angular, pero esta solución parece funcionar hasta ahora, si alguien tiene problemas con este enfoque, hágamelo saber. Gracias.

@btinoco que no funciona porque no se llama a ningún método de ciclo de vida. Tendría que conectar manualmente todo en cada componente que usa la directiva en lugar de simplemente hacer que Angular lo conecte por usted.

@hccampos gracias por el ngOnInit de mi directiva no se ejecutó (a menos que use la directiva en mi componente manualmente) o llamo a la directiva ngOnInit() en el ngOnInit() mi componente . Nuevamente gracias por hacérmelo saber.

@btinoco - sí. es un tema sutil pero desagradable. Uno que @ angular / flex-layout espera que se solucione pronto. ;-)

¿Alguna noticia del equipo de Angular sobre esto? Ha pasado 1 año desde que se abrió el problema ...

Encontrar esta descripción detallada sobre este problema fue genial,
entonces, no encontrar comentarios del equipo de Angular fue súper desagradable :(

Respecto a las soluciones que ya funcionan:

Esta solicitud de función se parece mucho a los mixins. De hecho, la viñeta no 2 en la descripción
de esta característica realmente coincide con el oficial
documentación de TypeScript, consulte aquí .
Sin embargo, en angular, esto se vuelve un poco peor, ya que al mezclar una clase con @Input() s,
tengo que volver a declararlos en la clase base.

Otra solución que ya funciona hoy sería hacer que el componente contenga un elemento contenedor y aplicar las directivas allí.
Por ejemplo, si el componente contiene una plantilla como <wrapper g-touch>...

Respecto a "Crear una función auxiliar que lo haga a través de un servicio directamente en elementRef.nativeElement":
Sí, eso también parece una buena idea. No me importaría WebWorkers por ahora,
ya que todavía son experimentales y les faltan algunas funciones más importantes para la producción,
y casi ninguna biblioteca funcionaría en WebWorkers.
Consulte, por ejemplo, también nuestra biblioteca de materiales que accede directamente al DOM.

Con respecto a la opción 1) de la propuesta:

La semántica actual para los enlaces de propiedad de host es,
que establecieron una propiedad myDir en el elemento HTML subyacente,
pero no ninguna directiva. Sin embargo, si host también puede introducir directivas, los usuarios pueden escribir lo siguiente
y estaría confundido por qué esto no actualiza la propiedad en la directiva myDir :

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

Con respecto a la Opción 1) y la Opción 3):
La introducción de algún tipo de enlaces host entre directivas en el mismo elemento puede:

  • conducir a un ciclo en el gráfico de enlace de datos, que Angular no admite, y por lo tanto
    conducen a errores difíciles de depurar debido a valores obsoletos / errores de "La expresión ha cambiado después de la comprobación".
  • conducen a una sobrecarga de rendimiento adicional, en comparación con las directivas que se inyectan entre sí
    y comunicarse directamente.

Con respecto a la opción 2) de la propuesta:

  • sí, tener que hacer referencia a la clase gTouch parece extraño, como todas las demás directivas
    se activan a través de NgModule s.

@ThomasBurleson hablemos sin conexión sobre su caso de uso con más detalles ...

@tbosch Me gustaría proponer otra opción: introducir una etiqueta angular nativa, llamémosla <ng-host> .

Nota: @mhevery propuso la introducción de una etiqueta <ng-host> en https://github.com/angular/angular/issues/7546 , sin embargo, aunque estoy usando el mismo nombre de etiqueta aquí, lo que soy La propuesta es separada y está destinada específicamente a abordar el problema que se ha planteado aquí.

La etiqueta ng-host no se implementaría como una directiva / clase de componente regular, sino que sería un código marco "mágico" ... similar a ng-content , ng-container , etc. .,
La etiqueta simplemente serviría como un "puntero" al host del componente de una manera análoga al selector css

Evita escenarios ambiguos, a cada componente solo se le permitiría tener, como máximo, un bloque <ng-host> y tendría que ser la etiqueta raíz / nodo de la plantilla de ese componente.

Así es como uno lo usaría:

// 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>
  `
})

Por cierto @tbosch , Gracias por responder. Realmente apreciamos su participación y comentarios sobre este tema.

¿Los pensamientos de todos los demás sobre esta funcionalidad son específicos de los componentes, o también tendría sentido si una directiva pudiera aplicar una directiva diferente a su host? El caso de uso por el que comencé a suscribirme a este problema involucraba algunas directivas de terceros que A) queríamos aislar de nuestro código en caso de que quisiéramos cambiar más tarde y B) queríamos aplicar alguna funcionalidad predeterminada a cada instancia sin tener que duplicar la configuración cada vez que la usamos.

Por ejemplo, una directiva de información sobre herramientas, que se aplicará en una gran cantidad de elementos a lo largo de nuestra aplicación, y queremos predeterminar el retraso y appendToBody (no admite un objeto de configuración centralizado). Debido a que no admitía un objeto de configuración central, tuvimos que poner tres o cuatro atributos en todos los lugares donde quisiéramos usarlo. Y luego, terminamos alejándonos de esa biblioteca (comenzamos a usar información sobre herramientas de Material) y tuvimos que reemplazar manualmente cada información sobre herramientas. Si hubiéramos podido crear nuestra propia directiva que la "envolviera", habría sido tan simple como cambiar esa directiva para aplicar [mdTooltip] a su host en lugar de a la otra biblioteca.

@MikeMatusz Parece que también tenía eso en mente, aquí está mi fragmento 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 {}

¿Sería posible crear un decorador de propiedades que instancia una directiva?
Por ejemplo:
@HostDirective(LayoutDirective) myLayoutDirective: LayoutDirective;

Esto funcionaría tanto para Componentes como para Directivas, proporcionaría una instancia de referencia para la interacción y no se perdería al heredar del Componente / Directiva.
Supongo que se vuelve más complicado si también desea proporcionar enlaces de entrada y eventos.

¿Dónde está esto? Soy bastante nuevo en Angular2 / 4, y lo que quiero hacer es crear una directiva que solo aplique varias otras directivas a la vez. De modo que en lugar de:

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

Solo puedo escribir:

<button customDirectiveABC>BUTTON TEXT</button>

Parece que esto debería ser fácil: composición básica / sequedad. Pero, por lo que puedo decir, ¿no es posible?

@soynog , me siento exactamente igual. También me gustaría saber dónde se encuentra esto.

Esperaba poder hacer cuadros de diálogo que se puedan arrastrar usando Angular Material y angular2-draggable (ya que angular / material # 1206 aún no es compatible) donde me gustaría agregar dinámicamente una directiva a md-dialog-container que MdDialog service crea, pero parece mucho más difícil obtener el comportamiento del compilador Angular 1.x aquí para las directivas dinámicas.

@tbosch , @ThomasBurleson , ¿el caso de uso fuera de línea que discutió estuvo relacionado con los problemas u objetivos que Thomas planteó en angular / material # 1206 por casualidad? Solo estoy tratando de entender los cambios de comportamiento entre los marcos 1.6.xy 2+.

¿Hay alguna actualización sobre este tema? Ganó tracción al principio, pero creo que ya no recibe atención.

¿Alguna actualización sobre eso?

Esto es algo que necesito con tanta urgencia, espero que esta propuesta salga adelante.

Esto sería bueno, me di cuenta hoy de que no puedo aplicar directivas programática / dinámicamente, me entristeció.

+1
Lo mismo para mi :)
Estoy buscando una forma de encajar varias directivas vinculadas en una directiva personalizada que haga todo lo que necesito. Por ejemplo :

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

Sería bueno si pudiera crear una directiva que envuelva todas esas cosas para poder reutilizarlas sin copiar / pegar todas las líneas.

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

Y en mi directiva personalizada

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

próximamente en el segundo aniversario de este número! Honestamente, esta característica sería tan, tan útil, que nos permite crear componentes y directivas altamente componibles sin tener que crear un millón de envoltorios. simplemente componga el componente que necesita a partir de las directivas que tiene. simple, limpio, eficaz.

@IgorMinar - ¿De todos modos podemos poner esta función en el radar para próximas mejoras?

Me gustaría saber si tal característica se consideraría un mal patrón o no. ¿Alguien?

@darkbasic - AFAIU, sin esta característica, un desarrollador necesitaría introducir un elemento contenedor (en ng-container ) simplemente para agregar directivas principales a la vista y el contenido de la plantilla.

No, no creo que poder tener el control total de su propio componente sin tener que usar envoltorios sea un mal patrón. Es una necesidad

@bradlygreen algún comentario?

Esta función es la solicitud más popular (si no la 5) entre todos los problemas abiertos de este repositorio ... En Internet, estamos viendo informes (respaldados por datos empíricos) del declive de Angular como el marco de facto ... creo Una de las cosas que impulsa es el sentimiento de que la comunidad no está siendo escuchada. La competencia; vue.js y react, están ganando terreno y han superado a angular porque, aunque no necesariamente implementan todo lo que todos quieren, al menos brindan comentarios continuos sobre los elementos solicitados más populares. Es muy frustrante esperar tanto y no escuchar nada ... ni siquiera un simple "no, no lo haremos".

(Consulte la sección de marcos Js "Deslizamientos angulares" )

... aunque creo que algunas opiniones sobre Angular / Vue / React / ... están influenciadas por diferentes factores ... esta característica concreta realmente sería digna de alguna forma de implementación (incluso las circunstancias son un poco más complicadas que solo una solución con una lista simple de directivas aplicadas) ... por lo que la posición concreta del equipo central de Angular sería muy bienvenida ... 🥇

No hay una ETA específica, pero estamos trabajando para hacer que esta categoría de cosas sea mucho más fácil en el renderizador en 2018.

Ojalá las cosas mejoren drásticamente en 2018. Estamos perdiendo

Ver:

@somombo se confirmó que este artículo era una mierda hace mucho tiempo

Las personas que realmente conocen sus cosas se burlaron del autor y ninguno de ellos lo tomó en serio, los me gusta son de react, vue fanboys, naturalmente.

Entonces, el hecho es que este problema aquí es una prioridad muy baja para el equipo angular, de hecho, es la prioridad más baja posible.

Consulte la lista de prioridades publicada en AngularHQ (busque el número de problema 8785)

Este es el caso a pesar de que este tema ha generado mucha discusión e interés en la comunidad, como lo demuestra la cantidad de comentarios.

Si usted es alguien que se preocupa por este problema y realmente le gustaría verlo implementado, entonces en lugar de esperar ... bueno, honestamente _potencialmente nunca_, tal vez pueda completar la Encuesta Angular Anual Oficial y hacer saber que se siente como este problema debería ser una prioridad más alta y agradecería que se cumpla más temprano que tarde.

¡No olvide agradecer a nuestro equipo de Angular por todo el gran trabajo que ha realizado!

También me gustaría votar por esta función. Esta ha sido la causa de demasiado dolor al tratar de solucionar este problema.

@somombo, por favor, no lea demasiado sobre la prioridad en AngularHQ todavía. la fórmula de prioridad no está completamente desarrollada. Habiendo dicho eso, creo que deberíamos revisar esta solicitud de función después de que se envíe la v6. Me temo que no tenemos el ancho de banda para ello antes y trabajar en esto entraría en conflicto con un trabajo ya en curso en el área del compilador / núcleo.

Esta no es una solicitud de solución rápida. Sospecho que se necesitará un esfuerzo considerable para hacerlo correctamente, pero las cosas en las que estamos trabajando para la v6 deberían hacer que esta sea mucho más fácil de implementar.

@IgorMinar el trabajo de Ivy hace que esto sea más factible. Pero sí, pasado v6.

@IgorMinar y @mhevery No puedo enfatizar lo suficiente lo agradecido que estoy (y el resto de nosotros también, estoy seguro) de que nos hayas dado esta retroalimentación concreta sobre cuáles son tus pensamientos y lo que debe suceder primero antes de que este problema pueda. ser abordado correctamente.

Los profanos no siempre tienen claro qué es una "solución rápida" y qué no. Sin embargo, salvo el hecho de que este no es un tipo de solución rápida y tiene que hacerse bien, estoy especialmente agradecido de que parezca que también siente que esta será una característica útil para angular.

Sabemos que ambos están muy ocupados y no es posible responder así a todos los problemas.
Así que tienes nuestro más sincero agradecimiento siempre que lo hagas. ¡Estamos emocionados y deseando que llegue angular v6 y más!

¡Gracias por todo el gran trabajo!

Puede hacer que su clase de componente extienda o implemente la clase directiva. Si está intentando aplicar la directiva bajo el capó, probablemente debería ser lógica en el componente.

export class gBtn extends gTouch

@NateMay , eso solo te permite extender una sola clase. Este problema trata más sobre la composición de varias funciones mediante directivas.

@NateMay hay dos problemas con eso: primero, solo puede extender una sola clase, y segundo, acaba de romper la inyección de dependencia.

Solo agrego mis dos centavos. Estoy construyendo un SPA multicapa con diseño angular, material y flexible, utilizando estados anidados de @ uirouter / angular. Entonces, la incapacidad de aplicar fácilmente directivas flexibles a los elementos de los componentes es muy limitante.

Entonces, vote por esta función, por favor.

+1 para esta función agregada

Es posible agregar una directiva a un ng-container , que no aparecerá en el DOM.

Necesitaba esto para la API de observador de intersecciones (que genera eventos cuando los elementos entran / salen de la ventana gráfica). Tengo una directiva intersector , que tiene eventos enter() y leave() cuando el elemento se vuelve visible / oculto. Tengo ciertos componentes que necesitan usar esta API internamente, pero no quería agregar un DIV adicional en la plantilla.

Entonces, lo que hice fue lo siguiente en component.html :

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

Luego, el constructor de la directiva intersector.directive.ts inyecta el ElementRef .

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

Para un elemento DOM normal, solo operaría en intersectorElementRef.nativeElement , pero para un ng-container el nodo es en realidad un nodo de comentario. Así que solo verifico si es un comentario, y si lo es, subo un nivel.

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 ...);

Esto no va a funcionar en todas las situaciones, pero estoy de acuerdo con esto por ahora. Creo que en el compilador IVY es posible que ya no utilicen comentarios, por lo que esto puede fallar. Lo importante es que tengo una sola directiva que puedo usar en los nodos DOM o en lo que es efectivamente un ' @HostBinding ' falso para la directiva.

Realmente esperaba que fuera posible agregar directivas dinámicamente. Quiero poder encapsular directivas de nivel inferior en directivas de "orden superior", más abstractas. Hice la siguiente pregunta sobre el desbordamiento de pila y me preguntaba si habrá una solución para esto en el futuro: https://stackoverflow.com/questions/51608645/abstract-away-leaflet-directive-in-custom-directive

como dijo @mhevery ... tenemos que ser pacientes y esperar a que llegue la versión completa de ivy (ng v7.0.0?). Al parecer, les será mucho más fácil implementar con Ivy ... Después de Ivy, debemos recordarle al equipo lo importante que es esta característica para nosotros, para que no se olviden de ella 😉

Suscribiéndose a esto. También necesito poder adjuntar dinámicamente una directiva a un componente que creé con 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)

¿¿¿Cualquier actualización???
Me encontré con otro caso de uso en el que estoy usando una directiva de terceros.
En algún escenario, necesito eliminar / agregar una directiva en un elemento HTML de forma dinámica.
¿Es esto posible de alguna manera o aún está pendiente de solución?

@micronyks ... en realidad no es posible agregar una directiva dinámicamente. Tenemos que esperar a que Ivy agregue la posibilidad de crear dicha característica.

@ mlc-mlapis, pero ¿hay algún plan para cuándo llegará IVY? en que version?

@micronyks ... Angular 7 por horario.

Chicos, seamos razonables aquí, Angular Team está esforzándose por trabajar en varias características enormes que son muy demandadas (PWA, SSR, Ivy y especialmente elementos personalizados), la última es una característica de muy alta prioridad, como pude entender, porque mucho de las grandes empresas (como Microsoft) lo han estado pidiendo desde siempre, y hay una razón para ello. Para lograr elementos personalizados eficientes, necesitan Ivy, tan pronto como terminen con Ivy, como dijo

Mientras tanto, en lugar de seguir exigiendo esta función (que también necesito desesperadamente por cierto), podemos ayudar al equipo de Angular a acelerar el proceso, probando las betas, informando errores, ayudando con los documentos, etc.

Recordemos que Angular Team ni siquiera es tan grande, es solo una docena de personas que intentan crear un marco increíble para todos, pero eso lleva tiempo.

... sí, es necesario ser un poco paciente ahora y esperar hasta el momento en que podamos ayudar más con Ivy ... cuando se complete el compilador y haya algunos documentos de diseño detallados disponibles.

@avatsaev Estoy de acuerdo con todo lo que dijiste. No deberías exigir cosas aquí. Pero puede explicar los problemas con los que se enfrenta cuando trabaja con Angular.

No estoy ni cerca de ser un desarrollador de Angular con mucha experiencia, pero algunas cosas se sienten mal o no se explican con la suficiente claridad.

Me encontré con este problema porque quiero encapsular un componente / directiva de terceros, sin tener una abstracción con fugas. Parte de esto es hacer posible tener directivas dinámicas. Lo que me sorprende es que es bastante complicado lograr tal cosa.

Estoy construyendo un generador de formularios, usando Angular Material y Flex-Layout, que toma una configuración JSON y genera un formulario. Esta característica me ayudaría a aplicar las directivas de diseño flexible al componente del host en tiempo de ejecución. Siento que uno de los mayores activos de Angular es la capacidad de generar código en tiempo de ejecución a partir de una configuración, esto contribuirá en gran medida a que ese código sea más versátil. Solo quería incluir un buen caso de uso. Soy paciente;)

Ese es mi caso de uso exacto

@NateMay, aquí está mi implementación si desea verla.

@NateMay, aquí está mi implementación si desea verla.

¿podría explicar por favor? Supongo que te refieres a la directiva de campo dinámico.

El dynamic-field.directive hace las cosas elegantes, pero también están sucediendo muchas otras cosas. Acabo de agregar CONTRIBUTING.md en la carpeta raíz, que tiene instrucciones para configurar localmente. Tenga cuidado con el uso en cualquier producto que esté en producción durante un par de meses. Estoy haciendo grandes cambios mientras trabajo hacia una implementación estable.

+1

De lejos, mis soluciones son, todas tienen desventajas.

  1. has-it , definir una nueva propiedad de miembro como esa directiva dentro de mi clase de componente, pasar todos los argumentos de constructor necesarios a esa directiva cuando llame a su función de constructor (por ejemplo, ElementRef, ViewContainerRef, TemplateRef ... cualquier variable inyectable que solicite) , y llamar manualmente a su devolución de llamada de ciclo de vida si lo ha hecho, como ngInit() ngAfterViewInit() en la función de devolución de llamada del ciclo de vida correspondiente del componente actual.
@component(...)
class MyComponent {
   theDirective: TargetDirective;
   constructor(el: ElementRef) {
       this.theDirective = new TargeDirective(el);
   }

  ngOnInit() {
     this.theDirective.ngOnInit();
  }
  ...
}
  1. Envuelva todo en la plantilla de componentes dentro de una plantilla ng de nivel superior,
    <ng-template><div targetDirective>....</div></ng-template> renderizarlos en ngAfterViewInit() como:
const vf = this.viewContainerRef.createEmbeddedView(this.templateRef);
vf.detectChanges();

De esta manera, Angular crea otro element con esa directiva y el contenido real de mi componente dentro de él justo después del elemento del componente original en el árbol DOM.

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

De esta forma es como lo que hace <route-outlet> .

Hay efectos secundarios obvios

¿Alguien puede confirmar si esto ahora es posible con Ivy? Si es así, ¿alguien tiene un ejemplo?

Recordemos que Angular Team ni siquiera es tan grande, es solo una docena de personas que intentan crear un marco increíble para todos, pero eso lleva tiempo.

Podría ser más grande si tuviera una comunidad de colaboradores.

Sin embargo, la posibilidad de que se acepte una corrección aportada para esto es muy baja.

Entonces, en cambio, volvemos a una docena de personas.

¿Alguien puede confirmar si esto ahora es posible con Ivy? Si es así, ¿alguien tiene un ejemplo?

Como aún no se ha dicho nada, pensé en proporcionar lo más cercano que pude encontrar, que es un artículo de hace un tiempo sobre la implementación de mixins con Ivy: https://blog.nrwl.io/metaprogramming-higher-order-components -y-mixins-con-hiedra-angular-75748fcbc310

Según el artículo, creo que una posible solución para el problema original de este hilo es utilizar la nueva característica llamada ... "características".

... Puedes imaginar que es un poco una pesadilla intentar buscar en Google algo sobre esta función. ¡Espero que publiquen pronto documentación oficial de Ivy! :)

@nayfin también construye diseñador / constructor de formularios visuales
Y después de unos meses de trabajar para quedarme atascado en el hecho de que no tengo forma de implementar la directiva para el div agregado dinámicamente, me vuelve loco ... Debería ser tan ajeno a llamar a algún MyDirectiveFactory :: apply (HTMLElement)

Esta característica sería muy bien recibida, ya que siempre me encuentro haciendo un solo div para adjuntar directivas de nivel superior. Además, si quiero alguna directiva de diseño flexible, también tengo que hacer ese div único y sería bueno si pudiera adjuntarlas directamente al elemento host en lugar de tener que hacer esto.

Sería genial poder agregar directivas dinámicamente como:

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

Además, si quiero alguna directiva de diseño flexible, también tengo que hacer ese div único y sería bueno si pudiera adjuntarlas directamente al elemento host en lugar de tener que hacer esto.

@tsteuwer Siempre puede usar el selector: host en su scss para aplicar propiedades de estilo al elemento host.

Pero sí, también me gustaría tener la capacidad de aplicar directivas al elemento host. sería útil para hacer que el elemento host se pueda desplazar y aplicar CdkScrollable desde Angular Material CDK.

Envuelva todo en la plantilla de componentes dentro de una plantilla ng de nivel superior

Una alternativa un poco más elegante a esto es usar https://github.com/trotyl/angular-contrib y agregar

host: { ngNoHost: '' }

Este proyecto modifica el renderizador y renderiza hijos de elementos con el atributo ngNoHost, sin padre.

Por supuesto, tiene muchos de los mismos inconvenientes.

Lástima que todavía esté abierto después de 3 años. Las directivas vinculadas al elemento host realmente mejorarían la capacidad de reutilización del código.

Además, si quiero alguna directiva de diseño flexible, también tengo que hacer ese div único y sería bueno si pudiera adjuntarlas directamente al elemento host en lugar de tener que hacer esto.

@tsteuwer Siempre puede usar el selector: host en su scss para aplicar propiedades de estilo al elemento host.

Pero sí, también me gustaría tener la capacidad de aplicar directivas al elemento host. sería útil para hacer que el elemento host se pueda desplazar y aplicar CdkScrollable desde Angular Material CDK.

No es ideal, pero puede crear el CdkScrollable programáticamente de esta manera:
this.scrollable = new CdkScrollable (this.elementRef, this.scrollDispatcher, this.zone);
this.scrollable.ngOnInit ();

También tienes que destruirlo manualmente:
if (this.scrollable) {
this.scrollable.ngOnDestroy ();
}

https://github.com/angular/angular/issues/8785#issuecomment -361004682 IgorMinar el trabajo de Ivy hace que esto sea más factible. Pero sí, pasado v6.

@mhevery Siguiendo con su comentario: point_up_2 :, ahora que Ivy finalmente ha aterrizado por

¿Hay cambios?

Si esta característica específica estuviera en la encuesta Angular https://twitter.com/angular/status/1252646001162088448?s=20 , apuesto a que sería la entrada más votada.

Hay toneladas de características que serían las más votadas, esta es seguro, pero también usa Observables por @output y muchas otras. Desafortunadamente, al ritmo actual, nunca se implementarán.

Si esta característica específica estuviera en la encuesta Angular, apuesto a que sería la entrada más votada.

¡Gran idea @princemaple!

Aunque no es lo ideal, esto se puede abordar en la sección "comentarios adicionales" de la encuesta (de la pregunta 2).
En donde dice:

"How else should we improve Angular for you?"

Básicamente, todos los interesados ​​en ver esta función, simplemente respondan la encuesta y hagan saber explícitamente que les importa mucho ver implementado y resuelto el "Problema # 8785".

Aquí está el enlace directo a la encuesta:
https://goo.gle/angular-survey-2020

¡Que la fuerza esté con usted! 🙂

Yo también he luchado con la forma de agregar programáticamente más funcionalidad a los componentes y, honestamente, creo que algunas de las propuestas aquí parecen las MEJORES formas de abordar esos problemas específicos.

He hablado con miembros del equipo angular con respecto a ese artículo.

¿Alguien puede confirmar si esto ahora es posible con Ivy? Si es así, ¿alguien tiene un ejemplo?

Como aún no se ha dicho nada, pensé en proporcionar lo más cercano que pude encontrar, que es un artículo de hace un tiempo sobre la implementación de mixins con Ivy: https://blog.nrwl.io/metaprogramming-higher-order-components -y-mixins-con-hiedra-angular-75748fcbc310

Y básicamente me dio la impresión de que se trataba de piratear los componentes internos de angular y, de hecho, no estaba diseñado para el consumo típico de los usuarios.

No estoy seguro de si existe alguna razón técnica que nos impida poder hacer esto, pero creo que si tuviéramos las capacidades para hacer esto, mejoraría DRÁSTICAMENTE mi día a día con angular.

“Hemos aumentado drásticamente nuestra inversión para trabajar con la comunidad. En las últimas tres semanas, nuestro recuento de problemas abiertos ha disminuido en más de 700 problemas en el marco, las herramientas y los componentes. Hemos abordado más de 2,000 temas y planeamos hacer grandes inversiones en los próximos meses, trabajando con la comunidad para hacer aún más ". - @StephenFluin
de Angular 10 Announcement

Entonces, supongo que esto significa que veremos este problema anulado en v11. 🤞😏

¡¿Qué mejor manera hay de "trabajar con la comunidad" (y apaciguarlos) que trabajar para agregar una de sus características más solicitadas? (este 😉)

¡Escúchalos!

Solo para establecer las expectativas, lo que está pidiendo no es una cantidad trivial de trabajo y las estructuras de datos actuales no están realmente diseñadas para esto. Entonces, para respaldar algo como esto, se requeriría una ingeniería importante.

@mhevery ¿en qué se diferencia de aplicarlos desde el padre en la plantilla?

@ k3nsei Es necesario verlo desde el punto de vista NgModule , que en realidad es el elemento clave que crea la infraestructura para todos sus componentes.

@ mlc-mlapis Tenemos @HostBinding y @HostListener, así que tal vez @HostDirective sería una buena opción para esa funcionalidad. He visto charlas de que Ivy apis habilita tales funcionalidades.

Para mí, el punto clave es tener alguna API de composición que nos permita tener más clases desacopladas con la capacidad de tener extensiones / rasgos con basura reutilizable de funcionalidad. Por ejemplo, como seleccionable, expandible / colapsable.

@ k3nsei Podría ser, pero no estoy seguro si no es demasiado dinámico, por lo que tiene menos rendimiento que las estructuras estrictamente estáticas.

"Solo para establecer las expectativas, lo que está pidiendo no es una cantidad trivial de trabajo y las estructuras de datos actuales no están realmente diseñadas para esto. Por lo tanto, para respaldar algo como esto se requeriría una ingeniería importante". - https://github.com/angular/angular/issues/8785#issuecomment -654391378

Gracias por su respuesta oportuna @mhevery.

Creo que hablaré en nombre de la comunidad al decir que no se nos escapa en absoluto que este será un gran desafío. Si no fuera así, estoy seguro de que a estas alturas habríamos creado algunas bibliotecas de terceros que lo logran correctamente (de alguna manera). [que yo sepa no hay ninguno].

Además, no hace falta decirlo, pero háganos saber si hay alguna fruta madura (o de otro tipo) que podamos ayudar a contribuir a esto.

Le agradecemos sinceramente y valoramos su comunicación sincera y esperamos seguir siendo parte del diálogo sobre lo que necesitamos frente a lo que es realista / pragmático para agregar.

Aunque tengo entendido que Ivy hace que esto sea más fácil que antes.

@mhevery

@IgorMinar el trabajo de Ivy hace que esto sea más factible. Pero sí, pasado v6.

Aunque tengo entendido que Ivy hace esto más fácil que antes

Mi nuevo entendimiento es "más fácil" todavía no significa "fácil"

Mi nuevo entendimiento es "más fácil" todavía no significa "fácil"

Ivy: En lo que pasaste dos años y aún no aborda ninguno de los problemas angulares más populares.

Ivy: En lo que pasaste dos años y aún no aborda ninguno de los problemas angulares más populares.

@pauldraper Supongo que nuestros problemas no son sus "problemas", ya que este en particular ni siquiera está en su radar (Ver Hoja de ruta https://angular.io/guide/roadmap).

Para mí, personalmente, creo que es hora de buscar en otra parte un proyecto que no solo sea de código abierto, sino un proyecto en el que la comunidad (y los usuarios) tengan una influencia real .

@pauldraper Supongo que nuestros problemas no son sus "problemas", ya que este en particular ni siquiera está en su radar (Ver Hoja de ruta https://angular.io/guide/roadmap).

@somombo Estoy decepcionado por el hecho de que este problema sigue abierto después de todos estos años, pero no puedo estar de acuerdo con usted. El primer punto de la hoja de ruta trata explícitamente sobre el manejo de problemas abiertos de github. Incluirlos a todos en la hoja de ruta no tendría mucho sentido, ¿verdad? Este problema es uno de los más votados (o el más votado) y espero que finalmente se resuelva.

El primer punto de la hoja de ruta trata explícitamente sobre el manejo de problemas abiertos de github. Incluirlos a todos en la hoja de ruta no tendría mucho sentido, ¿verdad? Este problema es uno de los más votados a favor (o el más votado) y espero que finalmente se resuelva.

no, esto es solo una ilusión, lea https://github.com/angular/angular/issues/5689 no hay absolutamente ninguna indicación de que quieran abordar ninguno de los problemas más votados, aparte de las "formas fuertemente tipadas" en el futuro

@pauldraper Supongo que nuestros problemas no son sus "problemas", ya que este en particular ni siquiera está en su radar (Ver Hoja de ruta https://angular.io/guide/roadmap).

@somombo Estoy decepcionado por el hecho de que este problema sigue abierto después de todos estos años, pero no puedo estar de acuerdo con usted. El primer punto de la hoja de ruta trata explícitamente sobre el manejo de problemas abiertos de github. Incluirlos a todos en la hoja de ruta no tendría mucho sentido, ¿verdad? Este problema es uno de los más votados (o el más votado) y espero que finalmente se resuelva.

De todos modos ... he terminado de esperar ... este problema literalmente ha sido un gran obstáculo para mí. Así que el hecho de que ni siquiera parece que esté siendo planeado para un futuro próximo significa que es hora de que yo personalmente siga adelante. Mucha suerte a todos los demás.

Me gustaría que se cambiara el nombre de esto a "Soporte para agregar directivas a las directivas". Si bien ese nombre puede resultar confuso, creo que es importante que esta función funcione en directivas y no se limite a los componentes. Otros nombres para la característica pueden ser "directivas implícitas" o "directivas adjuntas", lo que significa que cuando usa un componente o directiva dada en una plantilla, extrae las directivas implícitas / adjuntas en el elemento host.

He querido esto muchas veces, principalmente porque la composición es potencialmente una forma más limpia de reutilización en Angular, en comparación con la herencia. La herencia puede ser torpe porque no hay herencia múltiple, los parámetros del constructor deben transmitirse y algunas características de Angular funcionan cuando se heredan, y otras tienen que "reconectarse" en cada clase hoja.

Me imagino que las "directivas implícitas / adjuntas" funcionan como una forma de instanciación de directiva local de componente o local de directiva, lo que da como resultado que la directiva se instancia en el elemento host sin requerir que el selector de directiva exista en el marcado de la plantilla.

He aquí un ejemplo:

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

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

}

Las propiedades @Input() y @Output() de la directiva adjunta se pueden usar en la plantilla, y los métodos del ciclo de vida de la directiva adjunta deben llamarse como parte del ciclo de vida del componente del host. La directiva adjunta también se puede vincular al elemento host. Básicamente, actúa exactamente como una directiva declarada por plantilla en la actualidad, pero no es necesario declararla en la plantilla.

Creo que esta característica proporcionaría un beneficio significativo a Angular, permitiendo arquitecturas de componentes más limpias / simples a través de la composición de directivas.

Hoy, tiene 2 opciones si desea una forma similar de reutilización de directivas:

  • Requerir que un conjunto de directivas relacionadas siempre se declaren juntas en el marcado de la plantilla; y detectar y lanzar errores si las directivas requeridas no están presentes en el constructor. No hay forma de exigir que las directivas requeridas se declaren en el mismo elemento. Esto es complicado desde el punto de vista de la documentación y la creación de plantillas, y no es una API o un contrato sólido debido a la redundancia, pero por lo demás no es horrible.
  • Cree una instancia manual de las directivas adjuntas dentro de la directiva de host y reenvíe los parámetros del constructor, las propiedades @ Input / @Output , los enlaces de host y los métodos del ciclo de vida a las directivas internas. Este es un lío frágil para los autores de componentes, pero es factible dado un conjunto simple de componentes relacionados. Es mucho más agradable para la creación de plantillas.

Para decirlo de otra manera, la ausencia de la función a veces crea una compensación innecesaria entre el uso limpio + simple de componentes y la creación limpia + simple de componentes.

@johncrim
Tenga en cuenta que en un caso del mundo real, su directiva personalizada tendría entradas, y querría transformarlas y pasarlas como entradas a la directiva adjunta. Quizás esto podría hacerse con una sintaxis similar a los atributos host en las opciones del decorador de directivas.

@amakhrov : Buen punto:

En los casos en que haya conflictos de nombres o problemas de claridad de nombres (que trataría de evitar al diseñar directivas para la composición), o cuando las entradas o salidas deben transformarse, eso podría manejarse con bastante facilidad inyectando la directiva adjunta en el padre directiva, y cree nuevas propiedades de entrada o salida en el padre que delegan a las directivas adjuntas.

Me quedo corregido.
Este problema se incluye ahora en la sección "Futuro" de la hoja de ruta oficial. Consulte https://angular.io/guide/roadmap#support -adding-directives-to-host-elements

Admite agregar directivas a los elementos del host

Una solicitud de característica de larga data es agregar la capacidad de agregar directivas a los elementos del host. La función permitirá a los desarrolladores aumentar sus propios componentes con comportamientos adicionales sin usar la herencia. El proyecto requerirá un esfuerzo sustancial en términos de definición de API, semántica e implementación.

Como acabo de notarlo, no estoy seguro de cuándo se agregó, pero debo admitir que es una gran noticia y un gesto significativo y tranquilizador. Seguiré cruzando los dedos.

¡Gracias al equipo por ponerlo ahí! 🙏🏾

¿Fue útil esta página
0 / 5 - 0 calificaciones