繁体   English   中英

Angular 2:从装饰器访问注入的依赖项

[英]Angular 2: Accessing injected dependencies from decorator

我正在制作一个可重复使用的Angular2组件,我希望用户能够指定一个模板字符串或templateUrl,然后该组件将通过属性或通过某种服务方法设置该组件。

在Angular 1中,这很简单,我们可以这样做:

// somewhere else in app
myService.setTemplateUrl('path/to/template.html');

// directive definition
function myDirective(myService) {

  return {
    template: function(element, attrs) {
      return attrs.templateUrl || myService.getTemplateUrl();
    }
    // ...
  };

}

如何在Angular2中实现这一目标?

@Component({
  selector: 'my-component',
  template: '...' // cannot see `mySerivce` from here, nor access the element attributes
})
export class MyComponent {

  constructor(private myService: MyService) {}

}

虽然我的问题特别涉及如何实现动态模板,但更广泛的问题是是否可以从各种装饰器访问注入的依赖项实例。

所以我终于想出了一种方法来使用自定义模板做我想做的事情。

我认为实际问题的答案必须是否定的,注射器在装饰器中不可用 这是我对Angular 2组件生命周期的理解。

对于那些感兴趣的人,以下是我提出的用于实现用户定义的自定义模板的内容:

给定一个指令SimpleTimer ,我们可以提供这样的自定义模板:

<!-- app.ts template -->
<div *simpleTimer="#timer=timerApi">
  <div class="time">{{ timer.getTime() }}</div>
  <div class="controls">
    <button (click)="timer.toggle()">Toggle</button>
    <button (click)="timer.reset()">Reset</button>
  </div>
</div>

然后使用如下的TemplateRef和ViewContainerRef注射剂:

// SimpleTimer.ts
constructor(private templateRef: TemplateRef,
          private viewContainer: ViewContainerRef,
          private cdr: ChangeDetectorRef) {}

ngOnInit() {
  // we need to detach the change detector initially, to prevent a
  // "changed after checked" error.
  this.cdr.detach();
}

ngAfterViewInit() {
  let view = this.viewContainer.createEmbeddedView(this.templateRef);
  let api = {
    toggle: () => this.toggle(),
    reset: () => this.reset(),
    getTime: () => this.getTime()
  }
  view.setLocal('timerApi', api);

  setTimeout(() => this.cdr.reattach());
}

有关其工作原理和原因的演练, 请参阅我撰写的关于该主题的博客文章

编辑:我刚刚注意到你的意图是访问DI。 截至目前你不能因为他们开火得太晚了。 这个答案的其余部分是做你询问的模板。

我对这个问题非常感兴趣,所以我花了比我想象的更多的时间来研究这个问题。 从目前我所知道的,没有一种简单的方法可以做到这一点。

您有3个主要选择:

1.使用已知组件的* ng-if

这是迄今为止解决这个问题最简单的方法。 只有几个选项,您只能加载所需的组件

<special *ngIf="!type">Default</special>
<special *ngIf="type == 'awesome'"> I'm Awesome </special>
<special *ngIf="type == 'admin'">Admin Only</special>

优点:简单,模板语法。 缺点:必须知道类型,当很多选项时会很烦人

2.使用DynamicComponentLoader动态创建组件

这很快就会变得毛茸茸,非常先进。 您基本上调用基于传递的参数加载组件。 这将允许您定义模板值,然后传递它以创建新组件。

这是一篇很好的文章,可以学习如何开始使用它

这是一个使用这种确切方法的SO答案

这里有人用它来动态加载组件100% (超级hacky,与asyncRouter混淆)

优点:解决这个问题的“角度”方式,超级灵活。 缺点:如果你只需要一个简单的开关,那就非常有用了 没有多少人这样做,所以帮助不会那么容易。

3.作弊(走出Angular之外)毕竟只是javascript。 您可以创建一个类或对象,粘贴在window并调用自封装函数

template: (function() {
    return "<supertemplate-" + window.superTempId + "' />";
}())

(免责声明)我没有测试过这个,但似乎它会起作用

唯一的事情是什么时候。 这就是为什么你不能做其他服务,因为它们在完成元数据时不存在,但是如果你先设置模板或其他什么我不明白为什么它不起作用

优点:可以毫无困难地工作缺点:非常不是“角度方式”。 超级哈克。

这是一个相当常见的请求,所以我假设我们将通过“首选方法”或更多标准功能看到更多内容。

希望有所帮助!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM