Как заменить целевой элемент на компонент в * ngFor, используя пользовательскую директиву? - PullRequest
0 голосов
/ 17 марта 2019

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

Обнаружена проблема: Все элементы списка заменены загрузчиком вместо того, на котором я щелкнул.

Я не могу найти способ выбрать только элемент, по которому щелкнули.

Stackblitz: https://stackblitz.com/edit/angular-wmybgc?file=src%2Fapp%2Floader-and-error-handler.directive.ts

Директива:


    import { StoreService } from '@services/store/store.service';
    import { Directive, Input, ViewContainerRef, TemplateRef, ComponentFactoryResolver, Type } from '@angular/core';
    import { LoaderComponent } from '@components-ui/loader/loader.component';

    export class LoaderAndErrorContext {
      $implicit: boolean;
      message: string;
    }

    @Directive({
      selector: '[loaderAndErrorHandler]'
    })
    export class LoaderAndErrorHandlerDirective {

      private isLoading: boolean;

      @Input('loaderAndErrorHandlerWait') private delay: number;
      @Input('loaderAndErrorHandlerBeforeDisplaying') private message: string;
      @Input() set loaderAndErrorHandler(isLoading: boolean) {

        this.isLoading = isLoading;

        this.viewControllerRef.clear();

        if (this.isLoading) {

          this.displayComponent(LoaderComponent);

          setTimeout(() => {
            this.store.sendNotification(this.message);
          }, this.delay);
        }
        else {
          this.viewControllerRef.clear();
          this.viewControllerRef.createEmbeddedView(this.templateRef);
        }
      }

      constructor(
        private viewControllerRef: ViewContainerRef,
        private templateRef: TemplateRef<any>,
        private componentFactoryResolver: ComponentFactoryResolver,
        private store: StoreService
      ) {
        this.displayComponent = this.displayComponent.bind(this);
      }

      displayComponent = (component: Type<LoaderComponent>, message?: string): void => {
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
        message
          ? this.viewControllerRef.createComponent(componentFactory).instance['message'] = message
          : this.viewControllerRef.createComponent(componentFactory);
      }
    }

Реализация:

<h2>Mes documents</h2>
<ul [@fadeInOutStagger]="documents.length">
  <ng-container *loaderAndErrorHandler="isLoading; wait 2000 beforeDisplaying errorMessage;">
  <li *ngFor="let document of documents | sortBy:'label'">
      <article (click)="getDocument(document.id)" class="document-label">
        <input type="image" src="assets/images/icon/icone_document.svg">
        <label>
          <strong>{{document.label}}</strong>
        </label>
      </article>
      <article class="document-date">
        <p>{{document.date | dateTo:'JJ Mois'}}</p>
      </article>
  </li>
  </ng-container>
</ul>

1 Ответ

0 голосов
/ 18 марта 2019

Вы можете сделать это следующим образом:

<h2>Mes documents</h2>
<ul>
  <ng-container *ngFor="let document of documents; let i = index" >
  <li *loaderAndErrorHandler="document.isLoading; wait 2000 beforeDisplaying errorMessage;">
      <article (click)="getDocument(document.id, i)" class="document-label">
        <img src="https://image.noelshack.com/fichiers/2019/11/7/1552854323-iconfinder-note3-254240.png" height="20px" width="20px">
        <label>
          <strong>{{document.label}}</strong>
        </label>
      </article>
      <article class="document-date">
        <p>{{document.date}}</p>
      </article>
  </li>
  </ng-container>
</ul>

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

import { Component, Input, ViewRef } from '@angular/core';
import { StoreService } from './services/store/store.service';
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {

  documents = [
    {
      id: '123',
      label: 'Certificat of something great',
      date: '15 jun.',
      isLoading: false
    },
    {
      id: '123',
      label: 'Certificat of something not so great',
      date: '15 jun.',
      isLoading: false
    },
    {
      id: '123',
      label: 'Certificat of something ok',
      date: '15 jun.',
      isLoading: false
    },
  ];

  public errorMessage = 'Erreur: Une erreur s\'est produite durant la récupération de votre document.\nVeuillez renouveler votre requête ultérieurement.'

  constructor(private store: StoreService) {
    this.store.notificationPublisher.subscribe(notification => {
      console.log('Notification:', notification)
    })
  }

  public getDocument = (documentId: string, documentIndex: number): void => {
    this.documents[documentIndex].isLoading = true;
    setTimeout(() => {
      this.documents[documentIndex].isLoading = false;
    }, 1000)
  }
}

Я добавил только состояние для каждого документа и элемента, для которого вы выполняете цикл с *ngFor

РЕДАКТИРОВАТЬ : Вы можете проверить это здесь => https://stackblitz.com/edit/angular-hfnhg7

...