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

时间:2015-12-17 09:03:50

标签: angular

我正在制作一个可重复使用的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) {}

}

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

2 个答案:

答案 0 :(得分:3)

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

我认为实际问题的答案必须是否,注射器在装饰器中不可用。这是我对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());
}

要了解其工作原理和原因,please see this blog post I wrote up on the topic

答案 1 :(得分:1)

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

我对这个问题非常感兴趣所以我花了比我想象的更多的时间来研究这个问题。 据我所知,目前还没有 EASY 方式来做到这一点。

您有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

动态创建组件

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

This is a good article to learn how to start using it

Here is a S.O. Answer that uses this exact method

Here is someone using it to load components 100% dynamically(超级hacky,与asyncRouter混淆)

优点:&#34; Angular&#34;解决这个问题的方法,超级灵活。 缺点:如果你只是需要一个简单的开关,那就非常复杂。没有多少人这样做,所以帮助不会那么容易。

第3。作弊(走出Angular之外) 毕竟它只是javascript。您可以创建一个您坚持window并调用自封装函数

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

(免责声明)我没有对此进行过测试,但似乎可以正常使用

唯一的事情是 WHEN。这就是为什么你不能做其他服务,因为他们在元数据完成时不存在,但如果你设置你的模板或其他任何我不知道为什么它不会工作

优点:可以毫无困难地工作 缺点:非常不是&#34; Angular Way。&#34;超级哈克。

这是一个相当常见的请求,因此我假设我们将使用&#34;首选方法&#34;或更多标准功能..

希望有所帮助!