Angular: 建议:需要能够在组件声明中向宿主元素添加指令。

创建于 2016-05-23  ·  114评论  ·  资料来源: angular/angular

我一直在研究 Angular 2,并遇到了扩展某些类型组件的潜在障碍。

在下面的示例中,我有一个按钮组件和一个将根据触摸事件应用样式的指令。 除了按钮之外,还有许多其他对象将继承完全相同的触摸行为。 我已经探索了我的选择,但我不知所措:

  • 直接扩展一个 TouchClass。 这似乎不太理想,因为 typescript 不支持多类继承,而且我还想向消费者公开该行为以在他们自己的类中使用。
  • 通过接口伪造多类继承。 这似乎是一个 hack,需要我在我试图混入的每个类上重新声明一个 shim api。 https://www.stevefenton.co.uk/2014/02/TypeScript-Mixins-Part-One/
  • 创建一个辅助函数,该函数直接在组件构造函数中的 elementRef.nativeElement 上通过服务执行此操作。 我真的不想这样做,因为它在文档中声明在工作程序中运行时 nativeElement 将为空,而这种功能是我最兴奋的。

不用太深入,我假设 componentMetadata 在组件编译期间可用,并且可以扫描主机属性以查找可以动态添加并同时编译的其他指令。 这将允许您以角度方式进行混合:使用可组合指令扩展功能,并且在不破坏视图投影的情况下进行。 下面的简短示例。

当前行为
在 componentMetadata.host 中声明指令会将其视为常规属性

预期/期望行为
在 host 中声明的指令将在编译时处理。

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

    }
}

关于这如何工作的一些想法:

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

谢谢大家,Angular 2 看起来很棒! 如果我遗漏了什么,请告诉我。

core directive matching host and host bindings feature

最有用的评论

@IgorMinar常春藤工作使这更可行。 但是是过去的 v6。

所有114条评论

我目前正在开发一个大型客户端,因此我试图将所有与 GUI 相关的问题分解为可重用的 Angular2 指令。 正如詹姆斯完美地指出的那样,这总是让我遇到同样的问题。
为了良好的模块化和动态架构,这确实必须以某种方式起作用。 触摸示例只是许多需要这样做的场景之一。 例如拖放,调整大小观察等等等。
再举一个例子作为plunker:
https://plnkr.co/edit/J65THEMic0yhObt1LkCu?p=info

是否有可能很快添加此功能?

这是与此相关的 StackOverflow 问题: http :

@Andy1605你有没有找到解决这个问题的方法? 因此,在 RC 期间,我有点想与 NG2 合作。 很想把它拿回来,但这个特殊的问题阻止了我构建可扩展的 UI 模式。

我也觉得 Angular 在这里缺少一个基本功能。 组件应该可以为其宿主声明(多个)属性指令。 无法做到这一点也是我项目的主要障碍。
有没有人知道这是否会在未来实施,或者是否有无法完成的原因?

我在这里提出了这个问题的解决方案(尽管是 angular 版本 1): https :

我的想法是,编译框架不仅具有添加指令的能力,还具有一个名为“hostTransforms”(在 angular 1 的情况下为“nodeTransforms”)的新扩展点,它可以访问未修改、未过滤的组件声明以及当编译器第一次遇到组件并准备插入到 DOM 时的原始未编译组件主机节点。 通过这种方式,开发人员可以使用自定义属性扩展组件装饰器,然后在编译之前使用 nodeTransforms 将这些自定义属性转换为 angular 框架熟悉的内容。 检查功能请求线程以获取示例。

我对angular源代码比angular 2源代码更熟悉,所以我不确定这里的实现过程是否相同。 但由于这似乎是一个非常受欢迎的请求,我很想看到它要么在 angular 2 中实现并反向移植,要么在 angularjs 中实现并向前移植(这是一回事吗?)。

+1

我必须同意,让我们向主机添加贡献属性指令的功能会很棒。 我知道我现在可以使用这样的功能来实现一种更“有角度”的方式,将拖/放功能添加到我的 UI 组件中。

如何创建一个类似于<ng-container>的新标签,让您可以将它们应用到组件的模板中,而不是host元数据属性? 类似于<ng-host [attributeDirective]>东西表示指令被添加到主机组件中。

@jjstreet您的提议听起来与replace: true相似(显然不完全相同,但相似),这在不久前已被弃用。 但也许replace: true被弃用的原因在这里不适用。

@pkozlowski-opensource 我们能从 ng2 团队得到任何形式的回应吗?

我愿意以任何方式实现这一目标。 我建议使用 host 属性,因为它可以访问组件的本地范围,并且它已经为组件本身添加了属性。 指令似乎是这种行为的自然延伸。

+1 此功能需要在 UI 组件中具有干净且可重用的代码

+1

能否请我们从 ng2 团队获得某种回应? 即使只是说你不会这样做,或者说这是个好主意但不是当前的优先事项,我也只是想听听一些意见。

我想为此添加另一个用例。 它将允许 ng2-mobx (https://github.com/500tech/ng2-mobx) 摆脱包装组件并看起来更干净。

我也很想拥有这个。 目前我需要它来制作自定义routerLink指令。 我很想重用 angular 一个,只为它提供 mi 指令准备的参数。

所以代替<a [routerLink]="repeatedCodeToGetLink()">我会有<a [myRouterLink]>并且它会动态地应用[routerLink]和解析的参数。

真的很兴奋这个前景!

我们需要这个已经有一段时间了。 事实上,在我知道它有一个未解决的问题之前,我在堆栈溢出时询问了这个特性。

我提供了一个详细的示例,说明我们如何能够使用此功能来解决我们已经开放了一段时间的https://github.com/angular/flex-layout/issues/162 。 (请参阅此处的示例和说明

我们真的很期待任何反馈,我看到这个问题是这个 repo所有排名第三的。 希望我们可以在下一个版本中或更早的版本中看到这一点(手指交叉)!

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

@jjstreet我认为你的建议

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

... 与另一个单独的提案相得益彰,该提案是出于我们在这里讨论的不同原因而提出的。

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

目前我通过在父组件中添加指令来解决这个问题,然后使用@HostListener 在主机中添加侦听器。

父.html
<my-component myDirective>

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

但是如果我们可以直接在主机中添加属性会更清晰......

这是我处理这个问题的方式,但我真的认为从地面实现这个功能将是最好的解决方案。

  • 我的堆栈溢出答案的完整解释: http :
  • TL;DR 见 Plunker: https ://plnkr.co/edit/TCagW8vOPrqSiOT9Oztf

只是每月提醒一下,我们正在等待 Angular 团队对此的正面或负面评论。

@tbosch - 关于这个问题的优先级的任何公众想法。 它也会影响@angular/flex-layout

@fadzic你能不能不只是通过这样做将指令添加到宿主元素...

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

或者像这样,如果你需要像 ElementRef 或 Renderer2 这样的参数
@HostBinding('attr.myHilite') myHilite = new myHiliteDirective(this.elementRef, this.renderer)

我还需要向宿主元素添加指令,我被重定向到这个问题。 我能够使用上面的代码做我需要的事情。 我绝不是使用 angular 的专家,但到目前为止,这种解决方法似乎有效,如果有人对此方法有疑问,请告诉我。 谢谢。

@btinoco 不起作用,因为没有调用生命周期方法。 您必须手动将使用该指令的每个组件中的所有内容连接起来,而不仅仅是让 Angular 为您连接起来。

@hccampos感谢您的ngOnInit没有执行(除非我在组件中手动使用该指令)或者我在组件的ngOnInit()上调用了该指令的ngOnInit() ngOnInit() . 再次感谢你让我知道。

@btinoco - 是的。 这是一个微妙但令人讨厌的问题。 @angular/flex-layout 希望尽快修复的问题。 ;-)

Angular 团队有关于这方面的消息吗? 问题开出来已经1年了...

找到关于这个问题的详细描述非常酷,
然后发现没有来自 Angular 团队的反馈是非常不酷的 :(

关于已经工作的解决方案:

这个功能请求听起来很像 mixins。 实际上,说明中的第 2 项
此功能的实际匹配官方
TypeScript 的文档,请参见此处
在 angular 中,这会变得更糟,因为将类与@Input() s 混合,您
必须在基类上重新声明它们。

今天已经有效的另一种解决方案是让组件包含一个包装元素并在那里应用指令。
例如,如果组件包含像<wrapper g-touch>...这样的模板

关于“创建一个帮助函数,它通过直接在 elementRef.nativeElement 上的服务来完成”:
是的,这似乎也是个好主意。 我暂时不会关心 WebWorkers,
因为它们仍处于试验阶段并且缺少一些更大的生产功能,
几乎没有图书馆可以在 WebWorkers 上工作。
参见例如我们直接访问 DOM 的材料库。

关于提案的选项 1):

主机属性绑定的当前语义是,
他们在底层 HTML 元素上设置了一个属性myDir
但不是任何指令。 但是,如果host也可以引入指令,用户可以这样写
并且会困惑为什么这不会更新指令myDir的属性:

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

关于选项 1) 和选项 3):
在同一元素上的指令之间引入某种host绑定可以:

  • 导致数据绑定图中出现循环,Angular 不支持,因此
    由于过时的值/“检查后表达式已更改”错误,导致难以调试错误。
  • 与相互注入的指令相比,导致额外的性能开销
    并直接交流。

关于提案的选项 2):

  • 是的,必须引用gTouch类似乎很奇怪,因为所有其他指令
    通过NgModule触发。

@ThomasBurleson让我们更详细地离线讨论您的用例...

@tbosch我想提出另一种选择:引入原生角度标签,我们称之为<ng-host>

注意: @mhevery提议在https://github.com/angular/angular/issues/7546 中引入<ng-host>标签,但是,即使我在这里使用相同的标签名称,我是什么提议是单独的,专门用于解决此处提出的问题。

ng-host标签不会作为常规指令/组件类实现,而是作为“魔法”框架代码......类似于ng-contentng-container等。 .,
该标签将简单地充当组件主机的“指针”,其方式类似于 css :host选择器

它避免了模棱两可的情况,每个组件最多只能有一个<ng-host>块,并且它必须是该组件模板的根标签/节点。

以下是人们将如何使用它:

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

顺便说一句@tbosch ,谢谢你的回复。 我们非常感谢您对此问题的参与和反馈。

其他人对此功能的看法是否特定于组件,或者如果指令可以将不同的指令应用于其主机也有意义? 我开始订阅这个问题的用例涉及一些第三方指令 A) 我们希望与我们的代码隔离,以防我们以后想要更改,B) 希望将一些默认功能应用于每个实例而不必重复每次我们使用它时的设置。

例如,一个工具提示指令,它将应用于整个应用程序中的大量元素,我们希望默认延迟和 appendToBody(它不支持集中配置对象)。 因为它不支持中央配置对象,所以我们不得不在我们想要使用它的任何地方放置三四个属性。 后来,我们确实离开了那个库(开始使用材料工具提示),我们不得不手动替换每个工具提示。 如果我们能够创建我们自己的指令来“包装”它,那么就像更改该指令以将 [mdTooltip] 应用于其主机而不是其他库一样简单。

@MikeMatusz看起来我也想到了这一点,这是我来自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 {}

是否可以创建一个实例化指令的属性装饰器?
例如:
@HostDirective(LayoutDirective) myLayoutDirective: LayoutDirective;

这适用于组件和指令,为交互提供实例引用,并且从组件/指令继承时不会丢失。
如果您还想提供输入和事件绑定,我想它会变得更加复杂。

这是站在哪里? 我对 Angular2/4 还很陌生,我想做的是创建一个指令,它一次只应用几个其他指令。 所以,而不是:

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

我只能写:

<button customDirectiveABC>BUTTON TEXT</button>

感觉这应该很容易 - 基本成分/干燥。 但据我所知这是不可能的?

@soynog ,我感觉完全一样。 我也想知道这是什么情况。

我希望能够使采用了棱角分明材料和angular2,拖动可拖动对话框(因为角/材料#1206尚不支持),我想动态地将指令添加到md-dialog-containerMdDialog服务创建,但在这里获得动态指令的 Angular 1.x 编译器的行为似乎要困难得多。

@tbosch@ThomasBurleson ,您讨论的离线用例是否与 Thomas 在 angular/material#1206 中偶然提出的问题或目标有关? 我只是想了解 1.6.x 和 2+ 框架之间的行为变化。

是否有关于此问题的更新? 它一开始就受到关注,但我认为它不再受到任何关注。

是的,有任何更新吗?

这是我非常需要的东西,希望这个提案能顺利推进。

这会很好,今天意识到我不能以编程方式/动态方式应用指令,感到很伤心。

+1
我也是 :)
我正在寻找一种将多个指令绑定到一个自定义指令中的方法,该指令可以满足我的所有需求。 例如 :

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

如果我可以创建一个指令来包装所有这些东西,这样我就可以重用它们而无需复制/粘贴所有行,那就太好了。

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

并进入我的自定义指令

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

在这个问题的两周年即将到来! 老实说,这个功能非常有用,它让我们可以创建高度可组合的组件和指令,而无需创建一百万个包装器。 只需根据您拥有的指令组合您需要的组件。 简单,干净,有效。

@IgorMinar - 无论如何,我们可以在雷达上获得此功能以用于即将进行的增强功能吗?

我想知道这样的功能是否会被视为不良模式。 任何人?

@darkbasic - AFAIU,如果没有此功能,开发人员将需要引入一个包装元素(在ng-container ),以便将父指令添加到模板视图和内容中。

不,我不认为能够完全控制自己的组件而不必使用包装器是一种糟糕的模式。 这是必需品。

@bradlygreen 有什么评论吗?

这个特性是这个 repo 的所有未决问题中最受欢迎的请求(如果不是前 5 名)......在整个互联网上,我们看到 Angular 作为事实上的框架衰落的报告(由经验数据支持)......我认为推动社区不被倾听的情绪之一。 比赛; vue.js 和 react 正在取得进展并且已经超越了 angular,因为即使它们不一定实现每个人都想要的每一件小事,但它们至少会提供有关最受欢迎的请求项目的持续反馈。 等了这么久却什么也没听到,真是令人沮丧……甚至连一句简单的“不,我们不会做”的声音都没有。

(参见“Angular Slips”Js 框架部分)

...虽然我认为关于 Angular / Vue / React / ... 的一些观点受到不同因素的影响 ... 这个具体的功能确实值得某种形式的实现(即使情况比只是一个复杂一点一个简单的应用指令列表的解决方案)......所以Angular核心团队的具体立场将非常受欢迎......🥇

没有具体的预计到达时间,但我们正在努力使 2018 年渲染器中的此类事情变得更加容易。

希望 2018 年情况会好转。我们正在输

看:

@somombo这篇文章很久以前就被证实是胡说八道

真正了解他们东西的人取笑作者,他们没有人认真对待他,喜欢的人自然是来自react,vue fanboys。

所以事实是,这个问题对于 Angular 团队来说是一个非常低的优先级,实际上它是最低优先级。

请参阅AngularHQ 上发布的优先级列表(查找问题编号 8785)

尽管如评论数量所示,此问题引起了社区的大量讨论和兴趣,但情况仍然如此。

如果你是一个关心这个问题并且真的很想看到它实施的人,那么与其等待..老实说_可能永远不会_,也许你可以填写官方年度角度调查,并表明你对这个问题的看法应该是一个更高的优先级,并希望看到它早日实现。

不要忘记感谢我们的 Angular 团队所做的所有伟大的工作!

我也想为这个功能投票。 这是试图解决这个问题的太多悲痛的原因。

@somombo请不要过多地了解 AngularHQ 中的优先级。 优先级公式没有完全充实。 话虽如此,我认为我们应该在 v6 发布后重新审视这个功能请求。 我担心我们没有更早的带宽,并且在此方面的工作会与编译器/核心领域已经在进行的工作相冲突。

这不是快速修复请求。 我怀疑要正确地完成它需要付出相当大的努力,但是我们正在为 v6 工作的东西应该使这个更容易实现。

@IgorMinar常春藤工作使这更可行。 但是是过去的 v6。

@IgorMinar@mhevery我怎么强调我(还有我们其他人,我敢肯定)你给了我们这些具体的反馈,告诉我们你的想法是什么,在这个问题出现之前需要先发生什么,我怎么强调都不为过得到妥善解决。

我们外行并不总是清楚什么是“快速修复”,什么不是。 但是,除非这不是一种快速修复类型的事情并且必须正确完成,否则我特别感谢您似乎也觉得这将是 angular 的一个有用功能。

我们知道你们都很忙,不可能对每一个问题都做出这样的回应。
因此,无论何时,您都会受到我们真诚的感谢。 我们很兴奋并期待 angular v6 及更高版本!

感谢您所做的一切伟大的工作!

您可以让组件类扩展或实现指令类。 如果您试图在幕后应用指令,那么它可能只是组件中的逻辑。

export class gBtn extends gTouch

@NateMay ,只允许你扩展一个类。 这个问题更多地是关于使用指令组合多个功能。

@NateMay 有两个问题 - 首先,您只能扩展一个类,其次,您刚刚破坏了依赖注入。

只需加上我的两分钱。 我正在使用@uirouter/angular 的嵌套状态构建具有角度、材料和弹性布局的多层 SPA。 那么无法轻松地将 flex 指令应用于组件元素是非常有限的。

所以请为这个功能投票。

+1 此附加功能

可以向ng-container添加指令,该指令不会出现在 DOM 中。

我需要交叉观察器 API(当元素进入/离开视口时引发事件)。 我有一个intersector指令,当元素变得可见/隐藏时,它有enter()leave()事件。 我有某些组件需要在内部使用此 API,但我不想在模板中添加额外的 DIV。

所以我在component.html所做的是以下内容:

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

然后intersector.directive.ts指令构造函数注入ElementRef

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

对于普通的 DOM 元素,您只需对intersectorElementRef.nativeElement ,但对于ng-container节点实际上是一个注释节点。 所以我只是检查一下它是否是评论,如果是,那么我会上升一个级别。

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

这并不适用于所有情况,但我现在对此很好。 我相信在 IVY 编译器中,他们可能不再使用注释 - 所以这可能会中断。 重要的是我有一个指令,我可以在 DOM 节点上使用,或者有效地作为指令的假“ @HostBinding ”。

我真的希望可以动态添加指令。 我希望能够将较低级别的指令封装在“更高阶”、更抽象的指令中。 我问了以下关于堆栈溢出的问题,我想知道将来是否会有解决方案: https :

正如@mhevery所说......我们需要耐心等待完整版ivy(ng v7.0.0?)登陆。 显然,他们用 ivy 实现起来会容易得多...... ivy 之后,我们必须提醒团队这个功能对我们来说有多重要,所以他们不要忘记它😉

订阅这个。 我还需要能够将指令动态附加到我使用 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)

任何更新???
我遇到了另一个使用第三方指令的用例。
在某些情况下,我需要在 HTML 元素上动态删除/添加指令。
这是否可能以任何方式或等待解决方案?

@micronyks ...实际上不可能动态添加指令。 我们必须等待 Ivy,它应该增加创建这样一个功能的可能性。

@mlc-mlapis 但有什么计划什么时候会来? 在哪个版本?

@micronyks ... Angular 7 按计划发布。

伙计们,让我们在这里讲道理,Angular Team 正在努力开发几个需求量很大的功能,(PWA、SSR、Ivy,尤其是自定义元素),据我所知,最后一个是非常高优先级的功能,因为很多的大企业(如 Microsoft)一直要求它,这是有原因的。 为了实现高效的自定义元素,他们需要常春藤,一旦他们用常春藤完成,正如@mhevery所说,引擎将更容易地允许动态指令。

与此同时,我们可以帮助 Angular 团队加速这个过程,通过测试 Beta 版、报告错误、帮助文档等,而不是继续要求这个功能(顺便说一句,我也非常需要)。

让我们记住,Angular Team 甚至没有那么大,只有十几个人试图为每个人制作一个很棒的框架,但这需要时间。

...是的,现在有必要耐心等待,等到我们可以为 Ivy 提供更多帮助的那一刻......编译器完成并提供一些详细设计文档时。

@avatsaev我同意你所说的一切。 你不应该要求这里的东西。 但是你可以解释你在使用 Angular 时要处理的问题。

我远不是一个非常有经验的 Angular 开发人员,但有些事情感觉不对或解释得不够清楚。

我遇到这个问题是因为我想封装第三方组件/指令,而没有泄漏的抽象。 部分原因是使动态指令成为可能。 让我感到惊讶的是,实现这样的事情相当复杂。

我正在构建一个表单生成器,使用 Angular Material 和 Flex-Layout,它采用 JSON 配置并生成一个表单。 此功能将帮助我在运行时将 flex-layout 指令应用于宿主组件。 我觉得 Angular 最大的资产之一是能够在运行时从配置生成代码,这将大大有助于使代码更加通用。 只是想加入一个好的用例。 我有耐心 ;)

这是我的确切用例

@NateMay如果您想查看它,这是我的实现。

@NateMay如果您想查看它,这是我的实现。

你能解释一下吗? 我猜你的意思是dynamic-field.directive

dynamic-field.directive做了一些花哨的事情,但还有很多其他的事情正在发生。 我刚刚在根文件夹中添加了CONTRIBUTING.md ,其中有本地设置的说明。 在生产中使用几个月时要小心。 在我努力实现稳定的实施过程中,我正在做出重大改变。

+1

到目前为止,我的解决方法是,它们都有缺点。

  1. has-it ,在我的组件类中定义新的成员属性作为该指令,在调用其构造函数时将所有需要的构造函数参数传递给该指令(例如 ElementRef、ViewContainerRef、TemplateRef...它要求的任何可注入变量) ,如果有的话,手动调用它的生命周期回调,比如ngInit() ngAfterViewInit()在当前组件对应的生命周期回调函数中。
@component(...)
class MyComponent {
   theDirective: TargetDirective;
   constructor(el: ElementRef) {
       this.theDirective = new TargeDirective(el);
   }

  ngOnInit() {
     this.theDirective.ngOnInit();
  }
  ...
}
  1. 将组件模板中的所有内容包装在顶级 ng 模板中,
    <ng-template><div targetDirective>....</div></ng-template>将它们呈现在ngAfterViewInit()例如:
const vf = this.viewContainerRef.createEmbeddedView(this.templateRef);
vf.detectChanges();

这样,Angular 会在 DOM 树中的原始组件元素之后创建另一个带有该指令的element和我组件的实际内容。

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

这种方式就像<route-outlet>所做的那样。

有明显的副作用

有人可以确认 Ivy 现在是否可以这样做吗? 如果是这样,有人有例子吗?

让我们记住,Angular Team 甚至没有那么大,只有十几个人试图为每个人制作一个很棒的框架,但这需要时间。

通过拥有一个贡献者社区,它可能会更大。

但是,为此提供的修复程序被接受的可能性非常低。

所以相反,我们回到了十几个人。

有人可以确认 Ivy 现在是否可以这样做吗? 如果是这样,有人有例子吗?

由于还没有消息,我想我会提供我能找到的最接近的东西,这是一篇关于使用 Ivy 实现 mixin 的文章: https :

根据文章,我认为该线程原始问题的一个可能解决方案是使用名为...“功能”的新功能。

...您可以想象尝试在 Google 上搜索有关此功能的任何信息都是一场噩梦。 希望他们尽快发布一些 Ivy 官方文档! :)

@nayfin还构建可视化表单设计器/构建器
经过几个月的努力,我无法将指令部署到动态添加的 div 上,这让我发疯了......

这个特性会非常受欢迎,因为我发现自己总是制作一个div来附加顶级指令。 此外,如果我想要任何 flex-layout 指令,我也必须制作一次性 div,如果我可以将它们直接附加到宿主元素而不必这样做,那就太好了。

能够动态添加指令会非常酷,例如:

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

此外,如果我想要任何 flex-layout 指令,我也必须制作一次性 div,如果我可以将它们直接附加到宿主元素而不必这样做,那就太好了。

@tsteuwer你总是可以在你的 scss 中使用 :host 选择器来将样式属性应用到宿主元素。

但是,是的,我也希望能够将指令应用于宿主元素。 将有助于使宿主元素可滚动并从 Angular Material CDK 应用 CdkScrollable。

将组件模板中的所有内容包装在顶级 ng-template 中

一个稍微光滑的替代方法是使用https://github.com/trotyl/angular-contrib并添加

host: { ngNoHost: '' }

该项目填充渲染器并渲染具有 ngNoHost 属性的元素的子元素,无父元素。

当然,它也有许多相同的缺点。

遗憾的是,这家酒店在 3 年后仍然开放。 绑定到宿主元素的指令将真正提高代码重用能力。

此外,如果我想要任何 flex-layout 指令,我也必须制作一次性 div,如果我可以将它们直接附加到宿主元素而不必这样做,那就太好了。

@tsteuwer你总是可以在你的 scss 中使用 :host 选择器来将样式属性应用到宿主元素。

但是,是的,我也希望能够将指令应用于宿主元素。 将有助于使宿主元素可滚动并从 Angular Material CDK 应用 CdkScrollable。

不理想,但您可以通过这种方式以编程方式创建 CdkScrollable:
this.scrollable = new CdkScrollable(this.elementRef, this.scrollDispatcher, this.zone);
this.scrollable.ngOnInit();

您也必须手动销毁它:
如果(this.scrollable){
this.scrollable.ngOnDestroy();
}

https://github.com/angular/angular/issues/8785#issuecomment -361004682 IgorMinar Ivy 的工作使这更可行。 但是是过去的 v6。

@mhevery跟进您的评论 :point_up_2:,既然 Ivy 终于完全登陆,我们可以在 v10 发布时(或之前)拥有此功能吗? 如果没有,请告知我们还有哪些其他考虑可能会进一步阻碍这一点。

有什么变化吗?

如果此特定功能出现在 Angular 调查https://twitter.com/angular/status/1252646001162088448?s=20 中,我敢打赌它会是投票最高的条目。

有大量的功能会被评为最高投票,当然这个功能也是使用 Observables 的@output和许多其他功能。 不幸的是,以目前的速度,它们永远不会得到实施。

如果这个特定功能出现在 Angular 调查中,我敢打赌它会是投票最高的条目。

好主意@princemaple!

虽然不理想,但这实际上可以在调查的“额外评论”部分(问题 2)中解决
它在哪里说:

"How else should we improve Angular for you?"

所以基本上,每个对这个功能感兴趣的人,请回答调查并明确说明,你非常关心“问题 #8785”的实施和解决。

这是调查的直接链接:
https://goo.gle/angular-survey-2020

愿原力与你同在! 🙂

我也一直在为如何以编程方式向组件添加更多功能而苦苦挣扎,老实说,我认为这里的一些建议似乎是解决这些特定问题的最佳方法。

我已经就那篇文章与 angular 团队成员谈过

有人可以确认 Ivy 现在是否可以这样做吗? 如果是这样,有人有例子吗?

由于还没有消息,我想我会提供我能找到的最接近的东西,这是一篇关于使用 Ivy 实现 mixin 的文章: https :

基本上是给人一种这是在用 angular 的内部结构进行黑客攻击的印象,实际上它并不是为典型的用户消费而设计的。

我不确定是否存在任何技术原因阻止我们能够做到这一点,但我认为如果我们有能力做到这一点,它将极大地改善我的日常工作。

“我们大幅增加了与社区合作的投资。 在过去的三周里,我们的框架、工具和组件的未解决问题数量减少了 700 多个。 我们已经触及了 2,000 多个问题,我们计划在未来几个月内进行大量投资,与社区合作做更多事情。” — @StephenFluin
来自Angular 10 公告

所以我想这意味着我们会在 v11 中看到这个问题被解决了? 🤞😏

有什么更好的方式来“与社区合作”(并安抚他们)而不是努力添加他们最需要的功能之一!? (这个😉)

听他们说!

只是为了设定期望,您所要求的不是微不足道的工作量,当前的数据结构并不是真正为此而设计的。 所以要支持这样的事情需要一些重大的工程。

@mheevery与在模板中从父级应用它们有何不同?

@k3nsei有必要从NgModule角度来看它,这实际上是为其所有组件创建基础架构的关键元素。

@mlc-mlapis 我们有@HostBinding@HostListener,所以@HostDirective可能是该功能的不错选择。 我已经看到 Ivy apis 启用此类功能的谈话。

对我来说,关键点是拥有一些组合 API,这将使我们能够拥有更多解耦类,并且能够拥有具有可重用功能的扩展/特征。 例如可选择,可展开/可折叠。

@k3nsei可能是,但我不确定它是否不是太动态,所以性能不如严格的静态结构。

“只是为了设定期望,你所要求的不是微不足道的工作量,当前的数据结构并不是真正为此而设计的。所以支持这样的事情需要一些重大的工程。” — https://github.com/angular/angular/issues/8785#issuecomment -654391378

感谢您及时回复@mheevery。

我想我将代表社区说,我们并没有完全失去这将是一个非常大的挑战。 如果不是这样,我相信现在我们会制作一些第三方库来正确地(以某种方式)实现这一目标。 [据我所知,没有]。

另外,不言而喻,请让我们知道是否有一些容易实现的成果(或其他方式),我们可以协助为此做出贡献。

我们真诚地感谢您,并重视您的认真沟通,并希望我们将继续参与对话,讨论我们需要什么,什么是现实/务实的补充。

虽然我的理解是常春藤使这比以前更容易。

@mhevery

@IgorMinar常春藤工作使这更可行。 但是是过去的 v6。

虽然我的理解是常春藤使这比以前更容易

我的新理解是“更容易”并不意味着“容易”

我的新理解是“更容易”并不意味着“容易”

Ivy:你花了两年时间做的事情仍然没有解决任何最流行的 Angular 问题。

Ivy:你花了两年时间做的事情仍然没有解决任何最流行的 Angular 问题。

@pauldraper我想我们的问题不是他们的“问题”,因为这个特定的问题甚至不在他们的关注范围内(参见路线图 https://angular.io/guide/roadmap)。

就我个人而言,我认为是时候去别处寻找一个不仅是开源的项目,而且是一个社区(和用户)对其方向有真正影响的项目。

@pauldraper我想我们的问题不是他们的“问题”,因为这个特定的问题甚至不在他们的关注范围内(参见路线图 https://angular.io/guide/roadmap)。

@somombo我很失望这个问题在这么多年后仍然存在,但我不能同意你的观点 路线图上的第一点明确是关于处理开放的 github 问题。 在路线图上列出所有这些没有多大意义,不是吗? 这个问题是最upvoted一个(或upvoted),我希望这将是最后采取的解决。

路线图上的第一点明确是关于处理开放的 github 问题。 在路线图上列出所有这些没有多大意义,不是吗? 这个问题是投票最多(或投票最多)的问题之一,我希望它最终能得到解决。

不,这只是一厢情愿,通过https://github.com/angular/angular/issues/5689阅读,绝对没有迹象表明他们想要解决任何最受好评的问题,除了未来

@pauldraper我想我们的问题不是他们的“问题”,因为这个特定的问题甚至不在他们的关注范围内(参见路线图 https://angular.io/guide/roadmap)。

@somombo我很失望这个问题在这么多年后仍然存在,但我不能同意你的观点 路线图上的第一点明确是关于处理开放的 github 问题。 在路线图上列出所有这些没有多大意义,不是吗? 这个问题是最upvoted一个(或upvoted),我希望这将是最后采取的解决。

都一样..我已经等不及了..这个问题对我来说是一个巨大的障碍。 因此,它似乎并没有被计划在短期内的事实意味着我个人是时候继续前进了。 祝其他人好运。

我希望将其重命名为“支持向指令添加指令”。 虽然该名称可能令人困惑,但我认为此功能适用于指令很重要,而不仅限于组件。 该功能的其他名称可能是“隐含指令”或“附加指令”,这意味着当您在模板中使用给定组件或指令时,它会在宿主元素上引入隐含/附加指令。

我多次想要这个,主要是因为与继承相比,组合可能是 Angular 中一种更干净的重用形式。 继承可能很笨重,因为没有多重继承,构造函数参数必须向下传递,一些 Angular 特性在继承时起作用,而其他特性必须在每个叶类中“重新连接”。

我想象“隐含/附加指令”像指令实例化的组件本地或指令本地形式一样工作,这导致指令在宿主元素上实例化,而不需要指令选择器存在于模板标记中。

下面是一个例子:

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

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

}

附加指令上的@Input()@Output()属性可以在模板中使用,附加指令上的生命周期方法应该作为宿主组件生命周期的一部分被调用。 附加指令也可以绑定到宿主元素。 基本上,它与今天的模板声明指令完全一样,但不需要在模板中声明。

我认为这个特性将为 Angular 带来显着的好处,通过指令组合实现更清晰/更简单的组件架构。

今天,如果您想要类似形式的指令重用,您有两种选择:

  • 要求在模板标记中始终一起声明一组相关的指令; 如果构造函数中不存在所需的指令,则检测并抛出错误。 没有办法要求在同一个元素上声明所需的指令。 从模板创作和文档的角度来看,这很混乱,并且由于冗余而不是强大的 API 或合同,但在其他方面并不可怕。
  • 在主机指令中手动实例化附加指令,并将构造函数参数、@Input/@ Output属性、主机绑定和生命周期方法转发到内部指令。 这对于组件作者来说是一团糟,但在一组简单的相关组件的情况下是可行的。 模板创作要好得多。

换句话说,该功能的缺失有时会在干净+简单的组件使用和干净+简单的组件创作之间产生不必要的权衡。

@johncrim
请注意,在现实世界中,您的自定义指令会有输入,您可能希望转换它们并作为输入传递给附加指令。 也许这可以通过类似于 Directive 装饰器选项中的host属性的语法来完成。

@amakhrov :好点 - 为了清楚起见,我从我的示例中排除了输入。 在我需要的大多数情况下,我不需要转换附加指令的输入(或输出) - 它们(理想情况下)充当可组合单元,并且它们的输入(或输出)值可以从模板绑定使用父指令(或组件)。

如果存在命名冲突或命名清晰度问题(我在设计组合指令时会尽量避免),或者需要转换输入或输出时,可以通过在父级中注入附加指令来轻松处理指令,并在委托给附加指令的父级上创建新的输入或输出属性。

我站着纠正。
这个问题现在列在官方路线图的“未来”部分。 请参阅https://angular.io/guide/roadmap#support -adding-directives-to-host-elements

支持向宿主元素添加指令

一个长期存在的功能请求是添加向宿主元素添加指令的能力。 该功能将允许开发人员在不使用继承的情况下用额外的行为来扩充他们自己的组件。 该项目将需要在 API 的定义、语义和实现方面付出大量努力。

因为我刚刚注意到它,我不确定它是什么时候添加的,但我承认这是一个好消息,一个有意义和令人放心的姿态。 我会继续交叉手指。

感谢团队把它放在那里! 🙏🏾

此页面是否有帮助?
0 / 5 - 0 等级