动态组件 - createComponent - OnChanges

时间:2018-05-24 03:15:34

标签: angular

我正在通过createComponent方法创建动态组件,但我无法让child组件更新input从{{1}传递给它的值parent通过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;
  }
}

ChildComponent:

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;
  }
}

问题:当child组件中的值发生变化时,如何通过createComponent()方法创建动态parent组件来更新其值?

3 个答案:

答案 0 :(得分:2)

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

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);
}

答案 1 :(得分:0)

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

&#13;
&#13;
@Component({
  selector: 'parent-component',
  template: `
    <div>
      <input type="text" (keyup)="onNameChange($event)">
      <span>{{ name }}</span>
    </div>
    <ng-container #container></ng-container>
  `,
  styles: []
})
export class ParentComponent implements AfterContentInit {
  @ViewChild('container', { read: ViewContainerRef}) container: ViewContainerRef;
  private component;

  private _name = 'John Doe';
  get name() {
    return this._name;
  }
  set name(name: string) {
    this._name = name;
  }
  constructor(private resolver: ComponentFactoryResolver) { }

  onNameChange(event) {
    this.name = event.target.value;
    this.component.instance.name = this.name;
  }

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

Click here for StackBlitz DEMO

答案 2 :(得分:-1)

如果您创建一个带有可观察对象的服务,该服务会在每次值更改时发出,并且在动态组件的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;
        });
      }
    }

然后我动态注入的组件是这样的:

    @Component({
      selector: 'app-carousel-slide',
      templateUrl: './carousel-slide.component.html',
      styleUrls: ['./carousel-slide.component.scss']
    })
    export class CarouselSlideComponent implements OnInit, OnDestroy {

      @HostBinding('class.swiper-slide') public mustAddSwiperSlideClass = true;
      carouselSlide: CarouselSlide;
      numberOfIndex: number;
      activeSlide: number;
      subActiveSlide: Subscription;

      constructor(private _carouselService: CarouselService) { }

      ngOnInit() {
        this.subActiveSlide = this._carouselService.currentIndex.subscribe(
          (data: number) => {
            this.activeSlide = data;
          }
        );
      }

      ngOnDestroy() {
        if (this.subActiveSlide) {
          this.subActiveSlide.unsubscribe();
        }
      }
    }

因此,当我从parentComponent执行函数clickOnCross时,我需要通过调用服务上的函数来更新动态注入组件上的Input activeSlide,该服务会更新activeSlide并将值发射给所有动态注入组件。这是CarouselService的代码:


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

      currentIndex = new Subject<number>();

      constructor() { }

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