在被包含的组件中使用父范围/方法/属性

时间:2018-06-27 10:53:49

标签: angular scope ng-content

我尝试使用transclusion n angular6,但无法使示波器变直。

<component-smart1
  [someObject]="myObject"
  [someService]="saidService">

  <component-dumb-1
    [specificObject]="getSpecificObject()"></component-dumb-1>

  <component-dumb-2
    [specificObject]="getSpecificObject()"
    (someEvent)="handleEvent()"></component-dumb-2>

</component-smart1>

现在,我希望愚蠢的组件(component-dumb-1component-dumb-2)使用智能组件(component-smart1)的作用域/方法/属性。

我的目标是能够使用智能组件中的相同方法,在换位中使用不同组件来组成不同的变体。 例如:

<component-smart1
  [someObject]="myObject"
  [someService]="saidService">

  <component-dumb-1
    [specificObject]="getSpecificObject()"></component-dumb-1>

  <component-dumb-2
    [specificObject]="getSpecificObject()"
    (someEvent)="handleEvent()"></component-dumb-2>

  <component-dumb-3
    [specificObject]="getSpecificObject()"
    (someOtherEvent)="handleOtherEvent()"></component-dumb-3>

</component-smart1>

这可能吗?

2 个答案:

答案 0 :(得分:1)

嗨,我想您正在尝试制作一种通用的smart-component,并希望其子级(通过ng-content)能够访问其范围,而不是您编写此模板时所使用的组件范围。我认为这是不可能的,并且对Transclusion所做的事情有些误解。

智能组件通常是非常特定的组件,可以将它们绑定到特定的路线和内容。如果不是这种情况,那么您最终将不得不在其中放置很多沉重的东西(例如注入的服务),而这在每种情况下都是不需要的。

如果设计得当,则包含可以帮助构成哑组件。因此,它们应该尽可能通用或灵活。

答案 1 :(得分:1)

首先,在Angular> = 2.x中,不再存在作用域。组件模板的执行上下文始终是其组件类。但是,parent => childchild => parent之间有几种通信方式。

第一种方法是使用@ContentChildren装饰器,该装饰器将返回QueryList。这要求您添加模板引用变量,例如#myChild。这就是您的模板的样子:

<component-smart1>
  <component-dumb-1 #myDumbChild></component-dumb-1>
  <component-dumb-2 #myDumbChild></component-dumb-2>
</component-smart1>

使用@ContentChildren装饰器,您可以查询这些参考变量并访问哑组件公共API。

另一种方法是利用DI系统的功能。例如,您可以在子组件级别配置提供程序,将抽象类用作注入令牌并用于策略useExisting。这将允许您查询一个令牌以获取特定类型的所有内容子级。这是一个示例:

abstract class MyDumbComponent {
  // some properties here
  // some methods here
};

@Component({
  selector: 'my-dumb-component',
  providers: [
    { provide: MyDumbComponent, useExisting: DumbComponentA }
  ]
})
export class DumbComponentA implements MyDumbComponent {
  ...
}

请注意,我在这里使用了一个抽象类作为令牌,因为接口在转换后将消失,其次,我想为具有相同方法的组件定义一些通用的“接口”。

然后您可以在父组件中像这样查询它们:

@Component({
  ...
})
export class ParentSmartComponent { }
  @ContentChildren(MyDumbComponent) myDumbComponents: QueryList<MyDumbComponent>;

  ngAfterContentInit() {
    // here you'll have access to your dumb components
  }
}

第三种首选方法是使用@Output。尽管上面的方法在某些情况下可以正常工作,但是如果我对您的理解是正确的,则希望从子组件与父组件进行通信。上面所有这些都将父组件放到了驾驶员座位上,这意味着它们并没有真正列出某些事件,而是可以访问子组件的API。另一方面,@Output允许孩子通知其父母发生了事情。然后,父级可以侦听此事件并执行一些方法或任务。

如上所述,在大多数情况下,这是子组件与父组件之间进行通信的首选方式,因为它不会将子组件与父组件紧密耦合,反之亦然。愚蠢的组件仍然非常可重用,并且只向外部分发一些事件,然后父组件可以侦听并采取相应的行动。

这也使您可以根据自己的喜好来编写组件。您可以在智能组件中使用任何哑组件。这里唯一的要求是它们发出事件以通知其父项。

为了完整起见,您还可以将智能组件注入到哑组件中。看起来像这样:

@Component({
  ...
})
export class MyDumbComponent {
  constructor(private smartComponent: ParentSmartComponent) { }
}

但是,我也不推荐这种方法,因为它再次将笨拙的组件与智能组件紧密耦合。正如@ n​​-sokolowski已经提到的那样,内容投影可以用作合成的手段,并且所组成的组件应尽可能地可重用和通用。

总而言之,只需在哑组件内部使用@Output,然后发出父组件可以监听的特定事件即可。