【问题标题】:Chaining transclusion in nested components嵌套组件中的链接嵌入
【发布时间】:2016-12-20 16:52:32
【问题描述】:

我有以下嵌套组件的结构。

<app-root>
    <app-comp-1>
      <app-comp-2>
      </app-comp-2>
   </app-comp-1>
  <app-root>

我想将任何内容嵌入到最后一个子项 (app-comp-2) 中。所以,我需要这样的东西。

<app-comp-1>
    <app-comp-2>
        <ng-content></ng-content>
    </app-comp-2>
</app-comp-1>

但在 app-root 组件中只有 app-comp-1 组件可用。所以,这是我必须转换我的内容的地方。

<app-root>
    <app-comp-1>
        <content-I-want-to-transclude></content-I-want-to-transclude>
    </app-comp-1>
</app-root>
 ---------------------------
<app-comp-1>
    <ng-content></ng-content>
    <app-comp-2>
        ...
    </app-comp-2>
</app-comp-1>

所以我需要一个解决方案来获取已嵌入到第一个组件中的内容并将其传递给第二个组件。

Plunker

【问题讨论】:

    标签: angular


    【解决方案1】:

    This Github issue 使用ngProjectAs 属性为这个问题提供了一个很好的解决方案。

    在有 2 层内容投影的情况下,第一层使用带有 ngProjectAs 属性的 ng-content 元素,以及下一个选择器。

    第二层使用另一个ng-content,并选择第一层的ngProjectAs属性的值:

    级别 1(父组件):

    <ng-content select="my-component" ngProjectAs="arbitrary-selector"></ng-content>
    

    Level 2(嵌套子组件):

    <ng-content select="arbitrary-selector"></ng-content>
    

    用法:

    <my-app>
      <my-component>My Projected Content</my-component>
    </my-app>
    

    结果 DOM:

    <my-app>
      <my-component>
          <nested-component>My Projected Content</nested-component>
      </my-component>
    </my-app>
    

    【讨论】:

    • 感谢@hevans900,但我无法重现它。值得注意的是,由于根级别(在 ivy 之前)不支持嵌入,您如何设法让 my-app 根组件不替换其全部内容(无论其模板如何)?也许一个在线示例可以澄清这一点。
    • 抱歉,这只是一个伪代码示例——我实际上并没有用根(引导)组件尝试它——而且我看不出任何人想要顺便说一句的理由!
    【解决方案2】:

    我有一个类似的问题,我有一个卡片组件,它有一个子卡片头组件以及一个卡片主体的选择器。

    card-header 组件有一个切换按钮,用于调度打开/关闭卡片的操作。

    然后我需要能够通过卡片组件将额外的按钮从父组件传递到卡片头组件

    我通过在每个级别添加选择器来解决它。

    首先,我创建了一个通用的卡片头组件,允许我拥有一段代码,通过向 NgRx 存储发送操作来处理切换卡片内容,该存储包含一组隐藏的卡片(使用提供的名称输入属性)。

    toggled 状态发生变化时,card-header 组件订阅 store 并向父组件发出事件

    @Component({
      selector: 'po-card-header',
      template: `
        <div class="card-header">
    
          <span class="text-uppercase">{{ header }}</span>
    
          <div class="header-controls">
            <ng-content select=[card-header-option]></ng-content>
            <ng-content select=[header-option]></ng-content>
    
            <span class="header-action" (click)="onTogglePanel()">
             <i class="fa" [ngClass]="{ 'fa-caret-up': !collapsed, 'fa-caret-down': collapsed}"></i>
            </span>
    
          </div>
    
        </div>
      `
    })
    export class CardHeaderComponent implements OnInit, OnDestroy {
      ...
      @Input() name: string;
      @Output() togglePanel = new EventEmitter<boolean>();
    
      collapsed$: Observable<boolean>;
      collapsedSub: Subscription;
    
      constructor(private store: Store<State>) {
        this.collapsed$ = this.store.select(state => getPanelCollapsed(state, this.name);
      }
    
      ngOnInit(): void {
        this.collapsedSub = this.collapsed$.subscribe(collapsed => {
          this.collapsed = collapsed;
          this.togglePanel.emit(collapsed);
        });
      }
    
      .... unsubscribe on destroy.
    }
    

    注意标题有 2 个ng-content 部分。

    header-option 选择器适用于我在明确使用此组件时要添加的任何其他图标,例如

    <div class="card">
    
      <po-card-header>
        ...
        <span header-option class="fa fa-whatever" (click)="doSomething()"></span>
      </po-card-header>
    
      ...
    
    </div>
    

    我的新图标将位于标题中的默认切换图标旁边。

    第二个card-header-option 选择器用于根组件,它使用卡片组件,而不是卡片标题组件,但仍希望将额外的图标传递到标题中。

    @Component({
      selector: 'po-card',
      template: `
    
        <div class="card">
          <po-card-header>
            ...
            <ng-content select="[card-header-option] header-option></ng-content>
          </po-card-header>
    
          <div class="card-block">
            <ng-content select="[card-body]"></ng-content>
          </div>
    
        </div>
      `
    })
    ...
    

    [card-header-option] 选择器将选择具有该属性的任何元素,然后使用 header-option 属性将它们传递到卡片标题组件

    我的卡片组件最终的用法是这样的。

    <div>
       Some component that uses the card
       <po-card 
         header="Some text to go in the card header" 
         name="a-unique-name-for-the-card">
    
         <span card-header-option class='fa fa-blah header-action'></span>
    
         <div card-body>
           Some content that gets put inside the [card-body] selector on the card component.
         </div>
    
       </po-card>
    </div>
    

    最终的结果是我可以使用我的自定义卡片组件,并获得卡片标题组件提供的切换功能的好处,还可以提供我自己的自定义操作,这些操作也会在标题中呈现

    希望你觉得这对你有帮助:-)

    【讨论】:

      【解决方案3】:

      根元素不支持Transclusion,请参阅this

      但是你可以在所有组件中添加 ng-content 以传递内容。

      @Component({
        selector: 'my-app',
        template: `
        <app-comp-1>
          <app-comp-2>
            <app-comp-3>
               Hello World
            </app-comp-3>
          </app-comp-2>
        </app-comp-1>
        `
      })
      export class AppComponent { name = 'Angular'; }
      
      @Component({
        selector: 'app-comp-1',
        template: `
         app-comp-1
         <ng-content></ng-content>
        `
      })
      export class AppComponent1 {  }
      
      @Component({
        selector: 'app-comp-2',
        template: `
         app-comp-2
         <ng-content></ng-content>
        `
      })
      export class AppComponent2 {  }
      
      @Component({
        selector: 'app-comp-3',
        template: `
         app-comp-3
         <ng-content></ng-content>
        `
      })
      export class AppComponent3 {  }
      

      看到这个Plunker

      【讨论】:

      • 感谢您的支持!但是我必须处理“Hello world”仅在 app-comp-1 中可用的情况,我想从那里获取它并将其传递给 app-comp-3。内容必须仅在第一个组件中可用
      • 你能用你的用例 fork 和更新 plunker 并将其添加到问题中吗?
      • 不幸的是,plunker 似乎不再工作了。
      猜你喜欢
      • 2013-09-16
      • 2017-12-31
      • 2020-02-17
      • 2014-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多