Angular:使用 @ContentChildren 让子组件进入另一个组件

2024-05-09

我在 Angular 6 中创建了一个自定义轮播组件。简单用法如下:

<mpx-carousel>
  <div *mpxCarouselItem>Slide 1</div>
  <div *mpxCarouselItem>Slide 2</div>
  <div *mpxCarouselItem>Slide 3</div>   
</mpx-carousel>

但它也支持嵌套,如下所示:

<mpx-carousel>
  <div *mpxCarouselItem>Slide 1</div>
  <div *mpxCarouselItem>
    <mpx-carousel>
      <div *mpxCarouselItem>Sub-slide A</div>
      <div *mpxCarouselItem>Sub-slide B</div>
    </mpx-carousel>
  </div>
  <div *mpxCarouselItem>Slide 3</div>   
</mpx-carousel>

在父级中CarouselComponent代码,我想确定是否有孩子CarouselComponents,并访问它们的属性。所以我使用@ContentChildren:

@ContentChildren(CarouselComponent, { descendants: true }) 
childCarousels: QueryList<CarouselComponent>;

ngAfterContentInit() {
    console.log(this.childCarousels.length); // 1, BUT it's a reference to itself, not the child
}

在父轮播的 ngAfterContentInit 中,我看到 @ContentChildren 找到了 1 个子 CarouselComponent,这看起来不错。但仔细检查发现,它实际上找到了这个父轮播本身,而不是它的子轮播。为了真正找到子轮播,我必须订阅 childCarousel.changes:

@ContentChildren(CarouselComponent, { descendants: true }) 
childCarousels: QueryList<CarouselComponent>;

ngAfterContentInit() {
    console.log(this.childCarousels.length); // 1, BUT it's a reference to itself, not the child

    this.childCarousels.changes.subscribe(() => {
        console.log(this.childCarousels.length); // 2, now it includes itself and its child
    });
}

因此,@ContentChildren 包含父级本身有点奇怪,而且您必须等待changes在检测到孩子之前发生事件,但这是一个足够简单的解决方法。

当子轮播出现在另一个组件内时,真正的麻烦就开始了。这CarouselComponent是项目中许多其他组件使用的共享组件,所以我经常可以像这样使用它......

<!-- parent carousel -->
<mpx-carousel>
  <mpx-component-A></mpx-component-A>
  <mpx-component-B></mpx-component-B>
</mpx-carousel>

...在哪里<mpx-component-A>是另一个在内部使用自己的 mpx-carousel 的组件。我仍然希望这里的父轮播能够检测 mpx-component-A 视图内使用的轮播。但是当我使用上面的技术时,@ContentChildren 找不到 ComponentA 中定义的 CarouselComponent 实例。在 ngAfterContentInit 中,childCarousels 再次只包含一个项目,它是对父级本身的引用。在这种情况下, childCarousels.changes 甚至永远不会触发,所以我们也不会在那里得到埋藏的子旋转木马:

@ContentChildren(CarouselComponent, { descendants: true }) 
childCarousels: QueryList<CarouselComponent>;

ngAfterContentInit() {
    console.log(this.childCarousels.length); // 1, BUT it's a reference to itself, not the child

    // The changes event never even fires
    this.childCarousels.changes.subscribe(() => {
        console.log(this.childCarousels.length); // N/A
    });
}

我可以提供完整的代码CarouselComponent and CarouselItemDirective如果有帮助的话。但我的问题更普遍:有没有办法让父组件获取对包含的某种类型(轮播组件)的后代组件的引用within另一个组件(ComponentS)?

我使用@ContentChildren可能完全搞错了树,所以我愿意接受完全不同的方法。

提前致谢!

EDIT: 这是一个 StackBlitz https://stackblitz.com/edit/angular-omiku4这让我更清楚我想要做什么。注意console.log()语句carousel.component.ts以及显示预期输出与实际输出的评论。 (注意:在现实世界中,旋转木马每隔几秒就会以动画方式旋转其项目。但为了简单起见,我在这里删除了所有这些)。


这是一个尽管如此,据我所做的研究,没有自动神奇的方法来查询另一个组件内类型的所有嵌套组件。

内容儿童:
不检索其他组件模板中的元素或指令,因为组件的模板对于其祖先来说始终是一个黑匣子。

引用自角度文档 https://angular.io/api/core/ContentChildren

过去我有类似的用例,我的解决方案是为嵌套的所有组件部分创建一个通用接口

export interface CommonQueryInterface {
  commonField: QueryList<any>
}

所以逻辑是在最顶层(最高的父组件)有一个遍历所有具有字段的子组件的逻辑commonField在他们里面。但我认为这种方法与您想要的结果不同。

因此,在我看来,为了访问任何轮播中的所有轮播组件,可以创建一个轮播服务来管理这些轮播元素

该服务可能如下所示:

@Injectable({ providedIn: "root" })
export class CarouselService {
  carousells = {};

  addCarousel(c: CarouselComponent) {
    if (!this.carousells[c.groupName]) {
      this.carousells[c.groupName] = [];
    }
    this.carousells[c.groupName].push(c);
  }

  getCarousels() {
    return this.carousells;
  }

  clear(c: CarouselComponent) {
    if (c.topLevel) {
      delete this.carousells[c.groupName];
    }
  }
}

我的解决方案建议在轮播组件内将轮播元素本身添加到服务中

export class CarouselComponent implements AfterContentInit, OnDestroy {
  @ContentChildren(CarouselItemDirective) items: QueryList<
    CarouselItemDirective
  >;
  @ContentChildren(CarouselComponent, { descendants: true })
  childCarousels: QueryList<CarouselComponent>;
  @Input() name;
  @Input() groupName;
  @Input() topLevel = false;

  @ViewChildren(CarouselComponent) childCarousels2: QueryList<
    CarouselComponent
  >;

  constructor(private cs: CarouselService) {}

  getActiveCarousels() {
    return this.cs;
  }

  ngAfterContentInit() {
    if (this.name) {
      this.cs.addCarousel(this);
    }
  }

  ngOnDestroy(){
      this.cs.clear(this)
  }

  queryCarousells() {
    return this.cs.getCarousels();
  }
}

完成此操作后,您将可以随时访问活动的(屏幕上)轮播。这种方法的缺点是你需要除了name @Input多一个组名@Input。这是我的实现中所需要的,以便我们可以在同一视图中区分不同的轮播组件。

这是该概念的基本实现堆栈闪电战 https://stackblitz.com/edit/carousel-manager?file=src%2Fapp%2Fcarousel%2Fcarousel.component.ts

此实现的好处是,通过访问所有轮播,您可以在轮播组件本身内部添加与轮播相关的自定义逻辑,并且它将被锁定,远离组件的使用者。 例如,这样的逻辑可能是重新启动单个轮播组内的所有轮播。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Angular:使用 @ContentChildren 让子组件进入另一个组件 的相关文章

  • 提供常量

    Provide 在 RC4 中已被弃用 以前 我可以这样做 provide API URL useValue address 我正在尝试这样的事情 provide API URL useValue address 但这不起作用 我找到了几个
  • Firestore从集合中获取文档ID

    我正在尝试使用 id 检索我的文档 但无法弄清楚 目前我像这样检索我的文档 const racesCollection AngularFirestoreCollection
  • 如何在 *ngFor 中停止属性绑定到 Angular 中的每个元素

    在我的 html 中 我想将属性绑定应用于每个元素 我有一个点击和悬停事件 每当用户 将鼠标悬停或单击单个元素 但现在悬停或 单击发生在其中的每个元素上 ngFor 我想要它只 发生在他们选择 悬停的元素上 我需要什么 改变 我看到了另一个
  • Angular2 - Keyup 需要澄清

    在我的应用程序中 我有条件地添加一个类 当用户输入某些内容时 我正在检查该值 并相应地添加类名称 效果很好 但它只更新一组 keyup 0 设置一些值keyup 这不像angular 1 here 所以有人解释一下为什么我们要设置 keyu
  • 在 MatDialog Content Angular 7 中添加新行

    我正在使用 MatDialog 并尝试在内容定义中添加新行 两个都 n and 没有这样做 有没有另一种方法无需手动进入 html 并更改它 因为它是可重用的组件 var status MatDialogRef
  • 更改 mat-form-field 中的边框颜色

    我正在使用有角度的材料mat form field 我有深色背景 因此尝试将表单字段的边框更改为白色 但我无法使用 css 来实现它 不 无论我在 css 中进行什么更改 它们都不会反映到mat form field 这是我的代码的链接 S
  • 从字符串变量导入模块

    我需要从内存变量导入 JavaScript 模块 我知道这可以使用SystemJS and Webpack 但我找不到一个好的工作示例或文档 文档主要讨论 js 文件的动态导入 基本上我需要像下面这样导入模块 let moduleData
  • Angular 2:如何从组件控制

    这个问题在这里已经有答案了 我尝试使用 Angular 2 但停止处理 HTML 5 视频 在手册中我发现 可以在模板中使用
  • Angular 5 - 谷歌未定义(谷歌地图)

    我想在我的 Angular 5 应用程序上使用谷歌地图 但遇到了一些问题 加载视图时 我在 js 控制台中收到错误 LoginComponent Host ngfactory js sm 1 ERROR ReferenceError goo
  • 方法与管道

    在 Angular 应用程序中的模板插值中使用管道和方法有区别吗 例如 h1 name toLowerCase h1 vs h1 name lowercase h1 就性能而言 是有真正的收获还是只是个人喜好 我知道调用模板中的方法通常会降
  • 选中复选框时,角度反应形式禁用输入

    我有一个复选框和输入 我想要做的是在选中复选框时禁用输入 并在取消选中复选框时启用输入 这是我的表格组 this workingTime this fb group toggle false this is the checkbox fro
  • 将 WAR 部署到 Tomcat(Spring Boot + Angular)

    我正在尝试使用以下命令部署 Spring Boot 应用程序WAR包装至Tomcat 10 应用程序已成功部署 但是 当我尝试访问端点时 它会导致404 未找到 战争文件 应用程序 war http localhost 8080 appli
  • Angular 4 触发自定义事件 - EventEmitter 与dispatchEvent()

    我正在构建指令 该指令应该在元素进入视口时添加类 并且还将触发自定义事件 我找到了两种触发事件的方法 EventEmitterand dispatchEvent 两者都工作正常 在这种情况下应该使用哪个 为什么 对代码的任何其他建议表示赞赏
  • 绑定到元素(div)的大小

    我有一个具有宽度和高度属性的组件 如何将组件的视图高度和宽度绑定到这些属性 我需要在组件大小发生变化时更新它们 即通过重新调整浏览器窗口的大小 Use window resize onResize event 聆听全球事件 您可以使用win
  • 在 ng 服务命令上找不到构建器 @angular-devkit/build-angular:dev-server 的实现[重复]

    这个问题在这里已经有答案了 我尝试更新以下 Angular CLIthis https www npmjs com package angular cli updating angular cli 但现在我无法运行我的应用程序 当我尝试运行
  • Angular6 材料垫选择列表中当前选择的值

    使用角度材质2垫选择列表 能够判断当前选项是选中还是未选中 Boolean 组件 html
  • Angular 4 Http POST 不起作用

    我希望每个人都做得很好 我最近开始使用 Angular 4 4 我一直在尝试将数据发布到我的 api 服务器 但不幸的是它不起作用 我花了大约两天的时间 但仍然没有成功 甚至已经尝试过 6 7 篇文章角 io https angular i
  • 使用 rxjs 将数据添加到 http 响应

    我有一个包含司机 ID 的旅行实体 我可以使用 RESTFull 端点获取获取行程 例如 trips 2 example response id 2 driver id 123 我可以使用端点获取驱动程序详细信息 例如 drivers 12
  • 找不到管道“货币”(AOT)

    我们有一个从 Angular 6 升级到 7 的 Angular 我们正在使用内置的currency管道 服务时一切正常 ng serve 以及在 DEV 中构建时 但是当我们尝试构建生产我们正在得到The pipe currency co
  • Angular:升级到版本 5 错误:@angular/compiler-cli 的版本需要为 2.3.1 或更高版本。当前版本是“5.0.0”

    免责声明 潜在的的重复项this https stackoverflow com questions 47104188 migrating angular 4 x to angular 5 但是详细问题并没有显示有问题的具体错误消息 因此它

随机推荐