父组件向子组件通信

    科技2022-07-31  92

    父组件向子组件通信

    The parent with dynamic children pattern is a common pattern in Angular for developing interdependent components with advanced features. Here I would like to explain with an example using Google map:

    具有动态子代的父模式是Angular中常见的模式,用于开发具有高级功能的相互依赖的组件。 在这里,我想用一个使用谷歌地图的例子来解释:

    <google-map [key]="apiKey"> <google-map-marker *ngFor="let marker of markers" [lat]="marker.lat" [lng]="marker.lng"> </google-map-marker></google-map>

    The <google-map> is the parent component, which initializes and renders the Google map. The <google-map-marker> is the child component that can be dynamically added to create markers on the map. Here is a guide about how you can use component injection to implement this kind of components.

    <google-map>是父组件,用于初始化和呈现Google地图。 <google-map-marker>是可以动态添加以在地图上创建标记的子组件。 这是有关如何使用组件注入来实现这种组件的指南。

    什么是成分注入? (What is component injection?)

    This is a very useful feature powered by the Angular dependency injection engine. It allows developers to inject the reference of any ancestor in the constructor through the engine.

    这是由Angular依赖项注入引擎提供支持的非常有用的功能。 它允许开发人员通过引擎将任何祖先的引用注入到构造函数中。

    @Component(...)export class DropDownComponent { constructor( parent: DropDownGroupComponent ) { ... } ...}

    With the help of this feature, child components can access the methods and state of their parents dynamically.

    借助此功能,子组件可以动态访问其父代的方法和状态。

    Google map API示例 (Example with Google map API)

    Here is a simple implementation of Google map API as an example. This is the parent <google-map> component:

    这里以Google map API的简单实现为例。 这是父<google-map>组件:

    @Component({ selector: 'google-map', template: ` <div #mapContainer style="height: 500px"></div> <div #content><ng-content></ng-content></div> `})export class GoogleMapComponent { @ViewChild('mapContainer', { static: false }) mapContainer: ElementRef; @ViewChild("content", { static: false }) contentWrapper: ElementRef; @Input() key: string; _isReady = false; _pending = []; google; map; ngAfterViewInit() { GoogleMapsApiLoader({ apiKey: this.key }).then(googleMapApi => { const mapContainer = this.mapContainer.nativeElement; const map = new googleMapApi.maps.Map(mapContainer, { zoom: 0, center: {lat: 0, lng: 0} }); this.onload(googleMapApi, map); }) } onload(google, map) { this.google = google; this.map = map; this._isReady = true; this._pending.forEach(resolve => { resolve() }); } isReady(): Promise<any> { return new Promise(resolve => { if (this._isReady) { resolve(); } else { this._pending.push(() => { resolve() }); } }) }}

    This component initializes Google map API and also renders the map in the view. As the API is loaded asynchronously, the isReady method is added such that child components can rely on it to wait for the initialization process to complete before taking any action.

    该组件会初始化Google Map API,并在视图中呈现地图。 由于API是异步加载的,因此添加了isReady方法,以便子组件可以依靠它来等待初始化过程完成,然后再执行任何操作。

    @Component({ selector: 'google-map-marker', template: ''})export class GoogleMapMarkerComponent { @Input() lat: number; @Input() lng: number; _markerObj; constructor(parent: GoogleMapComponent) { parent.isReady().then(() => { const google = parent.google; const map = parent.map; const Marker = google.maps.Marker; this._markerObj = new Marker({ position: { lat: this.lat, lng: this.lng }, map: map }); }); } ngOnDestroy() { if (this._markerObj) this._markerObj.setMap(null); }}

    The <google-map-marker> component is more straight forward. It simply injects the GoogleMapComponent when it is created and adds the marker to it.

    <google-map-marker>组件更为简单。 它只是在创建GoogleMapComponent时注入它,并向其中添加标记。

    奖励:使用“可选”注释可以更灵活地使用案例 (Bonus: Use the “optional” annotation for more flexible use cases)

    Component injection will declare a mandatory dependency by default. It means the child component will no longer be able to use alone.

    默认情况下,组件注入将声明强制性依赖关系。 这意味着子组件将不再能够单独使用。

    @Component(...)export class DropDownComponent { constructor( parent: DropDownGroupComponent ) { ... } ...}@Component({ selector: 'app-root', template: ` <drop-down-component></drop-down-component> `})export class RootComponent { ...}

    This will throw an injection exception as no DropDownGroupComponent ancestor can be found. In order to solve this, the optional annotation can be added to declare an optional dependency.

    因为找不到DropDownGroupComponent祖先,这将引发注入异常。 为了解决这个问题,可以添加optional注释以声明可选依赖项。

    @Component(...)export class DropDownComponent { constructor( @Optional() parent: DropDownGroupComponent ) { ... } ...}

    Now there won’t be exception anymore and parent will be null when there is no DropDownGroupComponent ancestor.

    现在将不再有异常,并且当没有DropDownGroupComponent祖先时, parent将为null。

    使用组件注入的替代方法 (Alternatives of using component injection)

    Component injection is the simplest built in approach for developing interdependent components. Yet, the major weakness of component injection is the strong coupling between the parent and children. It will be quite difficult to maintain when there is complicated logic. Here are two more approaches that work better for more complex components:

    组件注入是用于开发相互依赖的组件的最简单的内置方法。 然而,组分注射的主要缺点是父母与孩子之间的强耦合。 如果逻辑复杂,将很难维护。 这是另外两种对更复杂的组件更有效的方法:

    Thanks for reading! Hope you find this article helpful. Any comments would be highly appreciated. :D

    谢谢阅读! 希望本文对您有所帮助。 任何意见将不胜感激。 :D

    翻译自: https://medium.com/@liutingchun_95744/angular-using-component-injection-to-communicate-between-parent-and-dynamic-child-components-99c1c297aa43

    父组件向子组件通信

    Processed: 0.010, SQL: 8