Angular에서 필요한 데이터 검색을 기다렸다가 렌더링 [Resolve]

15100 단어 Angular

Cannot read property '****' of undefined



api에서 데이터를 가져와서 표시하려고 하면 다음과 같은 오류가 발생했습니다.
해당 'name'속성은 표시되지만 오류가 나오는 것은 기분 나쁘다고 느꼈기 때문에 해결책을
조사했습니다.


버전



Angular CLI: 8.3.20
Node: 10.12.0
OS: darwin x64
Angular: 8.2.13

문제 코드



hoge.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

// 省略
export class HogeService {

  constructor(private http: HttpClient) { }

  getItem(id: string): Observable<any> {
    return this.http.get<any>('https://fugafuga.com/api/v2/items/' + id);
  }
}

app-routing.module.ts
// 省略
import { HogeComponent } from './hoge/hoge.component';

const routes: Routes = [
// 省略
  {
    path: 'hoge/:id',
    component: HogeComponent
  }
];

// 省略

hoge.component.ts
import { Component, OnInit } from '@angular/core';
import { HogeService } from '../hoge.service';
import { ActivatedRoute} from '@angular/router';

// 省略
export class HogeComponent implements OnInit {
  item: any;

  constructor(private route: ActivatedRoute, private hogeService: HogeService) { }

  ngOnInit() {
    const id = this.route.snapshot.paramMap.get('id');
    this.hogeService.getItem(id).subscribe(
      item => {
        this.item = item;
      });
  }
}

hoge.component.html
{{ item.name }}

가능한 원인


  • undefined 로 되어 있는 오브젝트의 타입 미스 → → 표시가 되어 있으므로 해당하지 않고
  • 필요한 데이터의 취득이 완료하기 전에 렌더링 하고 있다 ←   이쪽이었습니다.

    해결책 1 "?" 연산자 사용


  • 정식 명칭을 safe navigation operator 이라고 합니다.
  • 연산자 앞의 객체가 null 또는 undefined이면 이후에는 평가하지 않습니다.
  • a?.b?.c?.d 계속 사용할 수 있습니다

  • 이 경우, {{ item?.name }} 라고 하면(자) 에러는 표시되지 않게 되었습니다.
    그렇다고는 해도, 에러가 나올 가능성이 있는 오브젝트에 매회 ? 를 붙이는 것은 번거롭기 때문에, 다음의 방법을 사용했습니다.

    솔루션 2 Resolve 사용



  • Resolve 인터페이스을 구현 한 서비스를 만들고 라우팅 설정에 resolve 객체를 추가합니다.
  • 이렇게하면 필요한 데이터 검색을 기다린 다음 렌더링 할 수 있습니다.

    절차



    resolver 서비스 만들기


    ng g s hoge-resolver
    

    이름은 무엇이든 좋다고 생각합니다. 여기에서는 hoge-resolver 로 했습니다.
    다음 코드가 자동으로 생성됩니다.

    hoge-resolver.service.ts
    import { Injectable } from '@angular/core';
    
    @Injectable({
      providedIn: 'root'
    })
    export class HogeResolverService {
    
      constructor() { }
    }
    

    Resolve 인터페이스 구현



    hoge-resolver.service.ts
    import { Injectable } from '@angular/core';
    import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
    import { Observable } from 'rxjs';
    import { HogeService } from './hoge.service';
    
    @Injectable({
      providedIn: 'root'
    })
    export class HogeResolverService implements Resolve<any> {
    
      constructor(private hogeService: HogeService) { }
    
      resolve(route: ActivatedRouteSnapshot): Observable<any> {
        const id = route.paramMap.get('id');
        return this.hogeService.getItem(id);
      }
    }
    

    라우팅 설정에 resolve 객체 추가



    app-routing.module.ts
    // 省略
    import { HogeComponent } from './hoge/hoge.component';
    
    const routes: Routes = [
    // 省略
      {
        path: 'hoge/:id',
        component: HogeComponent,
        resolve: {
          item: HogeResolverService
        }
      }
    ];
    // 省略
    

    여기서 resolve 내용의 item 는 나중에 참조하기 위한 이름이고 HogeResolverService 는 방금 만든 서비스입니다.

    구성 요소에서 참조


    ActivatedRoute.data를 subscribe하여 이전 item를 참조할 수 있습니다.

    hoge.component.ts
    import { Component, OnInit } from '@angular/core';
    import { ActivatedRoute} from '@angular/router';
    
    // 省略
    export class HogeComponent implements OnInit {
      item: any;
    
      constructor(private route: ActivatedRoute) { }
    
      ngOnInit() {
        this.route.data.subscribe(data => this.item = data.item);
      }
    }
    

    이상으로 오류가 해결되었습니다.

    결론



    Resolve는 routing을 제어한다 route-guards 중 하나인 것 같기 때문에, 다른 기능도 기회가 있으면 배우고 싶습니다.

    참고



    Angular 공식 fetch-data-before-navigating
  • 좋은 웹페이지 즐겨찾기