A2: получить данные от апи и объявить переменные глобально
Помогите получить данные пользователя из АПИ, присвоить переменным, которые использовать в любом из компонентов
Делаю так: АПИ: {"login": "test", "ban": 0} Корневой app.module.ts: ... import { UserService } from './shared/user.service'; ... providers: [ UserService ], ... user.service.ts Вот тут начинаются проблемы, мое понимание углового пока на уровне копипаста import {Injectable} from '@angular/core'; import {HttpClient} from '@angular/common/http'; export class User { login: string; ban: number; } @Injectable() export class UserService { constructor(private http: HttpClient) { } user: User ngOnInit() { this.http.get('/server/api/userService').subscribe((data: User) => { this.user = data; test = this.user['login']; // <- В этом моменте непонятности } ); } } Помогите хотя бы для начала записать в консоль логин пользователя, typescript пока вообще не понимаю Правильно ли вообще я вижу алгоритм? 1) Сервисом загружаю данные с сервера, присваиваю переменным значения 2) Загружаю сервис в главный модуль 3) В любом из компонентов, без дополнительных инклудов могу использовать переменные {{user.login}} |
Здравствуйте. Я бы вам советовал прочитать о таком явлении как сервисы и dependency injection. Хотябы здесь: metanit.com
Ваш код по сути просто недописан. У вас есть сервис, который уже читает данные из АПИ. Сам факт объявления его как сервис делает его доступным внутри всего приложения. Осталось расширить интерфейс сервиса методами доступа к даннам из внешних компонентов. Дописываем UserService: getUserInfo(){ return this.user; } Встраиваем сервис в компонент. Например в AppComponent. Опускаю здесь ненужные подробности и возможно допуская кое-какие ошибки. Вам ведь нужен подход? :о) : export class AppComponent { currentUser: User; constructor( userServise: UserService // Вот момент встраивания сервиса ){} onInit(){ this.currentUser = this.userServise.getUserInfo(); } } |
sniffysko, спасибо за помощь, но пока не получается.
Вот так работает, но в пределах одного компонента. А на остальные не распространяется. А я хотел, чтобы переменные {{ user?.login }} были видны глобально, во всех шаблонах app.component.ts import { Component } from '@angular/core'; //import { UserService, User } from './shared/user2.service'; import { HttpClient } from '@angular/common/http' export class User { login: string; ban: number; } @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent { user: User constructor(private http: HttpClient) {} ngOnInit() { this.http.get('/server/api/userService').subscribe((data: User) => (this.user = data)) } } |
Vadya,
Ваш код не работает, потому что такие хуки как ngOnInit они как бы для компонентов. Снаружи никто метод этого класса не вызывает судя по коду. import {Injectable} from '@angular/core'; import {HttpClient} from '@angular/common/http'; import { of } from 'rxjs'; export class User { login: string; ban: number; } @Injectable() export class UserService { constructor(private http: HttpClient) {} user: User getUser() { if (this.user) { return of(this.user); } return this.http.get('/server/api/userService').pipe( tap((data: User) => this.user = data) ); } } |
Перенес этот код в отдельный файл, user.module.ts
В app.module.ts сделал импорт и providers: [ UserService ] Теперь непонятно, как этот сервис соберет данные и расшарит их для всех компонентов. Ведь даже запроса к апи он не делает Мне где то надо единожды вызвать функцию getUser? Пока не понимаю даже как вызвать ее в шаблоне компонента |
Vadya,
Нужно вызывать метод getUser сервиса UserService в каждом из компонентов, нуждающихся в юзере. Например, так export class AppComponent { currentUser: User; constructor( userServiсe: UserService // Вот момент встраивания сервиса ){} onInit(){ this.userServiсe.getUser().subscribe(user => this.currentUser = user); } } |
Получается по сути это как у меня здесь: https://javascript.ru/forum/518052-post3.html (#3)?
То есть если мне нужны данные пользователя в header.component и footer.component, то из каждого компонента будет произведен запрос к серверу? |
Цитата:
|
Вот теперь все встало на свои места. Я почему-то думал, что в угловом как-то можно сделать все в одном месте и после этого не трогать модули компонентов.
|
У меня еще один вопрос появился. Данные пользователя я получил, а в роутер передать через функцию не могу. Вот код с комментарием, где не получается:
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from "@angular/router"; import { Observable } from "rxjs"; import { UserService } from './user.service'; import { Injectable } from '@angular/core'; @Injectable() export class AuthGuard implements CanActivate { user: any; constructor(private userService: UserService, private router: Router) { } public isAuthenticated(): any { this.userService.getUser().subscribe(data => { this.user = data; if(this.user['login'] !== false) {return true;} }); } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) : Observable<boolean> | boolean{ if(this.isAuthenticated()) { // <- Сюда не проходит true return true; } else { this.router.navigate(['/login']); } } } |
Правильно. Не работает. Поэтому есть 2 совета:
1. Вынести функцию isAuthenticated из гуарда в сервис юзеров. Это, так сказать, разделение ответственносте. Гуарды гуардят, а юзерсервисы юзерсервисят. 2. Проставление типов any облегчает жизнь нерадивому разрабу, но следующего по пути дзена просветляют и научает. Ибо, если бы у вас стоял не any, а Observable<User>, то вы бы поняли, что совать его в условный оператор бессмысленно. Функция асинхронная. Тут конечно вопрос: у вас пользователь, которого вы сравниваете в 17 строке где-то хранится локально? Если да, то и асинхрон не нужен. Если вы его читаете с сервера, то нужен. Но тут такая ситуация, ежли вы по каждому перемещению по роутам будете сервер дергать, то это очень не хорошо. Итог: 1. isAuthenticated -- несем в изер сервис 2. Юзера читаем при инициализации и храним в том же юзер сервисе, либо создаем authService. 3. При перемещении по роуту избавляемся от асинхрона и выполняем обычные синхронные функции. 4. Если асинхронный подход нужен, то функцию isAuthenticated надо переписать под Observable. 5. Типизируем! Ангуляр довольно сложен, а типы помогут вам определиться с тем, что вы ожидаете получить. Если вас разочаровывают ошибки в процессе написания это говорит, что вы что-то не поняли и надо копать в этом направлении. |
Для меня это пока темный лес. Сохранил на потом, а пока хоть как-то сделать, но чтобы заработало.
Уже вторые сутки методом тыка пытаюсь вернуть логин из функции. public isAuthenticated(): any { this.userService.getUser().subscribe(data => (this.user = data)); // Здесь как то надо увидеть this.user.login } |
Цитата:
Для Юзер сервиса дописываем функцию: export class UserService{ isAutentificated: boolean = false; userInfo: UserInfo constructor( http: HttpService ){} // Синхронная! isAuthenticated(): boolean { return this.isAutentificated; } // Асинхронная authUser(data: {login: string, password}){ this.gttp.post('api/url').subscribe( (data: UserInfo) => { if(data){ this.userInfo = data; this.isAutentificated = true; } } ); } } authUser вызываем после того, как в форме логина вводим логин и пароль. Из компонента логина. А теперь ваш код в гуарде: canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) : Observable<boolean> | boolean{ if(this.userService.isAuthenticated()) { // Сейчас придет. Функция синхронная. return true; } else { this.router.navigate(['/login']); } } Если isAuthenticated все-же должен быть асинхронным, то подход другой. Что-то типа такого (вы же понимаете, что и код функции isAuthenticated будет через обсерверы и не такой как в примере выше): canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) : Observable<boolean> | boolean{ return this.userService.isAuthenticated() .subscribe( (data: boolean) => { return data; }); } Что-то типа так. Пусть меня поправят более опытные товарищи. Пишу навскидку. Но смысл таков: если функция асинхронная, подписываемся на нее и полученный в подписке рузультат возвращаем. Когды вы выставите тыпы в вашем коде среда разработки подскажет вам какие функции можно вписать. |
Это в userService
public isAuthenticated(): Observable<boolean> { return this.getUser().pipe( map(data => !!data['login']) ); } это в authGuard canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) : Observable<boolean>{ return this.userService.isAuthenticated().pipe( tap((isAuthenticated: boolean) => { if (!isAuthenticated) { this.router.navigate(['/login']) } }) ); } |
sniffysko, destus, огромное спасибо за примеры. Буду вникать поглубже, но теперь уже только после нг.
Если честно, то я даже не понимаю пока как оформлять функцию синхронной или наоборот. Жс у меня со скрипом всегда шел и понять его никак не могу. Вообще я делаю простую админку, не для массового использования, с целью познакомится с ангуляром. Это последнее из того что осталось сделать, чтобы был каркас, дальше уже думаю справлюсь самостоятельно. Я так вижу: мне нужена синхронная функция, так как проверка авторизации нужна только при переходе между страницами и при совершении каких-либо действий на сервере. Зачем асинхронная в данном случае не понимаю, но было бы интересно. Всех фронтовиков с наступающим! И до новых встреч ) |
Не актуально
А можете объяснить (неудобно наглеть, но желательно с примером), почему в этой конструкции не работает this.router.navigate(['/login']); Там конечно легко накостылировать на ЖС, но хочется правильно все сделать // this.router.navigate(['/login']); // Тут работает return this.userService.isAuthenticated().pipe( tap((isAuthenticated: boolean) => { if (!isAuthenticated) { this.router.navigate(['/login']); // А тут нет } else { return true; } }) ); В общем, как часто бывает, немного затупил - зацикливалось Хотел сделать так: { path: 'login', component: LoginComponent, canActivate: [AuthGuard] } Чтобы страницу логина было видно в canActivate и если авторизация есть, то переадресация на главную return this.userService.isAuthenticated().pipe( tap((isAuthenticated: boolean) => { if (!isAuthenticated && route.url.join() != 'login') { // но почему-то это условие не срабатывает (&& route.url.join() != 'login'). Если нет авторизации бросает на страницу логина, а потом с нее же на нее. Это я так понимаю, а вообще в корень отправляет. Без ошибок в консоли this.router.navigate(['/login']); } else { return true; } |
Часовой пояс GMT +3, время: 21:03. |