Angular 动态组件-createComponent-OnChanges

Angular 动态组件-createComponent-OnChanges,angular,Angular,我正在通过createComponent方法创建一个动态组件,我在让我的child组件更新其input值时遇到了问题,该值是通过component.instance.prop=somevalue方法从parent传递给它的,但是,当我更新父对象中的值时,子对象没有更新其引用 父组件: import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver, AfterContentInit } fro

我正在通过
createComponent
方法创建一个动态组件,我在让我的
child
组件更新其
input
值时遇到了问题,该值是通过
component.instance.prop=somevalue
方法从
parent
传递给它的,但是,当我更新父对象中的值时,子对象没有更新其引用

父组件:

import {
  Component,
  ViewChild,
  ViewContainerRef,
  ComponentFactoryResolver,
  AfterContentInit
} from '@angular/core';

import { ChildComponent } from '../child/child.component';

@Component({
  selector: 'parent-component',
  template: `
    <div>
      <input type="text" (keyup)="name = $event.target.value">
      <span>{{ name }}</span>
    </div>
    <ng-container #container></ng-container>
  `,
  styles: []
})
export class ParentComponent implements AfterContentInit {
  @ViewChild('container', { read: ViewContainerRef}) container: ViewContainerRef;
  private _name = 'John Doe';
  get name() {
    return this._name;
  }
  set name(name: string) {
    this._name = name;
  }
  constructor(private resolver: ComponentFactoryResolver) { }

  ngAfterContentInit() {
    let factory = this.resolver.resolveComponentFactory(ChildComponent);
    let component = this.container.createComponent(factory);
    component.instance.name = this.name;
  }
}
import {
  Component,
  OnInit,
  Input,
  OnChanges,
  SimpleChanges
} from '@angular/core';

@Component({
  selector: 'child-component',
  template: `
    <div>
      {{ name }}
    </div>
  `,
  styles: []
})
export class ChildComponent implements OnChanges {
  _name: string;
  get name() {
    return this._name;
  }
  set name(name: string) {
    this._name = name;
  }
  constructor() { }
  ngOnChanges(changes: SimpleChanges) {
    console.log('onchanges ', changes);
    this._name = changes.name.currentValue;
  }
}
导入{
组成部分,
ViewChild,
ViewContainerRef,
组件工厂分解器,
AfterContentInit
}从“@angular/core”开始;
从“../child/child.component”导入{ChildComponent};
@组成部分({
选择器:“父组件”,
模板:`
{{name}}
`,
样式:[]
})
导出类ParentComponent实现AfterContentInit{
@ViewChild('container',{read:ViewContainerRef})container:ViewContainerRef;
私人_name='johndoe';
获取名称(){
返回此。\u名称;
}
集合名称(名称:字符串){
这个。_name=name;
}
构造函数(专用冲突解决程序:ComponentFactoryResolver){}
ngAfterContentInit(){
让工厂=this.resolver.resolveComponentFactory(ChildComponent);
让component=this.container.createComponent(工厂);
component.instance.name=this.name;
}
}
子组件:

import {
  Component,
  ViewChild,
  ViewContainerRef,
  ComponentFactoryResolver,
  AfterContentInit
} from '@angular/core';

import { ChildComponent } from '../child/child.component';

@Component({
  selector: 'parent-component',
  template: `
    <div>
      <input type="text" (keyup)="name = $event.target.value">
      <span>{{ name }}</span>
    </div>
    <ng-container #container></ng-container>
  `,
  styles: []
})
export class ParentComponent implements AfterContentInit {
  @ViewChild('container', { read: ViewContainerRef}) container: ViewContainerRef;
  private _name = 'John Doe';
  get name() {
    return this._name;
  }
  set name(name: string) {
    this._name = name;
  }
  constructor(private resolver: ComponentFactoryResolver) { }

  ngAfterContentInit() {
    let factory = this.resolver.resolveComponentFactory(ChildComponent);
    let component = this.container.createComponent(factory);
    component.instance.name = this.name;
  }
}
import {
  Component,
  OnInit,
  Input,
  OnChanges,
  SimpleChanges
} from '@angular/core';

@Component({
  selector: 'child-component',
  template: `
    <div>
      {{ name }}
    </div>
  `,
  styles: []
})
export class ChildComponent implements OnChanges {
  _name: string;
  get name() {
    return this._name;
  }
  set name(name: string) {
    this._name = name;
  }
  constructor() { }
  ngOnChanges(changes: SimpleChanges) {
    console.log('onchanges ', changes);
    this._name = changes.name.currentValue;
  }
}
导入{
组成部分,
奥尼特,
输入,
一旦改变,
简单变化
}从“@angular/core”开始;
@组成部分({
选择器:'子组件',
模板:`
{{name}}
`,
样式:[]
})
导出类ChildComponent实现OnChanges{
_名称:字符串;
获取名称(){
返回此。\u名称;
}
集合名称(名称:字符串){
这个。_name=name;
}
构造函数(){}
ngOnChanges(更改:SimpleChanges){
log('onchanges',changes);
此._name=changes.name.currentValue;
}
}

问题:如何获得通过
createComponent()
方法创建的动态
子组件,以在
父组件中的值更改时更新其值?

您可以在父组件中执行此操作。stackblitz中的示例

template: `
<div>
  <input type="text" (keyup)="onKeyUp($event)">
  <span>{{ name }}</span>
</div>
<ng-container #container></ng-container>
`,

childComponent: ChildComponent;

ngAfterContentInit() {
  let factory = this.resolver.resolveComponentFactory(ChildComponent);
  let component = this.container.createComponent(factory);
  this.childComponent = component.instance;
  this.childComponent.name = this.name;
}

onKeyUp($event) {
  const changes = {
    name: new SimpleChange(this.name, $event.target.value, false)
  }

  this.name = $event.target.value;
  this.childComponent.ngOnChanges(changes);
}
模板:`
{{name}}
`,
childComponent:childComponent;
ngAfterContentInit(){
让工厂=this.resolver.resolveComponentFactory(ChildComponent);
让component=this.container.createComponent(工厂);
this.childComponent=component.instance;
this.childComponent.name=this.name;
}
onKeyUp($event){
常数变化={
名称:新SimpleChange(this.name$event.target.value,false)
}
this.name=$event.target.value;
this.childComponent.ngOnChanges(更改);
}

只需通过
component.instance.name=this.name重新分配您姓名上的每一项更改即可。为此,在每个
(keyup)
事件上实现一个处理程序函数:

@组件({
选择器:“父组件”,
模板:`
{{name}}
`,
样式:[]
})
导出类ParentComponent实现AfterContentInit{
@ViewChild('container',{read:ViewContainerRef})container:ViewContainerRef;
私有部分;
私人_name='johndoe';
获取名称(){
返回此。\u名称;
}
集合名称(名称:字符串){
这个。_name=name;
}
构造函数(专用冲突解决程序:ComponentFactoryResolver){}
onNameChange(事件){
this.name=event.target.value;
this.component.instance.name=this.name;
}
ngAfterContentInit(){
让工厂=this.resolver.resolveComponentFactory(ChildComponent);
this.component=this.container.createComponent(工厂);
this.component.instance.name=this.name;
}

}
如果您创建了一个服务,该服务具有每次值更改时都会发出的可观测数据,并且在动态组件的onInit上预订了该服务上的可观测数据,那么您从可观测数据中获得的数据将分配给您的组件属性。。。我用过类似的东西,看起来很有效

下面是我注入CarouselService的parentComponent:


    @Component({
      selector: 'app-carousel',
      templateUrl: './carousel.component.html',
      styleUrls: ['./carousel.component.scss'],
      providers: [ CarouselService]
    })
    export class CarouselComponent implements OnInit, AfterViewInit, AfterContentInit {

      @ViewChild('entryForSlides', { read: ViewContainerRef }) entryForSlides: ViewContainerRef;

      @Input() carouselSlides: Array<CarouselSlide>;
      @Input() hasPersistanceService: boolean;
      @Output() noSlidesRemaining: EventEmitter<boolean> = new EventEmitter(false);

      removedSlideToggle = false;

      carrouselInstance: any;

      activeIndex = 0;


      carouselSlideFactory: ComponentFactory<CarouselSlideComponent>;

      constructor( private _resolver: ComponentFactoryResolver, private _carouselService: CarouselService) {
        this.carouselSlideFactory = this._resolver.resolveComponentFactory(CarouselSlideComponent);
      }

      ngOnInit() { }

      ngAfterViewInit(): void {

        this.carrouselInstance = new Swiper ('.swiper-container', {
          init: false,
          // loop: true,
          spaceBetween: 30,
          // speed: 5000,
          pagination: {
            el: '.swiper-pagination',
          },
          // Navigation arrows
          navigation: {
            nextEl: '.swiper-button-nextSlide',
            prevEl: '.swiper-button-previousSlide',
          }
        });

        this.carrouselInstance.on('slideChangeTransitionEnd', () => {
          this.activeIndex = this.carrouselInstance.realIndex;
          this._carouselService.updateIndex(this.activeIndex);
        });

        this.carrouselInstance.init();
      }

      ngAfterContentInit(): void {
        this.generateSlides();
      }

      clickOnCross() {
        this._carouselService.updateIndex(this.activeIndex);
        this.entryForSlides.clear();
        this.carouselSlides.splice(this.carrouselInstance.realIndex, 1);
        this.generateSlides();

        // Timeout to update carousel with the new DOM slides (little hack while a better solution is found): DO NOT REMOVE
        setTimeout(() => {
          this.carrouselInstance.update();
        }, 1);
        if (this.carouselSlides.length <= 0 ) {
          this.noSlidesRemaining.emit();
        }
      }

      generateSlides() {
        this.carouselSlides.forEach((element, index) => {
          const component = this.entryForSlides.createComponent(this.carouselSlideFactory);
          component.instance.carouselSlide = element;
          component.instance.numberOfIndex = index;
          component.instance.activeSlide = this.activeIndex;
        });
      }
    }


    @Injectable({
      providedIn: 'root'
    })
    export class CarouselService {

      currentIndex = new Subject<number>();

      constructor() { }

      updateIndex(newIndex: number) {
        this.currentIndex.next(newIndex);
      }
    }

因此,当我从parentComponent执行clickOnCross函数时,我需要通过调用更新activeSlide并向所有动态注入组件发送值的服务上的函数来更新动态注入组件上的输入activeSlide。以下是转盘服务的代码:


    @Component({
      selector: 'app-carousel',
      templateUrl: './carousel.component.html',
      styleUrls: ['./carousel.component.scss'],
      providers: [ CarouselService]
    })
    export class CarouselComponent implements OnInit, AfterViewInit, AfterContentInit {

      @ViewChild('entryForSlides', { read: ViewContainerRef }) entryForSlides: ViewContainerRef;

      @Input() carouselSlides: Array<CarouselSlide>;
      @Input() hasPersistanceService: boolean;
      @Output() noSlidesRemaining: EventEmitter<boolean> = new EventEmitter(false);

      removedSlideToggle = false;

      carrouselInstance: any;

      activeIndex = 0;


      carouselSlideFactory: ComponentFactory<CarouselSlideComponent>;

      constructor( private _resolver: ComponentFactoryResolver, private _carouselService: CarouselService) {
        this.carouselSlideFactory = this._resolver.resolveComponentFactory(CarouselSlideComponent);
      }

      ngOnInit() { }

      ngAfterViewInit(): void {

        this.carrouselInstance = new Swiper ('.swiper-container', {
          init: false,
          // loop: true,
          spaceBetween: 30,
          // speed: 5000,
          pagination: {
            el: '.swiper-pagination',
          },
          // Navigation arrows
          navigation: {
            nextEl: '.swiper-button-nextSlide',
            prevEl: '.swiper-button-previousSlide',
          }
        });

        this.carrouselInstance.on('slideChangeTransitionEnd', () => {
          this.activeIndex = this.carrouselInstance.realIndex;
          this._carouselService.updateIndex(this.activeIndex);
        });

        this.carrouselInstance.init();
      }

      ngAfterContentInit(): void {
        this.generateSlides();
      }

      clickOnCross() {
        this._carouselService.updateIndex(this.activeIndex);
        this.entryForSlides.clear();
        this.carouselSlides.splice(this.carrouselInstance.realIndex, 1);
        this.generateSlides();

        // Timeout to update carousel with the new DOM slides (little hack while a better solution is found): DO NOT REMOVE
        setTimeout(() => {
          this.carrouselInstance.update();
        }, 1);
        if (this.carouselSlides.length <= 0 ) {
          this.noSlidesRemaining.emit();
        }
      }

      generateSlides() {
        this.carouselSlides.forEach((element, index) => {
          const component = this.entryForSlides.createComponent(this.carouselSlideFactory);
          component.instance.carouselSlide = element;
          component.instance.numberOfIndex = index;
          component.instance.activeSlide = this.activeIndex;
        });
      }
    }


    @Injectable({
      providedIn: 'root'
    })
    export class CarouselService {

      currentIndex = new Subject<number>();

      constructor() { }

      updateIndex(newIndex: number) {
        this.currentIndex.next(newIndex);
      }
    }


@注射的({
providedIn:'根'
})
导出类转盘服务{
currentIndex=新主题();
构造函数(){}
更新索引(新索引:编号){
this.currentIndex.next(newIndex);
}
}

我已经这样做了,但是每次都要更新
this.component.instance.name
ref似乎是一种可怕的方法,真的没有其他方法吗?我已经看到了
this.component.detectChanges()
setter
内部,但这仍然不起作用我已经这样做了,但每次都要更新
this.component.instance.name
ref似乎是一种可怕的方法,真的没有其他方法吗?我发现了这个问题。把例子改成这个。这里手动调用子组件的ngOnChanges。是的,我想我的意思是有一种方法可以避免每次手动调用进行更新。我觉得应该有一种自动处理更改的方法,就像
子组件[name]=“name”
方法通过
onChanges自动处理更改一样。