Угловое предупреждение о круговой зависимости - PullRequest
4 голосов
/ 07 ноября 2019

tl; dr: прокрутите вниз до решения

У меня круговая зависимость, и я получаю предупреждение, по праву, однако, я управляю им. Проблема в том, что у меня есть компонент чата. В углу вы можете выбрать, чтобы увидеть страницу их профиля, а на странице их профиля у вас есть возможность отправить им сообщение, отсюда и круговая зависимость. Я управляю этим с помощью

chat.component

public async openProfile(): Promise<void> {
  this.modalCtrl.dismiss(); //closing the chat component before opening the profile modal
  const profile = await this.modalCtrl.create({
    component: ProfileComponent,
  });
  await profile.present();
} 

profile.component

public async openChat(): Promise<void> {
  this.modalCtrl.dismiss(); //closing the profile component before opening the chat modal
  const chat = await this.modalCtrl.create({
    component: ProfileComponent,
  });
  await chat.present();
} 

Есть ли более простой способ обработки этой циклической зависимости?

ОБНОВЛЕНИЕ: согласно предложению ниже я попытался создать сервис. Однако теперь у меня есть трехсторонний круг зависимостей:

chat.component

private modalService: ModalService;

constructor(modalService: ModalService){
  this.modalService = modalService
}

public async openProfile(): Promise<void> {
  this.modalService.openProfile(this.userData);
} 

profile.component

private modalService: ModalService;

constructor(modalService: ModalService){
  this.modalService = modalService
}

public async openChat(): Promise<void> {
  this.modalService.openChat(this.userData);
}

modal.service

import { ModalController } from '@ionic/angular';
import { Injectable } from '@angular/core';
import { ProfileComponent } from '../../components/profile/profile.component';
import { ChatComponent } from '../../components/chat/chat.component';
import { UserData } from '../../interfaces/UserData/userData.interface';

@Injectable({
  providedIn: 'root',
})
export class ModalService {
  private modal: ModalController;
  public constructor(modal: ModalController) {
    this.modal = modal;
  }

  public async openProfileComponent(user: UserData): Promise<void> {
    this.modal.dismiss();
    const profile = await this.modal.create({
      component: ProfileComponent,
      componentProps: {
        contact: user,
      },
    });

    await profile.present();
  }

  public async openChatComponent(user: UserData): Promise<void> {
    this.modal.dismiss();
    const chat = await this.modal.create({
      component: ChatComponent,
      componentProps: {
        contact: user,
      },
    });

    await chat.present();
  }

  public close(): void {
    this.modal.dismiss();
  }
}

ОБНОВЛЕНИЕ Stackblitz слишком нестабилен с Ionic 4, поэтому я не могу скопировать его, поэтому вот gist с информацией и соответствующим кодом.

UPDATE2 Я воспользовался советом, упомянутым в ответах, но все еще получаю ошибку. Для этого я создал shared.module.ts, который выглядит следующим образом:

import { UserService } from './componentServices/user/user.service';
import { ModalService } from './componentServices/modal/modal.service';
import { AuthenticationSecurityService } from './componentServices/auth_security/authentication-security.service';
import { AuthGuardService } from '../_guards/auth-guard.service';
import { ApiService } from './componentServices/api/api.service';
import { ChatService } from './components/chat/socketIO/chat.service';

@NgModule({
  imports: [CommonModule, ReactiveFormsModule, IonicModule.forRoot(), FormsModule, IonicModule],
  declarations: [
    // various components
  ],
  exports: [
    // various components and common modules
  ],
})
export class SharedModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: SharedModule,
      providers: [
        UserService,
        ModalService,
        DashboardService,
        AuthenticationSecurityService,
        AuthGuardService,
        ApiService,
        ChatService,
      ],
    };
  }
}

app.module.ts

imports: [
    SharedModule.forRoot(),
]
client:135 Circular dependency detected:
src/sharedModules/componentServices/modal/modal.service.ts -> src/sharedModules/components/profile/profile.component.ts -> src/sharedModules/componentServices/modal/modal.service.ts

client:135 Circular dependency detected:
src/sharedModules/components/chat/chat.component.ts -> src/sharedModules/components/search/search.component.ts -> src/sharedModules/components/profile/profile.component.ts -> src/sharedModules/componentServices/modal/modal.service.ts -> src/sharedModules/components/chat/chat.component.ts

client:135 Circular dependency detected:
src/sharedModules/components/profile/profile.component.ts -> src/sharedModules/componentServices/modal/modal.service.ts -> src/sharedModules/components/profile/profile.component.ts

client:135 Circular dependency detected:
src/sharedModules/components/search/search.component.ts -> src/sharedModules/components/profile/profile.component.ts -> src/sharedModules/componentServices/modal/modal.service.ts -> src/sharedModules/components/chat/chat.component.ts -> src/sharedModules/components/search/search.component.ts

РЕШЕНИЕ

как сказали @ bryan60 и @Luis, там должен быть буфер, поэтому я пошел по пути излучения, который они оба предложили. Брайан дает больше похожего кода, где Луис дает большую сводку ответственности. Вот как я его рефакторинг:

app.component.ts

  public initializeApp(): void {
    this.platform.ready().then((): void => {
      this.statusBar.styleDefault();
      this.splashScreen.hide();
      this._subToObservables();
    });
  }

  private _subToObservables(): void {
    this.modalService.openModal$.subscribe(
      async (e: ModalEmitInterface): Promise<void> => {
        const { command, data } = e;
        switch (command) {
          case 'open-profile':
            const profile = await this.modalCtrl.create({
              component: ProfileComponent,
              componentProps: {
                contact: data,
              },
            });
            await profile.present();
            break;

          case 'open-chat':
            // same as above
            break;

          default:
            break;
        }
      },
    );
  }

modalSignal.service.ts

export class ModalService {
  private openModalSubject: Subject<ModalEmitInterface> = new Subject<ModalEmitInterface>();
  public readonly openModal$: Observable<ModalEmitInterface> = this.openModalSubject.asObservable();

  private emitPayload: ModalEmitInterface;
  public openProfileComponent(user: UserData): void {
    this.emitPayload = {
      command: 'open-profile',
      data: user,
    };
    this.openModalSubject.next(this.emitPayload);
  }

  // repeat for others
}

chat.component.html

<button (click)="openProfile(user)">click me</button>

chat.component.ts

export class ChatComponent {
  public constructor(private modalSignal: ModalService){}

  private openProfile(user: UserData): void {
    this.modalSignal.openProfileComponent(user);
  }
}

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

Ответы [ 3 ]

2 голосов
/ 14 ноября 2019

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

Вам понадобится услуга (как предложили другие), но также, скажем так, беспристрастный игрок. Идея состоит в том, чтобы использовать сервис в качестве буфера связи / обмена сообщениями между двумя взаимозависимыми компонентами, чтобы помочь сломать перекрестную ссылку. Для примера давайте предположим, что «App.Component».

Компонент и обязанности:

Modal.Service : получение сообщений для выполнения действий. Это может быть один метод, получающий строку или объект, указывающий действие или несколько методов для каждого действия. Подробная информация о реализации зависит от вас.

App.Component : получает введенный ModalService и подписывается на событие сообщения. На основании сообщения действия затем активирует соответствующий модальный режим.

Chat.Component : получает введенный Modal.Service и отправляет сообщение, указывающее действие, которое должно быть выполнено, т.е. показывает профиль.

Profile.Component : получает введенный Modal.Service и отправляет сообщение, указывающее действие, которое необходимо выполнить, т.е. отправляет сообщение.

Это очень хорошо масштабируется иСервис может использоваться в качестве буфера связи между несколькими другими модулями и / или компонентами.

1 голос
/ 14 ноября 2019

это немного раздражает, но вам нужны обертки или несколько сервисов. Один сервис не будет работать, как вы видели, потому что очевидно, что вы не можете импортировать свой компонент в сервис, а затем сервис в свой компонент. Это всего лишь немного больший круг.

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

подход 2 - немного приятнее ИМО. размещение оберток страниц вокруг ваших компонентов, которые обрабатывают модальные вызовы, и вы можете сохранить свой единый подход к обслуживанию.

создайте компоненты обертки страниц следующим образом:

@Component({
  template: `<profile (openChat)="openChat()></profile>`
})
export class ProfilePageComponent {
   openChat() {
     // call your service or what have you here
   }
}

создайте аналогичную настройку для компонента чата и измените свой профиль / компоненты чата, чтобы они просто излучали, а не вызывали службу (или простокнопки для вызова модалов в обёртках). Надеюсь, у вас не бывает таких двусторонних модальных отношений слишком часто. но это работает, потому что обертки не импортируются в компоненты, вы направляете их к оберткам страниц, но обертки страниц создают экземпляры компонентов в модальных моделях. Весы немного лучше, но все же не идеал. Большим преимуществом здесь является то, что при разработке этого приложения вы можете найти больше преимуществ в наличии обертки страниц вокруг ваших компонентов, если данный компонент может отображаться как страница или как модальный, поскольку иногда вы хотите, чтобы компонент находился в своем контексте по-другому. ,Если вы предвидите пользу для этого, тогда используйте этот подход. И наоборот, вы также можете обернуть ваши компоненты в модальные обертки и создать их экземпляры вместо компонентов напрямую. Логика импорта та же, и она работает по тем же причинам и дает те же преимущества контекста, но с другой стороны.

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

@Injectable()
export class ModalSignalService{
  private openChatSubject = new Subject()
  openChat$ = this.opopenChatSubject.asObservable()
  openChat() {
    this.openChatSubject.next()
  }
  private openProfileSubject = new Subject()
  openProfile$ = this.openProfileSubject.asObservable()
  openProfile() {
    this.openProfileSubject.next()
  }
}

, тогда есть общая страницаКомпонент оболочки подписывается на потоки и обрабатывает модальное создание

@Component({
  template: `<router-outlet></router-outlet>` // something like this and set up routing with components as child routes
})
export class PageWrapperComponet {

  constructor(private modalSignalService: ModalSignalService) {
    this.modalSignalService.openChat$.subscribe(e => this.openChatModal()) // open modal logic here
    this.modalSignalService.openProfile$.subscribe(e => this.openProfileModal())
  }
}

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

0 голосов
/ 07 ноября 2019

Создайте модальную службу, которая знает оба компонента.

 ModalService {
     public async openChat(): Promise<void> {
         this.modalCtrl.dismiss(); //closing the profile component before 
         opening the chat modal
         const chat = await this.modalCtrl.create({
         component: ProfileComponent,
     });

     public async openProfile(): Promise<void> {
             this.modalCtrl.dismiss(); //closing the chat component before opening the 
             profile modal
             const profile = await this.modalCtrl.create({
             component: ProfileComponent,
         });
        await profile.present();
    } 
  }

Добавьте службу в оба компонента.

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

Теперь эти два компонента не знают друг друга, поэтому у вас нет круговой зависимости.

Для того чтобы предупреждение исчезловпрыск через инжектор в компонентах

private modalService: ModalService;
public constructor(injector:Injector) {
    this.modalService = injector.get(modalService);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...