Подождите, пока Angular 2 загрузит/разрешит модель перед визуализацией вида/шаблона

В Angular 1.x, UI-Router был моим основным инструментом для этого. Вернувшись к обещанию "разрешить" значения, маршрутизатор просто дождался, когда обещание будет завершено, прежде чем указывать директивы.

В качестве альтернативы, в Angular 1.x, нулевой объект не приведет к сбою шаблона, поэтому, если я не возражаю временно неполному рендерингу, я могу просто использовать $digest для рендеринга после того, как promise.then() заполняет первоначально пустой объект модели.

Из двух подходов, если возможно, я предпочел бы подождать, чтобы загрузить представление, и отменить навигацию по маршруту, если ресурс не может быть загружен. Это спасает меня от работы "без навигации". EDIT: Обратите внимание, что это специально означает, что этот вопрос запрашивает Angular 2 совместимый с фьючерсами или наиболее эффективный метод для этого и просит избегать "оператора Элвиса", если это возможно! Таким образом, я не выбрал этот ответ.

Однако ни один из этих двух методов не работает в Angular 2.0. Разумеется, для этого запланировано или доступно стандартное решение. Кто-нибудь знает, что это такое?

@Component() {
    template: '{{cats.captchans.funniest}}'
}
export class CatsComponent {

    public cats: CatsModel;

    ngOnInit () {
        this._http.get('/api/v1/cats').subscribe(response => cats = response.json());
    }
}

Следующий вопрос может отражать ту же проблему: Angular 2 сделать шаблон после загрузки PROMISE. Обратите внимание, что в этом вопросе нет кода или принятого ответа.

Ответ 1

Пакет @angular/router имеет свойство Resolve для маршрутов. Таким образом, вы можете легко разрешать данные перед отображением маршрута.

Смотрите: https://angular.io/docs/ts/latest/api/router/index/Resolve-interface.html

Пример из документов с сегодняшнего дня, 28 августа 2017 года:

class Backend {
  fetchTeam(id: string) {
    return 'someTeam';
  }
}

@Injectable()
class TeamResolver implements Resolve<Team> {
  constructor(private backend: Backend) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<any>|Promise<any>|any {
    return this.backend.fetchTeam(route.params.id);
  }
}

@NgModule({
  imports: [
    RouterModule.forRoot([
      {
        path: 'team/:id',
        component: TeamCmp,
        resolve: {
          team: TeamResolver
        }
      }
    ])
  ],
  providers: [TeamResolver]
})
class AppModule {}

Теперь ваш маршрут не будет активирован до тех пор, пока данные не будут разрешены и не возвращены.

Доступ к разрешенным данным в вашем компоненте

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

  • route.snapshot.paramMap, который возвращает строку, или
  • route.paramMap, который возвращает Observable, вы можете .subscribe() to.

Пример:

  // the no-observable method
  this.dataYouResolved= this.route.snapshot.paramMap.get('id');
  // console.debug(this.licenseNumber);

  // or the observable method
  this.route.paramMap
     .subscribe((params: ParamMap) => {
        // console.log(params);
        this.dataYouResolved= params.get('id');
        return params.get('dataYouResolved');
        // return null
     });
  console.debug(this.dataYouResolved);

Я надеюсь, что это поможет.

Ответ 2

Попробуйте {{model?.person.name}}, это должно ждать, пока модель не будет undefined, а затем рендерит.

Angular 2 ссылается на этот синтаксис ?. как на оператор Elvis. Ссылка на него в документации трудно найти, поэтому вот ее копия в случае их изменения/перемещения:

Оператор Элвиса (?.) и пустые пути свойств

Оператор Angular "Элвис" (?.) является свободным и удобным способом защиты от значений null и undefined в путях свойств. Вот он, защищая от отказа рендеринга представления, если currentHero имеет значение null.

The current hero name is {{currentHero?.firstName}}

Давайте подробно рассмотрим проблему и это конкретное решение.

Что происходит, когда следующее свойство title с привязкой к данным имеет значение null?

The title is {{ title }}

Вид все еще отображает, но отображаемое значение пустое; мы видим только "Название" с ничем после него. Это разумное поведение. По крайней мере, приложение не сбой.

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

The null hero name is {{nullHero.firstName}}

JavaScript выдает ошибку нулевой ссылки, и поэтому Angular:

TypeError: Cannot read property 'firstName' of null in [null]

Хуже, весь вид исчезает.

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

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

Пока мы ждем данных, представление должно отображаться без жалобы, а пул свойств null должен отображаться как пустой, как это делает свойство title.

К сожалению, наше приложение вылетает, когда currentHero имеет значение null.

Мы могли бы кодировать эту проблему с помощью NgIf

<!--No hero, div not displayed, no error --> <div *ngIf="nullHero">The null hero name is {{nullHero.firstName}}</div>

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

The null hero name is {{nullHero && nullHero.firstName}}

Эти подходы заслуживают внимания, но они могут быть громоздкими, особенно если путь собственности длинный. Представьте, что вы защищаете нуль где-то в длинном пути свойств, таком как a.b.c.d.

Оператор Angular "Элвис" (?.) является более свободным и удобным способом защиты от нулей в путях свойств. Выражение выражается, когда оно достигает первого нулевого значения. Дисплей пуст, но приложение продолжает катиться, и ошибок нет.

<!-- No hero, no problem! --> The null hero name is {{nullHero?.firstName}}

Он отлично работает с длинными путями свойств:

a?.b?.c?.d

Ответ 3

EDIT: команда angular выпустила декоратор @Resolve. Это все еще нуждается в некоторых разъяснениях, как это работает, но до тех пор я возьму на себя другой ответ, связанный с этим, и предоставит ссылки на другие источники:


EDIT: этот ответ работает только для angular 2 BETA. Маршрутизатор не освобождается для angular 2 RC от этого редактирования. Вместо этого, при использовании angular 2 RC, замените ссылки на router на router-deprecated, чтобы продолжить использование бета-маршрутизатора.

Angular2 - лучший способ реализовать это будет через декоратор @Resolve. До этого момента самым близким факсимиле является CanActivate Декоратор компонентов, по Брэндону Робертсу. см. https://github.com/angular/angular/issues/6611

Хотя бета-версия 0 не поддерживает предоставление разрешенных значений компоненту, она была запланирована, и также существует обходное решение, описанное здесь: Использование разрешения в Angular2 Маршрутах

Пример бета-1 можно найти здесь: http://run.plnkr.co/BAqA98lphi4rQZAd/#/resolved. Он использует очень похожий способ обхода, но немного более точно использует объект RouteData, а не RouteParams.

@CanActivate((to) => {
    return new Promise((resolve) => {
        to.routeData.data.user = { name: 'John' }

Также обратите внимание, что также существует пример обхода для доступа к "разрешенным" значениям вложенного/родительского маршрута, а также к другим функциям, которые вы ожидаете, если вы использовали 1.x UI-Router.

Обратите внимание, что вам также необходимо вручную ввести любые службы, необходимые для этого, поскольку иерархия Injector angular в настоящее время недоступна в декораторе CanActivate. Простое импортирование Инжектора создаст новый экземпляр инжектора, без доступа к провайдерам из bootstrap(), поэтому вы, вероятно, захотите сохранить общую копию загрузочной инжектора. Брандонская вторая ссылка на этой странице является хорошей отправной точкой: https://github.com/angular/angular/issues/4112

Ответ 4

Установить локальное значение с помощью наблюдателя

... также не забудьте инициализировать значение с помощью фиктивных данных, чтобы избежать ошибок uninitialized.

export class ModelService {
    constructor() {
      this.mode = new Model();

      this._http.get('/api/v1/cats')
      .map(res => res.json())
      .subscribe(
        json => {
          this.model = new Model(json);
        },
        error => console.log(error);
      );
    }
}

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

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

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

Ответ 5

Внесите routerOnActivate в @Component и верните свое обещание:

https://angular.io/docs/ts/latest/api/router/OnActivate-interface.html

EDIT: Это явно НЕ работает, хотя текущую документацию можно немного интерпретировать по этой теме. См. Первый комментарий Брэндона здесь для получения дополнительной информации: https://github.com/angular/angular/issues/6611

РЕДАКТИРОВАТЬ: Соответствующая информация об объекте Auth0, как правило, не является правильной: https://auth0.com/blog/2016/01/25/angular-2-series-part-4-component-router-in-depth/

EDIT: команда angular планирует для этого создать декоратор @Resolve.

Ответ 6

Хорошее решение, которое я нашел, - это сделать на UI что-то вроде:

<div *ngIf="vendorServicePricing && quantityPricing && service">
 ...Your page...
</div

Только когда: vendorServicePricing, quantityPricing и service загружаются, страница отображается.

Ответ 7

Легкое исправление заключается в инициализации свойств объекта в init/constructor

cats.captchans.funniest

Как только обещание будет разрешено, оно свяжет фактические значения.