Работа с данными в 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))));
}
}
Благодарю за внимание

Domain models
By Sergey Mell
Domain models
- 408