Angular2 - 应该在模板中访问私有变量吗?

Posted

技术标签:

【中文标题】Angular2 - 应该在模板中访问私有变量吗?【英文标题】:Angular2 - should private variables be accessible in the template? 【发布时间】:2016-04-07 01:31:00 【问题描述】:

如果在组件类上声明了变量private,我应该能够在该组件的模板中访问它吗?

@Component(
  selector: 'my-app',
  template: `
    <div>
      <h2>title</h2>
      <h2>Hello userName</h2> // I am getting this name
    </div>
  `,
)
export class App 
  public title = 'Angular 2';
  private userName = "Test Name"; //declared as private

【问题讨论】:

【参考方案1】:

编辑:这个答案现在不正确。我发布它时没有关于该主题的官方指导,但正如 @Yaroslov 的(优秀且正确)答案中所解释的,这不再是案例:Codelizer 现在会发出警告,并且 AoT 编译将在组件模板中引用私有变量时失败。也就是说,在概念层面上,这里的一切都仍然有效,所以我会留下这个答案,因为它似乎很有帮助。


是的,这是意料之中的。

请记住,private 和其他访问修饰符是 Typescript 构造,而 Component/controller/template 是 Typescript 一无所知的角度构造。访问修饰符控制 类之间的可见性:创建字段 private 可防止 其他类 访问它,但模板和控制器是存在于 的东西em> 类。

这在技术上并不正确,但是(代替理解类与装饰器及其元数据的关系),以这种方式考虑它可能会有所帮助,因为重要的事情(恕我直言)是从考虑模板和控制器作为单独的实体,将它们视为组件构造的统一部分——这是 ng2 心智模型的主要方面之一。

这样想,显然我们希望组件类上的private 变量在其模板中可见,出于同样的原因,我们希望它们在该类的private 方法中可见。

【讨论】:

首先,我的想法和你的drawmoore一样。但是我将 tslint 升级到 4.02 并将 codelyzer 升级到 2.0.0-beta.1 并且我有错误说我在访问视图中的变量时不能使用私有。所以@Yaroslav 的回答似乎更合适。 我同意组件模型不能看到它的私有变量是没有意义的,它们可能应该在编译过程中混入同一个类,我的意思是,你必须公开组件特定的特征、对象和功能到所有其他组件,以便您可以在模板中使用它们,更不用说外部调整或调用这些可能会导致成品组件出现潜在的意外行为 @drewmoore,您好,我已经编写 Angular 代码几个月了。我遇到了这个问题。是否有任何进一步的辩论?因为我没有找到任何关于遵循什么模式的具体信息。 imo,物有所值,它似乎违反了代码分离。 @drewmoore,我必须说我完全同意你的分析器逻辑。恐怕Angular团队有点搞砸了。在 AOT 模式下,他们不允许私人成员,而在他们声称的文档中,这在私人成员的情况下绝对加强了你的观点,只会给这个话题增加更多的混乱。来自 Docs:“Angular 将组件的模板视为属于该组件。组件及其模板隐含地相互信任。因此,组件自己的模板可以绑定到该组件的任何属性,无论是否带有 *@*Input 装饰器。 " @drewmoore,文档链接:angular.io/guide/attribute-directives#appendix-why-add-input(我知道它主要关注输入装饰器,但他们谈论的很多内容不仅仅与它相关)【参考方案2】:

尽管代码示例表明问题是关于 TypeScript,但它没有 typescript 标记。 Angular2 也可用于 Dart,这是与 Dart 的显着区别。

Dart中,模板不能引用组件类的私有变量,因为与TypeScript相比,Dart有效地阻止了来自外部的私有成员的访问。

我仍然支持@drewmoores 的建议,将组件和模板作为一个单元来考虑。

更新 (TS) 似乎在 Angular2 TS 以及 https://github.com/angular/angular/issues/11422987654322@

【讨论】:

是否可以让 Typescript 编译器限制视图可以访问私有变量? 我不知道。我猜不是。 我认为将它们设为私有可能会影响组件的可测试性是否正确?例如,如果我在测试的上下文中创建一个组件,我将无法从我的测试中调用这些私有方法来确认模板/类交互是否正常。我还没有尝试过,如果这很明显,请原谅我:) 在 Dart 中你不能在测试中访问私有成员。是否应该支持以及是否应该测试私有 API 有很多讨论(独立于语言)。测试公共 API 应该能够到达每个代码路径。我认为这总体上是合理的。在 Dart 中,私有是每个库(可以包含多个文件),这使得公共 API 非常广泛 - 恕我直言,对于单元测试来说太宽泛了。 可以测试私有函数,请查看此线程***.com/questions/56044471/…【参考方案3】:

不,您不应该在模板中使用私有变量。

虽然我喜欢 drewmoore's answer 并看到其中完美的概念逻辑,但在实现方面它是错误的。模板不存在于组件类中,而是存在于它们之外。看看this repo 的证明。

它起作用的唯一原因是 TypeScript 的 private 关键字并没有真正将成员设为私有。即时编译发生在运行时的浏览器中,并且 JS 没有任何私有成员的概念(还没有?)。感谢 Sander Elias 让我走上正轨。

使用ngc 和Ahead-of-Time 编译,如果您尝试从模板访问组件的私有成员,您会遇到错误。克隆演示库,将MyComponent 成员的可见性更改为私有,运行ngc 时会出现编译错误。这里还有 answer 专用于 Ahead-of-Time 编译。

【讨论】:

这是最好的评论,imo 应该是公认的答案。不是说你可以在编译后使用私有变量,你应该..保持代码干净! 这是唯一一个有效的答案!现在,当您在模板中使用私有 var 时,Codelyzer 会向您发出警告。 我唯一的问题是,您如何区分实际公开暴露的成员,例如 @Inputs 和 Outputs,以及我们只想向我们的模板而不是外部世界公开的成员。如果您正在构建可重用组件,您希望模板可以访问方法/成员,但其他组件不能访问。我认为原来的答案是正确的。模板是组件的一部分。 我同意@Ashg - 而不仅仅是w.r.t。输入和输出。当我想在 组件之间进行通信时,例如通过将父组件注入其子组件。然后,子组件可以看到父组件向其模板公开的所有内容,而不仅仅是父组件想要向外界公开的方法。在 Angular 的限制下,这个答案仍然是正确的,但我认为这个设计没有经过深思熟虑。 这是一个很好的答案,因为它解决了 Angular 的 AoT 编译中的限制以及如何解决这些限制。然而,IMO 这个问题是概念性的(有意或无意)。从概念上讲,模板是类定义的一部分。模板不扩展也不继承类,它们不从外部访问实例化的对象......这是相反的。模板是在类本身中定义的,因此从概念上讲,它们是类的一部分,并且从概念上讲应该可以访问私有成员。【参考方案4】:

私有变量可以在组件的模板中使用。参考 angular2 备忘单:https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-child-setter

关于 typescript 中类的公共/私有成员的更详细解释可以在这里找到:https://www.typescriptlang.org/docs/handbook/classes.html。

默认情况下所有成员都是公开的。公共成员可以与类实例一起从组件类外部访问。但是 Private 成员只能在类成员函数中访问。

【讨论】:

我查看了第一个链接 (angular.io/guide/component-interaction#!#parent-to-child-setter),我没有看到任何地方表明在模板中使用私有变量是可以的。相反,他们使用 getter 和 setter 从模板访问私有变量。【参考方案5】:

一种解决方法是在 ts 文件中使用私有变量并使用 getter。

private _userName = "Test Name";
get userName() 
  return this._userName;

这是一个很好的方法,因为 ts 文件和 html 保持独立。即使更改ts文件中的_userName变量名,也不必在模板文件中进行任何更改。

【讨论】:

我认为如果你将_userName改为_clientName,例如为了一致性,你需要更改getter来获取clientName...所以没有胜利 用户在私有变量下划线是一种不好的做法。 @FlorianLeitgeb 为什么the official Angular docs do it? private _name = ''; 那么这段代码 sn-p 没有被正确审查。它们遵循样式约定,该约定在样式指南here 中声明。而且在他们页面上的 Typescript classes 部分here 没有使用下划线。 @FlorianLeitgeb 那么如 ruffin 发布的链接中所示的拦截 setter 方法的建议解决方案是什么?即,您将 setter 的私有支持字段称为什么?【参考方案6】:

简短的回答是不,您不应该能够从模板访问私有成员,因为它在技术上与 TS 文件分离。

【讨论】:

【参考方案7】:

在 tsconfig.app.json 中,如果您在编译器选项中提供 'fullTemplateTypeCheck' 选项,您可以在项目构建时看到项目的 html 文件中的所有无效引用。

"angularCompilerOptions": 
"enableIvy": true,
"fullTemplateTypeCheck": true

【讨论】:

【参考方案8】:

我知道这有点晚了,但我从 Angular2 开始就一直在关注这个问题,最后我想出了一个很好的解决方法,让我有很好的适当组件 API 并且能够通过适当的类型检查从模板访问其中的私有字段:

export interface MyComponentPrivateProperties 
    _label: string;

export class MyComponent 
    private _label: string = 'Label';

    public get view(): MyComponentPrivateProperties 
        return this as any;
    

<div>view._label</div>

通过这种方式,如您所见,我们甚至可以在 html 模板中进行类型检查以及我们需要的所有内容以及组件的适当 API。如果在另一个组件中我们像这样引用MyComponent 类:

export class OtherComponent 
    private _m: MyComponent;

    ngOnInit() 
        // here, this._label is not visible.
    

我们会注意到属性_label 不可见。

当然,请注意MyComponent 类不应实现接口MyComponentPrivateProperties。此接口将没有实现。这只是对角度 ngc 编译器的描述。它在编译时告诉编译器哪些是可以从模板访问的私有属性,然后在运行时消失。

【讨论】:

以上是关于Angular2 - 应该在模板中访问私有变量吗?的主要内容,如果未能解决你的问题,请参考以下文章

我可以在类之外定义私有模板函数吗?

angular2 指令可以访问它所应用的组件的模板吗?

为啥我应该在属性访问器中使用私有变量?

Angular2从另一个组件访问变量

Angular2:如何将返回服务值放入私有变量对象中?

我们可以在打字稿中访问另一个类中的私有变量吗