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

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

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

Из двух подходов, если возможно, я бы предпочел подождать, чтобы загрузить представление, и отменить навигацию по маршруту, если ресурс не может быть загружен. Это спасает меня от работы "без навигации". РЕДАКТИРОВАТЬ: Обратите внимание, что это означает, что этот вопрос запрашивает 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 с данными. Обратите внимание, что в вопросе нет кода или принятого ответа.

8 ответов

Решение

Посылка @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 {}

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

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

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

  1. route.snapshot.paramMap который возвращает строку или
  2. route.paramMap который возвращает наблюдаемый вы можете .subscribe() к.

Пример:

  // 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);

Надеюсь, это поможет.

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

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

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

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

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

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

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

The title is {{ title }}

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

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

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

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

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

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

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

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

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

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

Мы могли бы обойти эту проблему с помощью NgIf

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

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

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

У этих подходов есть свои преимущества, но они могут быть обременительными, особенно если путь свойств длинный. Представьте себе защиту от нуля где-нибудь в длинном пути свойства, таком как abcd

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

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

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

a?.b?.c?.d

РЕДАКТИРОВАТЬ: угловая команда выпустила декоратор @Resolve. Это все еще нуждается в некотором пояснении, как это работает, но до тех пор я возьму чей-либо связанный ответ здесь и предоставлю ссылки на другие источники:


РЕДАКТИРОВАТЬ: Этот ответ работает только для 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/. Он использует очень похожий обходной путь, но немного более точно использует RouteData объект, а не RouteParams,

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

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

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

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

... также не забудьте инициализировать значение фиктивными данными, чтобы избежать 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);
      );
    }
}

Предполагается, что Model - это модель данных, представляющая структуру ваших данных.

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

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

Хорошее решение, которое я нашел, состоит в том, чтобы сделать что-то вроде пользовательского интерфейса:

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

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

Реализовать routerOnActivate в вашем @Component и верни свое обещание:

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

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

РЕДАКТИРОВАТЬ: Связанная информация на обычно иначе точном сайте Auth0 не верна: https://auth0.com/blog/2016/01/25/angular-2-series-part-4-component-router-in-depth/

РЕДАКТИРОВАТЬ: команда Angular планирует для этой цели декоратор @Resolve.

Я использую Angular 6 с надстройкой Universal. Я прочитал ответы, особенно второй. Но я еще не понял суть. У меня есть распознаватель, который вызывает и HttpClient XHR запрос и отправить компоненту, и компонент на ngOnInit будет привязывать данные к представлению. Но проблема в режиме просмотра источника, нет данных из ответа XHR, который отображается в виде.

Простое решение - инициализировать свойства объекта в init/constructor.

cats.captchans.funniest

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

Другие вопросы по тегам