Angular Поведение Субъект в службе портится при инициализации двух одинаковых компонентов - PullRequest
0 голосов
/ 25 марта 2020

Я создал компонент, который называется боковой панелью. Этот компонент имеет, скажем, 2 входа, sideBarMode и sideBarSide. В app.component я добавляю этот компонент, как этот <sidebar [sideBarMode]="'sliding'" [sideBarSide]="'right'"></sidebar>.

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

<div>
<sidebar [sideBarMode]="'sliding'" [sideBarSide]="'right'"></sidebar>
</div>

<div>
<sidebar [sideBarMode]="'docked'" [sideBarSide]="'left'"></sidebar>
</div>

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

Это шаблон HTML.

<div class="container-fluid">
<div [ngClass]="barSliding ? 'sliding' : 'sidebar-docked'">
    <div class="row k-sidebar {{theme}}"
        [ngClass]="[sidebarLeft ? 'position-left' : 'position-right', barSliding ? 'sliding-content-bar' : 'sidebar-docked', isDesktopWidth.value ? 'largeView' : 'mobileView']"
        [@slideAnimation]= 'animationState'
        [@dockMobileSidebar]= 'animationStateMobile'
        [ngStyle]="{'width.px': isDesktopWidth.value ? sideBarWidth : '' }"
        >
        <div class="col-md-12">
            <ng-container *ngIf="sidebarTemplate">
                <ng-container *ngTemplateOutlet="sidebarTemplate">

                </ng-container>
            </ng-container>

            <ng-container *ngIf="!sidebarTemplate">
                <p>This is a paragraph, no template has been passed</p>
            </ng-container>
        </div>
</div>
</div>

Вот как я проверяю режим, если он закреплен или не в файле TS, и я вызываю этот метод из ngOnInit.

 private checkSidebarMode(mode) {
console.log('checkSidebarMode ', mode);
if (mode === 'sliding') {
  this.sidebarService.sideBarSliding.next(true);
  console.log('checkSidebarMode ', this.sidebarService.sideBarSliding.getValue());
  this.sideBarDock.next(false);
} else if (mode === 'docked' && this.sidebarService.innerWidth.getValue() <= 767) {
  this.sideBarDock.next(false);
  this.sidebarService.sideBarSliding.next(true);
} else if (mode === 'docked' && this.sidebarService.innerWidth.getValue() > 767) {
  this.sideBarDock.next(true);
  this.sidebarService.sideBarSliding.next(false);
  this.sidebarService.animationState.next('docked');
  console.log('checkSidebarMode ', this.sidebarService.sideBarSliding.getValue());
}
  }

Как вы можете видеть, если он скользящий, я устанавливаю наблюдаемое в true, а затем в ngOninit назначаю это значение barSliding. , который используется в шаблоне HTML для определения имени класса.

Прикрепленный является правильным, однако скользящий получает свойства пристыкованного ... почему?

enter image description here

1 Ответ

1 голос
/ 26 марта 2020

Поскольку ваш BehaviorSubject находится в sidebarService, это, вероятно, providedIn: 'root', что делает его одиночным. Таким образом, ваш BehaviorSubject одинаков для двух экземпляров вашего компонента.

У вас есть несколько решений:

  • объявляет BehaviorSubject непосредственно в вашем компоненте вместо использования общего BehaviorSubject в сервисе. Я бы обновил его, вызвав next() в ngOnChanges методе , как только изменится вход mode.
@Component({...})
export class MyComponent implements OnInit {
    @Input() mode: 'sliding' | 'docked' = 'sliding';

    private subject = new BehaviorSubject<string>(null);

    ngOnChanges() { 
        // here you should check that 'mode' changed before calling next()
        this.subject.next(this.mode);
    }
}
  • используйте фабричный метод в вашем сервисе, который возвращает новый экземпляр BehaviorSubject для использования вашим компонентом. Возможно, нет никакого реального преимущества перед первым решением, если оно простое BehaviorSubject без логинов c.
// service 

@Injectable()
export class MyService {

    public getNewBehaviorSubject() {
        return new BehaviorSubject<String>(null);
    }
}

// component

@Component({...})
export class MyComponent implements OnInit {
    @Input() mode: 'sliding' | 'docked' = 'sliding';

    private subject = this.myService.getNewBehaviorSubject();

    ngOnChanges() { 
        // here you should check that 'mode' changed before calling next()
        this.subject.next(this.mode);
    }
}

Но это имеет смысл, только если BehaviorSubject, который вы возвращаете из службы, должен использовать другую информацию из службы (другие данные, переменные c, такие как размер экрана, et c).

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

Один вопрос, который вы должны себе задать, заключается в следующем: должно ли это поведение использоваться компонентами или быть известным другим компонентам? В этом случае вы можете делегировать его в глобальную службу Singleton. Пример: каждому компоненту может понадобиться узнать в какой-то момент, открыто ли главное скользящее меню приложения или нет. Или это поведение указано c для экземпляра компонента? В этом случае состояние должно храниться в самом экземпляре компонента. Пример: у меня есть два складных элемента, каждый из которых должен отслеживать, свернут он или нет, но никакой другой компонент не должен знать об этом состоянии.

Есть много стратегий, которые вы можете принять, и нет ответа, который бы подходил для всех ситуаций.

...