Как управлять данными в маршрутизации при непосредственном доступе к ребенку? - PullRequest
0 голосов
/ 21 мая 2019

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

  • BrandListComponent (/brands) перечислит все доступные бренды.Когда вы нажимаете на марку, она перенаправляет вас к деталям.
  • BrandDetailComponent (/brands/:brandId) покажет вам информацию о марке и кнопку для доступа к списку автомобилей марки.
  • CarListComponent (/brands/:brandId/cars) отобразит все автомобили выбранной марки и даст вам возможность получить информацию о конкретном автомобиле. У вас нет идентификатора автомобиля и только его имя.
  • CarSummaryComponent (/brands/:brandId/cars/:carName) показать сводку по выбранному автомобилю.Необходимо иметь brandId и carName, чтобы иметь возможность вызывать данные из API: /brands/42/cars?filter[name]=mx5

Если мы возьмем навигацию пользователя, доступ к нашему сайту, URL будут вследующий порядок:

  1. /brands
  2. /brands/42
  3. /brands/42/cars
  4. /brands/42/cars/mx5/summary

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

А теперь представьте, что пользователь не хочет всегда проходить эти 4 шага, чтобы получить сводную информацию о своем автомобиле и добавить /brands/42/cars/mx5/summary к своему любимому.

Когда он получает к нему доступ, нам нужно получитьтекущий параметр carName (mx5), который прост, потому что он из нашего текущего маршрута, так:

export class CarSummaryComponent implements OnInit {

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    console.log(this.route.snapshot.params); // {carName: 'mx5'}
  }

}

Но как я могу получить brandId, не связывая CarSummaryComponent и его родительский маршрут: this.route.snapshot.parent.parent.params

Я пытался положиться на queryParams из RouterLink, но я считаю нецелесообразным использовать его в моем случае, так как он добавляет параметрэто значит, что у вас уже есть URL-адрес, поэтому вы дублируете информацию: /brands/42/cars/mx5?brandId=42

Так что я ищу наилучшее ожидание для получения рекурсивной информации, установленной во время всей маршрутизации.Я видел, что когда вы делаете прямой доступ к .../summary, он все равно выполняет конструктор каждого родителя, так что, возможно, решение начинается здесь, но создание службы для хранения параметра не является элегантным выбором для меня, и кто-то объяснил это правильно:https://github.com/angular/angular/issues/10248#issuecomment-312660931

На данный момент я могу рассказать о решении проблемы:

  • Как-то использовать систему распознавания маршрутизации (получить знания о ней в данный момент).Кстати, он мне, вероятно, понадобится для проверки необходимого ввода (поскольку я использую маршрутизацию вместо @Input).
  • Полагайтесь на новое состояние RouterLink (но как его настроить программнобез выполнения navigate ()?)

Я хотел бы создать «чистый» компонент, как я использовал @ Input / @ Output, но полагаясь на данные из маршрутизации.И даже лучше, я, вероятно, попытаюсь создать свой собственный декоратор из найденного решения, чтобы он был похож на чистый компонент с @RouteInput() brandId: number;, например.

Если у вас есть другое / лучшее решение, я был бы рад выслушатьих!

И спасибо за потраченное время, чтобы помочь:)

РЕДАКТИРОВАТЬ : вот решение, которое я реализовал на данный момент, но я не могу сделатьвнедрение зависимостей непосредственно внутри декоратора, поэтому нам нужно объявить ActivatedRoute внутри конструктора с правильным именем (route).

Во-вторых, тип должен всегда быть строковым, но вы не можете вывести его из декоратора, поэтому, если кто-то напишет carName: {name: string} приложение во время сбоя во время выполнения, а не во время компиляции.

Последнее, но не менее важное, производительность может стать реальной проблемой, поскольку он переписывает ngOnInit для каждого @RouteInput и создает новый вызов функции, а для каждого @RouteInput он также проходит весь маршрут, который не изменился.

Это первый черновик, но он работает:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

function findInRoute(route: ActivatedRoute, propertyKey: string) {
  const value: string = route.snapshot.params[propertyKey];
  if (!route.parent) {
    return value;
  }

  return findInRoute(route.parent, propertyKey) || value;
}

// It will be exported in another file
function RouteInput(name?: string) {
  return (target: any, propertyKey: string) => {
    const ngOnInit = target.ngOnInit || (() => {});
    target.ngOnInit = function() {
      if (this.route instanceof ActivatedRoute) {
        const value = findInRoute(this.route, name || propertyKey);
        if (value) {
          this[propertyKey] = value;
        }
      }
      ngOnInit.bind(this)();
    };
  };
}

@Component({
  selector: 'app-car-summary',
  templateUrl: './car-summary.component.html',
  styleUrls: ['./car-summary.component.scss']
})
export class CarSummaryComponent implements OnInit {

  @RouteInput() brandId: string; // Type should be inferred from the RouteInput
  @RouteInput() carName: string;
  @RouteInput() test: string;

  constructor(private route: ActivatedRoute) {

  }

  ngOnInit() {
    console.log(this); // {brandId: '42', carName: 'mx5', route: {...}}
  }

}

1 Ответ

0 голосов
/ 21 мая 2019

Я использую параметры, чтобы поймать параметры URL

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

constructor(private route: ActivatedRoute,
        private router: Router) { }

 ngOnInit() {
    this.route.params.subscribe((params: Params) => {
      const accountId = params['accountId'];
      this.router.navigateByUrl(`accounts/${accountId}`);
 });
}
...