Программно установленный атрибут динамически добавляемого дочернего компонента - PullRequest
1 голос
/ 15 апреля 2019

У меня есть коллекция карточных предметов

...
cards: Card[];
...
ngOnInit() {
  this.cardService.getCards.subscribe(r => { this.cards = r; });
}

Я добавляю компоненты дочерней карты в шаблон, как это

<div id="cards-container">
  <app-card *ngFor="let card of cards" [name]="card.name"></app-card>
</div>

Компонент Card имеет имя и стиль, зависящий от активного атрибута, который переключается при нажатии на компонент

@Component({
  selector: 'app-card',
  templateUrl: './card.component.html',
  'styleUrls: ['./card.component.scss']
})
export class CardComponent {
  private _name = '';
  private _active = false;

  // getters/setters
  ...

  onClick(e) {
    this.active = !this.active;
  }
}

card.component.html

<div [ngClass]="{'card': true, 'active': _active}"
     (click)="onClick($event)">
  {{name}}
</div>

Все это прекрасно работает.

Проблема: В родительском компоненте мне нужно перебрать все компоненты карты, которые были добавлены с помощью *ngFor="let card of cards", и установить их все активными или не активными, но я не могу понять, как это сделать.

Что я пробовал: Я пытался использовать @ViewChildren(), но метод QueryList toArray() всегда дает мне пустой массив. Из ограниченных примеров в документации ViewChildren Я не уверен на 100%, нужна ли мне дополнительная Директива в родительском компоненте или директивы в примерах просто используются для демонстрации. Так что я попробовал

@ViewChildren(CardComponent) cardList: QueryList<CardComponent>;

Я также пытался использовать ViewContainerRef , используя что-то похожее на этот ответ , но мне не удалось заставить его работать, и кажется, что это не ведет меня в правильном направлении. Я также просмотрел документацию для ComponentRef , но не понимаю, как или если это может помочь мне решить мою проблему.

Любые предложения указать мне правильное направление приветствуются.

UPDATE

Мой активный установщик в компоненте карты выглядит так

@Input()
set active(active: boolean) {
  this._active = active;
}

Мне нужно иметь возможность изменить это для всех карт в любой момент, другими словами, опция «выбрать / отменить выбор всех».

РЕШИТЬ!

Принимая предложение от @Tim Klein , я подписался на changes из QueryList и смог получить мои компоненты в массиве, который я обновляю при изменении QueryList. Теперь я просто перебираю массив компонентов и вызываю мой active setter.

cards: CardComponent[];
@ViewChildren(CardComponent) cardList: QueryList<CardComponent>;
...
ngAfterViewInit(): void {
  this.cards = this.cardList.toArray(); // empty array but that's okay
  this.cardList.changes.subscribe((r) => {
    this.cards = this.cardList.toArray(); // we get all the cards as soon as they're available
  });
}

1 Ответ

1 голос
/ 15 апреля 2019

Я думаю, что проблема, с которой вы можете столкнуться, заключается в том, что после вызова события жизненного цикла afterViewInit ваши динамические компоненты все еще не загружены. Поэтому, если вы вызываете cardList.toArray() внутри afterViewInit, он вернет пустой список, поскольку ваши компоненты еще не были добавлены в представление родительского компонента.

Вы должны попытаться подписаться на наблюдаемое changes (как в в этом примере ) и в рамках обратного вызова вызывать вашу логику для изменения состояний дочерних компонентов.

Обновление

Другой вариант - просто предоставить другой вход для вашего дочернего компонента, который может принимать логическое значение для своего активного состояния.

Затем, когда вы устанавливаете список объектов Card, просто выполните итерацию и измените какое-либо свойство active. Просто установите это свойство вместе с name в шаблоне для родительского компонента.

...