Как повторно применить привязку свойства без повторного рендеринга компонента? - PullRequest
0 голосов
/ 17 апреля 2020

Контекст

Бизнес-компонент A зависит от бизнес-службы, которая говорит либо:

  • true: все сворачиваемые компоненты должны быть открыты по умолчанию.

  • false: все сворачиваемые компоненты должны быть закрыты по умолчанию.

A передать эти знания (вычисление, а не сохраненное значение) через одностороннюю привязку свойства к нескольким техническим дочерним компонентам B, которые определяют шаблон для сворачиваемого компонента (их содержимое является проекцией определенного c бизнес-компонента).

Когда A и B впервые отображаются привязки разрешаются, и B получают знания: сворачиваемые компоненты знают, должны ли они отображать свое содержимое или скрывать его. Тем не менее, они по-прежнему предоставляют кнопку для отображения или скрытия контента, если пользователь хочет.

Примечание: сворачиваемый компонент не внедряет сам сервис для непосредственного доступа к знаниям, потому что это бизнес-сервис и это технический компонент.

Проблема

После действия в A я хочу "повторно разрешить" привязку свойства с помощью B поэтому я хочу, чтобы A снова передавал знание сервиса в B и, таким образом, переопределял любые действия пользователя (например: если он открыл свертываемый компонент, который по умолчанию закрыт, я хочу он будет восстановлен в состояние по умолчанию, закрыто) * 1) Я не хочу, чтобы пользователь видел моргание, вызванное разрушением / рендерингом компонента.

2) За этим нет веской причины Эта проблема для повторной визуализации компонента.

Код

@Component({
    selector: 'business-parent',
    template: '
    <generic-collapsable-component [opened]="businessHelper.conditionA">
        // A business-child component
    </generic-collapsable-component> 
    // More generic-collapsable-component
    '
})
export class BusinessParentComponent {

    constructor(private businessHelper: BusinessHelper) {
    }

    onBusinessAction() {
        // Here I do some business stuff...
        // And I want to force the binding [opened] to re-execute its default value, so I want GenericCollapsableComponent.opened = businessHelper.conditionA, and not what I currently have, which is the value of the last GenericCollapsableComponent.switch() I called.
    }
}

@Component({
    selector: 'generic-collapsable-component',
    template: '
    <button (click)="switch()">Switch to show or hide content</button>
    <div [ngClass]="{'is-hidden': !isOpen()}"
        <ng-content></ng-content>
    </div>
    '
})
export class GenericCollapsableComponent {

    @Input() opened: boolean; // Is intialized by the parent component but can be modified by the inner switch() method called in the template.

    constructor() {
    }

    switch(): void {
        this.opened = !this.opened;
    }

    isOpen(): boolean {
        return this.opened;
    };
}

Решения

  • Повторная визуализация компонента: NO.
  • Привязка функции () => логическое к установите начальное значение и используйте другое частное логическое значение для ответа на действия пользователя: это то, что я сделал, и это работает, но это не идеально.

Ответы [ 2 ]

1 голос
/ 17 апреля 2020

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

Техническую службу можно определить как:

import { Injectable } from "@angular/core";
import { BehaviorSubject, Observable } from "rxjs";

@Injectable({
  providedIn: "root",
})
export class TechnicalService {

  private conditionChanged = new BehaviorSubject<boolean>(false);
  public conditionChanged$ = this.conditionChanged.asObservable();

  public notifyConditionChanged(value: boolean) {
    this.conditionChanged.next(value);
  }
}

Предполагая, что родительский компонент уведомляется, когда вычисленное значение передается бизнес-службой, он может передавать информацию дочернему компоненту через техническую службу:

export class AppComponent {
  constructor(
    private businessService: BusinessService,
    private technicalService: TechnicalService) {

    this.businessService.conditionChanged$.subscribe((value) => {
      this.technicalService.notifyConditionChanged(value);
    })
  }
  ...
}

child будет обновлять свое свойство при получении уведомления от технической службы, заменяя значение, измененное пользователем вручную:

export class ChildComponent {
  public opened: boolean;
  private subscription: Subscription;

  constructor(private technicalService: TechnicalService) {
    this.subscription = this.technicalService.conditionChanged$.subscribe(value => {
      this.opened = value;
    });
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

  toggleOpened() {
    this.opened = !this.opened;
  }
}

См. this stackblitz для демонстрации.

1 голос
/ 17 апреля 2020

Я думаю, что вы запрашиваете двухстороннее связывание (он же "BANANA IN A BOX"). Это позволит вашему родительскому компоненту перейти в исходное состояние. Дочерний компонент может затем передать значение, когда оно изменяется в дочернем. Затем вы также можете изменить «открытое» состояние в родительском компоненте в зависимости от того, какая у вас бизнес-логика c, оставляя ваш универсальный c разборный компонент неосведомленным о любых бизнес-правилах, предписываемых при открытии / закрытии чего-либо.

Разборный компонент:

export class GenericCollapsableComponent {

    @Input() opened: boolean;
    @Output() openedChange = new EventEmittter<boolean>(); //must have the variable name followed by "Change"

    constructor() {
    }

    switch(): void {
        this.opened = !this.opened;
        this.openedChange.emit(this.opened); //alert the parent that the "opened" state has changed
    }

    isOpen(): boolean {
        return this.opened;
    };
}

Использование компонента свертывания:

<generic-collapsible [(opened)]="isOpened">
   <div>Some content!</div>
</generic-collapsible>

И выделенный код:

export class ParentComponent {
    isOpened: boolean = true;
}

Вот stackblitz , показывающий двустороннюю привязку и ряд различных способов изменения состояния сворачивания.

...