Оповещения на уровне приложения Dynamic Clarity

Я использую vmware/clarity дизайн-систему и пытаюсь реализовать динамические оповещения на уровне приложения с помощью загрузка динамического компонента описана в angular.io. Я следовал этому шаблону раньше, но не могу заставить его работать с предупреждениями, поступающими от Clarity.

app.component.ts

import { AfterViewInit, Component, ComponentFactoryResolver, OnDestroy, ViewChild } from '@angular/core';
import { ClrAlert } from '@clr/angular';

import { AlertsHostDirective } from './directives/alerts-host.directive';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit, OnDestroy {

  @ViewChild(AlertsHostDirective) alerts: AlertsHostDirective;
  private interval: NodeJS.Timer;

  constructor(private _componentFactoryResolver: ComponentFactoryResolver) { }

  ngAfterViewInit(): void {
    this.alert();
    this.getAlerts();
  }

  ngOnDestroy(): void {
    clearInterval(this.interval);
  }

  private alert() {
    const componentFactory = this._componentFactoryResolver.resolveComponentFactory(ClrAlert);

    const viewContainerRef = this.alerts.viewContainerRef;
    const componentRef = viewContainerRef.createComponent(componentFactory);

    componentRef.instance.isAppLevel = true;
    componentRef.changeDetectorRef.detectChanges();
  }

  private getAlerts() {
    this.interval = setInterval(() => {
      this.alert();
    }, 5000);
  }

}

app.component.html

<clr-main-container>
    <clr-alerts>
        <ng-template appAlertsHost></ng-template>
        <clr-alert clrAlertType="info"
                   [clrAlertAppLevel]="true">
            <div class="alert-item">
                <span class="alert-text">This is the first app level alert.    </span>
                <div class="alert-actions">
                    <button class="btn alert-action">Fix</button>
                </div>
            </div>
        </clr-alert>
        <clr-alert clrAlertType="danger"
                   [clrAlertAppLevel]="true">
            <div class="alert-item">
                <span class="alert-text">This is a second app level alert.</span>
                <div class="alert-actions">
                    <button class="btn alert-action">Fix</button>
                </div>
            </div>
        </clr-alert>
    </clr-alerts>
...

оповещения-host.directive.ts

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appAlertsHost]'
})
export class AlertsHostDirective {

  constructor(public viewContainerRef: ViewContainerRef) { }

}

Если я помещу директиву в компонент ClrAlerts, он будет работать так, как должен, в том случае, если оповещения уровня приложения будут добавлены после ClrAlerts в DOM. Но я хочу, чтобы все мои оповещения на уровне приложения появлялись внутри этого компонента по мере их поступления. В свою очередь, я хотел бы, чтобы компонент пейджера также обновлялся.

Это возможно?


person Jake Smith    schedule 08.02.2018    source источник
comment
Этот ответ может помочь вам: .com/questions/40922224/   -  person yurzui    schedule 12.02.2018
comment
Вы буквально всегда создаете компонент ClrAlert, насколько он динамичен? В вашем примере вы можете просто использовать простой *ngFor, и он будет работать нормально.   -  person Eudes    schedule 13.02.2018
comment
@Eudes, может быть, есть слово получше, чем динамический, но я заимствую формулировку из документации angular.io для динамического загрузчика компонентов. Используя шаблон, который они обрисовывают, вы загружаете тот же компонент, строго типизированный, как вы и говорите. То, что я думаю, подразумевается под динамикой во время выполнения. Другими словами, пока приложение уже загружено клиентом, компоненты могут создаваться на основе взаимодействия с пользователем или других событий. *ngFor потребовал бы, чтобы я знал все уведомления до того, как приложение загрузится, что упускает суть того, что я пытаюсь сделать.   -  person Jake Smith    schedule 13.02.2018
comment
* ngFor является динамическим, вам не нужно знать все уведомления во время выполнения. Если в вашем приложении есть глобальный провайдер, который отслеживает все уведомления, которые должны отображаться, а взаимодействия с пользователем и события обновляют этот список, тогда *ngFor идеально подойдет для вашего варианта использования.   -  person Eudes    schedule 13.02.2018
comment
Это правда. Позвольте мне попробовать...   -  person Jake Smith    schedule 13.02.2018


Ответы (2)


После обсуждения в комментариях я решил, что было бы проще опубликовать то, что я имел в виду, просто используя *ngFor: https://stackblitz.com/edit/clarity-light-theme-v11-vs8aig?file=app%2Fnotifications.provider.ts

Использование фабрики компонентов для создания всегда одного и того же компонента совершенно излишне и является ужасной практикой. В разделе документов Angular, на который вы ссылаетесь, конкретно объясняется, что их вариант использования заключается в динамическом создании экземпляра нового компонента, когда они даже не знают фактический класс компонента в первую очередь. Надеюсь, мой пример выше поможет вам понять, как организовать эти оповещения в вашем проекте.

person Eudes    schedule 13.02.2018
comment
Похоже, это сработает. Я был на пути к чему-то подобному, что вы собрали. Тем не менее, я думаю, что снова слишком усложнил и сделал уведомления Observable<MyAlertType>, а не просто прямым массивом, на который я мог нажать. - person Jake Smith; 13.02.2018
comment
Одна вещь, которую я заметил, заключается в том, что если вы начнете с уведомлений, удалите их с помощью кнопок «x», а затем добавьте больше, все будет отображаться не совсем правильно. Первый, который вы добавляете, вообще не отображается, а второй, кажется, создается сам по себе, но плохо отображается. Это похоже на ошибку с Clarity? - person Jake Smith; 13.02.2018
comment
Да, уведомление не удаляется из основного массива, когда пользователь нажимает x. Вы можете связать это, просто послушав (clrAlertClosedChange): stackblitz.com/edit/ - person Eudes; 13.02.2018
comment
Но при таком подходе вам все равно нужно явно установить текущий индекс после добавления предупреждения после удаления всех предупреждений. - person Jake Smith; 13.02.2018
comment
Это отстой, потому что я не уверен, как еще получить доступ к MultiAlertService, чтобы установить текущий индекс, не сохраняя этот код в AppComponent, чтобы получить ViewChild - person Jake Smith; 13.02.2018
comment
В настоящее время это лучшее решение, поскольку динамические компоненты добавляются как братья и сестры viewContainerRef, а не дочерние. Во-вторых, как мы можем добавить действие, особенно когда оповещение добавляется из дочернего компонента с использованием наблюдаемых? - person Samiullah Khan; 08.07.2018

@Eudes указал, что я слишком усложняю ситуацию. По сути, мне просто нужно отслеживать массив уведомлений, чтобы, когда происходит что-то «поддерживающее оповещение», я мог просто поместить объект оповещения в массив и заставить их отображаться через *ngFor.

Однако, похоже, что родительский компонент ClrAlerts неправильно обрабатывает эти изменения в определенных ситуациях. Например, если вы начинаете с предупреждений в массиве, закрываете их взаимодействием с пользователем, а затем добавляете новые, текущее предупреждение установлено неправильно. Поэтому это кажется самым чистым решением, которое я мог придумать:

Шаблон

<clr-main-container>
    <clr-alerts>
        <clr-alert *ngFor="let alert of alerts"
                   [clrAlertType]="alert.type"
                   [clrAlertAppLevel]="true">
            <div class="alert-item">
                <span class="alert-text">{{alert.text}}</span>
                <div class="alert-actions"
                     *ngIf="alert.action">
                    <button class="btn alert-action">{{alert.action}}</button>
                </div>
            </div>
        </clr-alert>
    </clr-alerts>
    ...

Составная часть

import 'rxjs/add/Observable/of';

import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core';
import { ClrAlerts } from '@clr/angular';

export interface AppLevelAlert {
  type: 'info' | 'warning' | 'success' | 'danger';
  text: string;
  action: string;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit, OnDestroy {

  @ViewChild(ClrAlerts) alertsContainer: ClrAlerts;
  private interval: NodeJS.Timer;
  alerts: AppLevelAlert[] = [];

  ngAfterViewInit(): void {
    this.alert();
    this.getAlerts();
  }

  ngOnDestroy(): void {
    clearInterval(this.interval);
  }

  private alert() {
    this.alerts.push({
      type: 'success',
      text: 'This was added later!',
      action: 'Do nothing'
    });
    if (!this.alertsContainer.currentAlert) {
      this.alertsContainer.multiAlertService.current = 0;
    }
  }

  private getAlerts() {
    this.interval = setInterval(() => {
      this.alert();
    }, 5000);
  }

}
person Jake Smith    schedule 13.02.2018
comment
Я также пытался использовать динамические компоненты, но это лучшее решение с точки зрения сложности и ясности. Я бы отправлял все оповещения в службу, а clr-оповещения просто подписывались на наблюдаемые оповещения. - person Samiullah Khan; 18.07.2018
comment
Так круто. TY для обмена. - person Randell S. Hynes; 12.12.2019