Работа с данными в Angular

Исходные данные

[
  {
    "id": 1,
    "first_name": "James", 
    "last_name": "Hetfield",
    "position": "Web developer"
  },
  {
    "id": 2,
    "first_name": "Elvis", 
    "last_name": "",
    "position": "Project manager"
  },
  {
    "id": 3,
    "first_name": "Steve", 
    "last_name": "Vai",
    "position": "QA engineer"
  }
]

Создаем сервис

import ...

@Injectable()
export class UserService {

  constructor(private http: HttpClient) { }

  getUsers(): Observable<UserServerResponse[]> {
    return this.http.get<UserServerResponse[]>('/users');
  }

  getUserAvatar(userId: number): string {
    return `/users/${userId}/avatar`;
  }
}

Создаем компоненту

import ...

@Component({
  selector: 'app-user-list',
  template: `
    <div *ngFor="let user of users | async">
      <img [src]="userService.getUserAvatar(user.id)">
      <p><b>{{user.first_name}} {{user.last_name}}</b>, {{user.position}}</p>
    </div>
  `
})
export class UserListComponent {

  users = this.userService.getUsers();

  constructor(public userService: UserService) { }
}

Проблема?

Возможные решения

1. Поправить шаблон компоненты

<p>
  <b>{{[user.first_name, user.last_name].filter(el => !!el).join(' ')}}</b>, 
  {{user.position}}
</p>

Возможные решения

2. Добавить метод в класс компоненты

getUserFullName(user: UserServerResponse): string {
  return [user.first_name, user.last_name].filter(el => !!el).join(' ');
}

3. ... или в сервис

Источник проблемы?

Мы получаем объекты,
но не работаем с ними
как с объектами

Нам нужны модели!

Модель здорового программиста

export class User {

  readonly id;
  readonly firstName;
  readonly lastName;
  readonly position;

  constructor(userData: UserServerResponse) {
    this.id = userData.id;
    this.firstName = userData.first_name;
    this.lastName = userData.last_name;
    this.position = userData.position;
  }

  fullName(): string {
    return [this.firstName, this.lastName].filter(el => !!el).join(' ');
  }

  avatar(): string {
    return `/users/${this.id}/avatar`;
  }
}

Поправим сервис

getUsers(): Observable<User[]> {
  return this.http.get<UserServerResponse[]>('/users')
    .pipe(map(listOfUsers => listOfUsers.map(singleUser => new User(singleUser))));
}
getUsers(): Observable<UserServerResponse[]> {
  return this.http.get<UserServerResponse[]>('/users');
}

Было...

...стало

Новая компонента

import ...

@Component({
  selector: 'app-user-list',
  template: `
    <div *ngFor="let user of users | async">
      <img [src]="user.avatar()">
      <p><b>{{user.fullName()}}</b>, {{user.position}}</p>
    </div>
  `
})
export class UserListComponent {

  users = this.userService.getUsers();

  constructor(private userService: UserService) { }
}

Расширим возможности модели

export class User {

  // ...

  constructor(userData: UserServerResponse, 
              private http: HttpClient, 
              private storage: StorageService, 
              private auth: AuthService) {
    // ...
  }
  
  // ...

  updateAvatar(file: Blob) {
    const data = new FormData();
    data.append('avatar', file);
    return this.http.put(`/users/${this.id}/avatar`, data);
  }
}

Создать объект стало сложнее...

export class PostService {

  constructor(private http: HttpClient, 
              private storage: StorageService, 
              private auth: AuthService) {
  }

  foo() {
    // ...
    new User(userData, this.http, this.storage, this.auth);
  }
}

Можно использовать Injector

export class PostService {

  constructor(private injector: Injector) { }

  foo() {
    // ...
    new User(userData, this.injector);
  }
}
export class User {

  constructor(userData, private injector: Injector) { }
  
  someAction() {
    const http = this.injector.get(HttpClient);
    // ..
  }
}

Статическое внедрение зависимостей

export let InjectorInstance: Injector;

export class AppModule {

  constructor(private injector: Injector) {
    InjectorInstance = this.injector;
  }
}
export class User {

  constructor(userData) { }
  
  someAction() {
    const http = InjectorInstance.get(HttpClient);
    // ..
  }
}

Делегируем создание объектов

import ...

@Injectable()
export class UserService {

  constructor(private http: HttpClient, private storage: StorageService, 
              private auth: AuthService) { }
  
  createUser(userData: UserServerResponse) {
    return new User(userData, this.http, this.storage, this.auth);
  }

  getUsers(): Observable<User[]> {
    return this.http.get<UserServerResponse[]>('/users')
      .pipe(map(listOfUsers => listOfUsers.map(singleUser => this.createUser(singleUser))));
  }
}

Благодарю за внимание

Made with Slides.com