Как стать автором
Обновить

Комментарии 14

Для начала покажу, как в $mol происходит работа с сервисами вообще и параметрами урла в частности:

id( next?: string ) {
  return this.$.$mol_state_arg.value( 'id', next )
}

Да, и это всё. Никаких токенов, параметров конструкторов, декораторов, инъекторов, хуков, возни со стримами, ручных подписок/отписок, и конечно никаких развесистых статей, как это правильно готовить.

А теперь расскажу, как это по настоящему правильно готовить в Ангуляре (пишу по памяти, много лет не трогал эту поделку):

class BaseObject {
  constructor( readonly $: Injector ) {}
}

class MyApp {
  get id() {
    return this.$.get( ActivatedRoute ).snapshot.queryParams.id ?? null
  }
  set id( id: string ) {
    this.$.get( Router ).navigate( ... )
  }
}

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

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

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

@Injectable({ providedIn: 'root' })
export class QueryParamService {
  constructor(private activatedRoute: ActivatedRoute, private router: Router) {}

  getQueryParam<T>(paramKey: string): QueryParam<T> {
    return new QueryParam<T>(paramKey, this.activatedRoute, this.router);
  }
}

использование функции useQueryParam скрыто

Как же скрыто?

@Component({ /* ... */ })
export class SomeComponent {
  protected readonly id$: QueryParam<string> = useQueryParam('id');
  //                                           ~~~~~~~~~~~~~~~~~~~
  //                           вот она родимая, прямо в компоненте
}

Я открыл файл и вижу, что здесь будет идти речь о query-параметре id.

Можем, в принципе, воспользоваться вашим решением:

@Component({ /* ... */ })
export class SomeComponent {
  protected readonly queryParamService = inject(QueryParamService);
  protected readonly id$: QueryParam<string> = 
    this.queryParamService.getQueryParam('id');
}

Если я правильно понял, под скрытыми зависимостями имеются в виду ActivatedRoute и Router . Ну в этом случае их здесь тоже нет — они скрыты в сервисе.

Получается практически то же самое, разве что у нас появилось лишнее поле queryParamService. К тому же, этот сервис providedIn: root и получает не совсем тот экземпляр ActivatedRoute.

А это важно, если мы будем работать с path-параметрами.

  1. Это работает совершенно не так, как хуки в реакте, поэтому use тут только вводит в заблуждение людей, знакомых с реактом.

  2. Если писать какую-то обертку над инжектами, то и называться он должен с inject.

  3. С query params там не всё так просто, по факту вы создали Subject прибитый к роутеру, да, мы все делали подобные, иногда декораторами, иногда сервисом. Мне нравился вариант с отдельными провайдерами для каждого параметра, в котором зашито и его имя и тип и функция конвертации. Но там надо разруливать моменты с одновременными навигейтами и взаимозависимости, чуть сложнее будет.

    Опять таки, это не use. И команда ангуляра сейчас предлагает переводить это в инпуты, сомнительно, но окей.

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

А если сравнивать не с реактом, а с вью 3? тогда больше похоже?

По статье чувствуется, что автор глубоко разбирается в теме. Поэтому рискну задать мой «детский» вопрос, внятного ответа на который я не нашёл ни на форумах ни у GitHub Copilot.

В TypeScript можно делать статические переменные и на их базе Singletons и Factories. В каких случаях их недостаточно и необходимо применять Angular’s injection?

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

и не могут заменить друг друга

А мне кажется, использование механизма инжектора всегда можно заменить использованием статической фабрики или даже синглтоном. Преимущества этого подхода в том, что это понятный механизм из классических языков программирования. Кроме того, это может проще тестироваться и быть имплементировано мультиплатформенно, например в рамках Kotlin Multiplatform.

Знаете ли Вы примеры, когда такая замена по каким-либо причинам невозможна?

Вы все же путаете патерны между собой, Dependency Injection, Factory, Singleton это паттерны, и совершенно разные, они ничего общего мижду собой не имеют, статические переменные это вообще свойство языка программирования, как вы что-то из этого хотите заменить другим у меня в голове не укладывается

статические переменные это вообще свойство языка программирования

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

Подход замечателен ещё и тем, что можно использовать интерецсы.

У меня, в моём проекте это замечательно работает. Мне интересно было бы понять, где это не сработает.

Вы пожалуй что-то не так в своем проекте делаете если решили заменить хорошо отлаженый dependency injection ангуляра на свой велосипед на статических переменных, прям аж интересно стало посмотреть на это)
Каким образом вы внедряете зависимости ангуляра тогда? Те же Router и т.д.?
Покажите кусок кода хотя бы

Показал в личке.

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории