03. Компонент избранных. Локальное хранилище LocalStorage

Вывод списка избранных

Мы сделали функционал добавления друга в избранные. Теперь нужно вернуться к странице вывода избранных. Мы создали компонент этой страницы в начале урока. Если в адресной строке вбить http://localhost:4200/favorites, то мы убедимся, что компонент страницы работает. Но мы не можем перейти в него, используя элементы управления. Значит добавим ссылку на этот адрес.

В верхней панели поставим ссылку с круглой кнопки с иконкой. Для этого открываем на редактирование ./src/app/app.component.html и вносим одну строчку:

<div class="outer">
	<div class="toppanel">
		<a routerLinkActive="active" routerLink="/friends-list" class="button button_round"><i class="fa fa-address-book"></i></a>
		<a routerLinkActive="active" routerLink="/favorites" class="button button_round"><i class="fa fa-heart"></i></a> <!-- Новая ссылка, ведёт на /favorites -->
	</div>
	<div class="header">
		<div class="header__logo"><img class="logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="></div>
		<div class="header__header"><h1>{{ transferVarsService.title }}</h1></div>
	</div>
	<router-outlet></router-outlet>
</div>

Новая ссылка работает:

Можно заняться выводом избранных друзей. Сначала напишем шаблон, он не сложный (./src/app/favorites/favorites.component.html):

<ul class="friends-list">
	<li *ngFor="let friend of favoriteFriends"> <!-- Мы так же получим весь массив friends, а затем найдём избранных и сохраним их в массив favoriteFriends, тип данных ExtendedFriend не понадобится -->
		<span><a routerLink="/detail/{{friend._id}}">{{friend.name}}</a></span>
	</li>
</ul>

Логика компонента:

import { Component, OnInit, Inject } from '@angular/core';
import { Friend } from '../friend';
import { FriendsService } from '../friends.service';
import { TransferVarsService } from '../transfer-vars.service';

@Component({
  selector: 'app-favorites',
  templateUrl: './favorites.component.html',
  styleUrls: ['./favorites.component.css']
})
export class FavoritesComponent implements OnInit {

	title: string = "Избранные";

	friends: Friend[] = [];

	favoriteFriends: Friend[] = [];

	constructor(
		private friendsService: FriendsService,
		private transferVarsService: TransferVarsService,
		@Inject('LOCALSTORAGE') private localStorage: any
	) { }

	getFriends():void {
		this.friendsService.getFriends().subscribe(result => {
			this.friends = result;
			this.fillFavoriteFriends();
		});
	}

	fillFavoriteFriends():void {

		if (this.friends.length <= 0) { return; }

		let that = this;

		this.friends.forEach(function(item, i, arr) {

			if (that.checkValInStorage(item._id)) {
				that.favoriteFriends.push(item);
			}

		});

	}

	checkValInStorage(id: string):boolean { // проверяет по id наличие друга в локальном хранилище, функция частично взята из ./src/app/friend-detail/friend-detail.component.ts

		let fav: string = "false";

		fav = localStorage.getItem(id);

		if (fav == "true") {return true;}
		else {return false;}

	}

	ngOnInit() {

		this.getFriends();
		
		this.transferVarsService.setTitle(this.title);

	}

}


Небольшой рефакторинг

Функция checkValInStorage() получилась довольно похожей на ту, что используется в ./src/app/friend-detail/friend-detail.component.ts. Можно немного сократить эту функцию и привести к единому виду в обоих файлах. Это позволит в дальнейшем вынести логику работы с localStorage в отдельный сервис. Рассмотрим отдельно этот метод:

	checkValInStorage(id: string):boolean {

		let fav: string = "false";

		fav = localStorage.getItem(id);

		if (fav == "true") {return true;}
		else {return false;}

	}

В файле ./src/app/friend-detail/friend-detail.component.ts переменная fav была нужна для выдерживания единого стиля кода с методом favoriteChanging() и конструктором класса ExtendedFriend. Здесь же объявление отдельной переменной явно избыточно.

	checkValInStorage(id: string):boolean {

		if (localStorage.getItem(id) == "true") {return true;}
		else {return false;}

	}

Выражения в скобках вполне можно возвращать в return, скобки отдадут true или false, а значит код метода можно вообще сократить до одной строчки.

	checkValInStorage(id: string):boolean {

		return (localStorage.getItem(id) === "true");

	}

Т.к. язык TypeScript строго типизированный, то перемнные лучше сравнивать не только по значению, но и по типу, для этого мы применили оператор ===. Результирующий код:

import { Component, OnInit, Inject } from '@angular/core';
import { Friend } from '../friend';
import { FriendsService } from '../friends.service';
import { TransferVarsService } from '../transfer-vars.service';

@Component({
  selector: 'app-favorites',
  templateUrl: './favorites.component.html',
  styleUrls: ['./favorites.component.css']
})
export class FavoritesComponent implements OnInit {

	title: string = "Избранные";

	friends: Friend[] = [];

	favoriteFriends: Friend[] = [];

	constructor(
		private friendsService: FriendsService,
		private transferVarsService: TransferVarsService,
		@Inject('LOCALSTORAGE') private localStorage: any
	) { }

	getFriends():void {
		this.friendsService.getFriends().subscribe(result => {
			this.friends = result;
			this.fillFavoriteFriends();
		});
	}

	fillFavoriteFriends():void {

		if (this.friends.length <= 0) { return; }

		let that = this;

		this.friends.forEach(function(item, i, arr) {

			if (that.checkValInStorage(item._id)) {
				that.favoriteFriends.push(item);
			}

		});

	}

	checkValInStorage(id: string):boolean {

		return (localStorage.getItem(id) === "true");

	}

	ngOnInit() {

		this.getFriends();
		
		this.transferVarsService.setTitle(this.title);

	}

}


Изменим метод checkValInStorage() в ./src/app/friend-detail/friend-detail.component.ts. Напомню, он был таким:

	checkValInStorage():boolean {

		let id: string = this.friend._id;
		let fav: string = "false";

		fav = localStorage.getItem(id);

		if (fav == "true") {return true;}
		else {return false;}

	}

Нужно переделать логику применения этого метода так, чтобы он принимал параметр _id из вне.

import { Component, Input, OnInit, Inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Friend } from '../friend';
import { FriendsService } from '../friends.service';
import { TransferVarsService } from '../transfer-vars.service';

export class ExtendedFriend extends Friend {
	favorite: boolean;
	constructor(fav) {
		super();
		this._id = "";
		this.index = 0;
		this.guid = "";
		this.isActive = false;
		this.balance = "";
		this.picture = "http://placehold.it/32x32";
		this.age = 0;
		this.eyeColor = "";
		this.name = "";
		this.gender = "female";
		this.company = "";
		this.email = "";
		this.phone = "";
		this.address = "";
		this.about = "";
		this.registered = "";
		this.latitude = 0;
		this.longitude = 0;
		this.tags = [];
		this.friends = [];
		this.greeting = "";
		this.favoriteFruit = "";
		this.favorite = fav;
	}
}

@Component({
	selector: 'app-friend-detail',
	templateUrl: './friend-detail.component.html',
	styleUrls: ['./friend-detail.component.css']
})
export class FriendDetailComponent implements OnInit {

	id: string;

	friends: Friend[];

	friend: ExtendedFriend;

	title: string = 'Редактирование';

	constructor(
		private route: ActivatedRoute,
		private friendsService: FriendsService,
		private transferVarsService: TransferVarsService,
		@Inject('LOCALSTORAGE') private localStorage: any
	) { }

	getFriends():void {
		this.friendsService.getFriends().subscribe(result => {
			this.friends = result;
			this.id = this.route.snapshot.paramMap.get('id');
			this.selectFriend(this.id);
			this.friend.favorite = this.checkValInStorage(this.friend._id); // передаём свойство _id в функцию, до этого мы просто вызывали её без аргумента
		});
	}

	selectFriend(id: string):void {

		this.friend = new ExtendedFriend(false);

		let tempFriend = new Friend;

		tempFriend = this.friends.find(friend => friend._id === id);

		for (let property in tempFriend) {
			this.friend[property] = tempFriend[property];
		}

	}
	
	favoriteChanging():boolean {

		if ((!this.friend) || (!this.friend._id)) {
			return false;
		}

		let _id = this.friend._id;
		let fav: boolean = true;

		fav = !this.friend.favorite;

		this.locStorage(_id, fav);

	}

	locStorage(id: string, action: boolean):void {

		if (action) {
			localStorage.setItem(id, 'true');
		} else {
			localStorage.removeItem(id);
		}

	}

	checkValInStorage(id: string):boolean { // Новая версия метода

		return (localStorage.getItem(id) === "true");

	}
	
	ngOnInit() {

		this.getFriends();

		this.transferVarsService.setTitle(this.title);

	}

}

Результат:

Результирующие листинги изменённых файлов

<ul class="friends-list">
	<li *ngFor="let friend of favoriteFriends">
		<span><a routerLink="/detail/{{friend._id}}">{{friend.name}}</a></span>
	</li>
</ul>

import { Component, OnInit, Inject } from '@angular/core';
import { Friend } from '../friend';
import { FriendsService } from '../friends.service';
import { TransferVarsService } from '../transfer-vars.service';

@Component({
  selector: 'app-favorites',
  templateUrl: './favorites.component.html',
  styleUrls: ['./favorites.component.css']
})
export class FavoritesComponent implements OnInit {

	title: string = "Избранные";

	friends: Friend[] = [];

	favoriteFriends: Friend[] = [];

	constructor(
		private friendsService: FriendsService,
		private transferVarsService: TransferVarsService,
		@Inject('LOCALSTORAGE') private localStorage: any
	) { }

	getFriends():void {
		this.friendsService.getFriends().subscribe(result => {
			this.friends = result;
			this.fillFavoriteFriends();
		});
	}

	fillFavoriteFriends():void {

		if (this.friends.length <= 0) { return; }

		let that = this;

		this.friends.forEach(function(item, i, arr) {

			if (that.checkValInStorage(item._id)) {
				that.favoriteFriends.push(item);
			}

		});

	}

	checkValInStorage(id: string):boolean {

		return (localStorage.getItem(id) === "true");

	}

	ngOnInit() {

		this.getFriends();
		
		this.transferVarsService.setTitle(this.title);

	}

}


import { Component, Input, OnInit, Inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Friend } from '../friend';
import { FriendsService } from '../friends.service';
import { TransferVarsService } from '../transfer-vars.service';

export class ExtendedFriend extends Friend {
	favorite: boolean;
	constructor(fav) {
		super();
		this._id = "";
		this.index = 0;
		this.guid = "";
		this.isActive = false;
		this.balance = "";
		this.picture = "http://placehold.it/32x32";
		this.age = 0;
		this.eyeColor = "";
		this.name = "";
		this.gender = "female";
		this.company = "";
		this.email = "";
		this.phone = "";
		this.address = "";
		this.about = "";
		this.registered = "";
		this.latitude = 0;
		this.longitude = 0;
		this.tags = [];
		this.friends = [];
		this.greeting = "";
		this.favoriteFruit = "";
		this.favorite = fav;
	}
}

@Component({
	selector: 'app-friend-detail',
	templateUrl: './friend-detail.component.html',
	styleUrls: ['./friend-detail.component.css']
})
export class FriendDetailComponent implements OnInit {

	id: string;

	friends: Friend[];

	friend: ExtendedFriend;

	title: string = 'Редактирование';

	constructor(
		private route: ActivatedRoute,
		private friendsService: FriendsService,
		private transferVarsService: TransferVarsService,
		@Inject('LOCALSTORAGE') private localStorage: any
	) { }

	getFriends():void {
		this.friendsService.getFriends().subscribe(result => {
			this.friends = result;
			this.id = this.route.snapshot.paramMap.get('id');
			this.selectFriend(this.id);
			this.friend.favorite = this.checkValInStorage(this.friend._id);
		});
	}

	selectFriend(id: string):void {

		this.friend = new ExtendedFriend(false);

		let tempFriend = new Friend;

		tempFriend = this.friends.find(friend => friend._id === id);

		for (let property in tempFriend) {
			this.friend[property] = tempFriend[property];
		}

	}
	
	favoriteChanging():boolean {

		if ((!this.friend) || (!this.friend._id)) {
			return false;
		}

		let _id = this.friend._id;
		let fav: boolean = true;

		fav = !this.friend.favorite;

		this.locStorage(_id, fav);

	}

	locStorage(id: string, action: boolean):void {

		if (action) {
			localStorage.setItem(id, 'true');
		} else {
			localStorage.removeItem(id);
		}

	}

	checkValInStorage(id: string):boolean {

		return (localStorage.getItem(id) === "true");

	}
	
	ngOnInit() {

		this.getFriends();

		this.transferVarsService.setTitle(this.title);

	}

}


Дополнительные ссылки