I have problem with my Angular2 service. My function call to rest server, next I would like to print output but console.log shows undefined.
Main class
export class EventListComponent implements OnInit {
events: Event[];
constructor(
private eventService: EventService
) {}
loadPosts() {
console.log('loading');
this.eventService.getEvents()
.subscribe(
events => this.events = events,
err => {
console.log(err);
});
console.log(this.events); // undefined
}
ngOnInit() {
// Load comments
this.loadPosts();
}
}
And EventService
#Injectable()
export class EventService {
private eventsUrl = 'http://localhost:5000/search';
constructor(private http: Http) {}
getEvents(): Observable<Event[]> {
return this.http.get(this.eventsUrl)
.map((res: Response) => res.json())
.catch((error: any) => Observable.throw(error.json().error || 'Server error'));
}
}
Whats is wrong?
You're logging this.events before it is set. Do:
loadPosts() {
this.eventService.getEvents()
.catch(err => console.log(err))
.subscribe(
events => {
this.events = events;
console.log(this.events);
});
}
Related
Iam using the row level security in supabase with nest.js, So how can I set runtime variables safely to the DB so that I can be sure that the variables sync with each app user (due to the http request triggered the execution)?
I saw that it is possible to set local variables in a transaction but I wouldn't like to wrap all the queries with transactions.
Thanks & Regards
I tried to execute this with subscribers in nestjs it working fine . but it wont have a function like beforeSelect or beforeLoad , so i drop it
import { Inject, Injectable, Scope } from '#nestjs/common';
import { InjectDataSource } from '#nestjs/typeorm';
import { ContextService } from 'src/context/context.service';
import { DataSource, EntityManager, LoadEvent, RecoverEvent, TransactionRollbackEvent, TransactionStartEvent } from 'typeorm';
import {
EventSubscriber,
EntitySubscriberInterface,
InsertEvent,
UpdateEvent,
RemoveEvent,
} from 'typeorm';
#Injectable()
#EventSubscriber()
export class CurrentUserSubscriber implements EntitySubscriberInterface {
constructor(
#InjectDataSource() dataSource: DataSource,
private context: ContextService,
) {
dataSource.subscribers.push(this);
}
async setUserId(mng: EntityManager, userId: string) {
await mng.query(
`SELECT set_config('request.jwt.claim.sub', '${userId}', true);`,
);
}
async beforeInsert(event: InsertEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeTransactionRollback(event: TransactionRollbackEvent) {
console.log('hello')
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeUpdate(event: UpdateEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeRemove(event: RemoveEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
}
After i get to know that we can use query runner instead of subscriber . but its not working ,
also i need a common method to use all the queries
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Users } from 'src/common/entities';
import { DataSource, EntityManager, Repository } from 'typeorm';
#Injectable()
export class UsersService {
constructor(
#InjectRepository(Users) private userRepository: Repository<Users>,
private dataSource: DataSource,
private em: EntityManager,
) {}
getAllUsers(userId: string) {
const queryRunner = this.dataSource.createQueryRunner();
return new Promise(async (resolve, reject) => {
let res: any;
try {
await queryRunner.connect();
await queryRunner.manager.query(
// like this we can set the variable
`SELECT set_config('request.jwt.claim.sub', '${userId}', true);`,
);
// after setting config variable the query should return only one user by userId
res = await queryRunner.query('SELECT * FROM users');
// but it reurns every user
} catch (err) {
reject(err);
} finally {
await queryRunner.manager.query(`RESET request.jwt.claim.sub`);
await queryRunner.release();
resolve(res);
}
});
}
}
Thanks in advance....
Sorry to say, bro. But in currently state of development TypeORM does not have a feature that let us set conection variables. The roundabout for your problem is to do something like this.
/**
* Note: Set current_tenant session var and executes a query on repository.
* Usage:
* const itens = = await tenantTransactionWrapper( manager => {
* return manager.getRepository(Entity).find();
* });
*
* #param {function} callback - a function thar receives an Entity Manager and returns a method to be executed by tenantTransactionWrapper
* #param {string} providedTenantId - optional tenantId, otherwise tenant will be taken from localStorage
*/
async function tenantWrapper<R>(
callback: (manager: EntityManager) => Promise<R>,
providedTenantId?: string,
) {
const tenantId = providedTenantId || tenantStorage.get();
let response: R;
await AppDataSource.transaction(async manager => {
await manager.query(`SET LOCAL smsystem.current_tenant='${tenantId}';`);
response = await callback(manager);
});
return response;
}
Then create a custom repository to make use of the wraper a little bit simple.
const customRepository = <T>(entity: EntityTarget<T>) => ({
find: (options?: FindManyOptions<T>) =>
tenantTransactionWrapper(mng => mng.getRepository(entity).find(options))(),
findAndCount: (options?: FindManyOptions<T>) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).findAndCount(options),
)(),
save: (entities: DeepPartial<T>[], options?: SaveOptions) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).save(entities, options),
)(),
findOne: (options: FindOneOptions<T>) =>
tenantTransactionWrapper(async mng =>
mng.getRepository(entity).findOne(options),
)(),
remove: (entities: T[], options?: RemoveOptions) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).remove(entities, options),
)(),
createQueryBuilder: () => {
throw new Error(
'Cannot create queryBuilder for that repository type, instead use: tenantWrapper',
);
},
tenantTransactionWrapper,
});
And finally use our customRepository :
class PersonsRepository implements IPersonsRepository {
private ormRepository: Repository<Person>;
constructor() {
this.ormRepository = AppDataSource.getRepository<Person>(Person).extend(
customRepository(Person),
);
}
public async create(data: ICreatePersonDTO): Promise<Person> {
const newPerson = this.ormRepository.create(data);
await this.ormRepository.save(newPerson);
return newPerson;
}
public async getAll(relations: string[] = []): Promise<Person[]> {
return this.ormRepository.find({ relations });
}
I hope this may help someone and will be very glad if someone provides a better solution.
First you have to create a custom class for wrapping your userId or any stuff
custome_service.ts ==>
#Injectable()
export class UserIdWrapper {
constructor(private dataSource: DataSource) {}
userIdWrapper = (callback: (mng: QueryRunner) => Promise<any>, userId: string) => {
const queryRunner = this.dataSource.createQueryRunner();
return new Promise(async (resolve, reject) => {
let res: any;
try {
await queryRunner.connect();
await queryRunner.manager.query(
`SELECT set_config('your_variable_name', '${userId}', false)`,
);
//here is your funciton your calling in the service
res = await callback(queryRunner);
} catch (err) {
reject(err);
} finally {
await queryRunner.manager.query(`RESET your_variable_name`);
await queryRunner.release();
resolve(res);
}
});
};
}
Now here you have to call the function inside user service
user.service.ts ==>
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Users } from 'src/common/entities';
import { UserIdWrapper } from 'src/common/local-settup/userId_wrapper';
import { DataSource, EntityManager, QueryRunner, Repository } from 'typeorm';
#Injectable()
export class UsersService {
constructor(
#InjectRepository(Users) private userRepository: Repository<Users>,
private dataSource: DataSource,
private userIdWrapper: UserIdWrapper
) {}
async getAllUsers(userId: string) {
//This is your call back funciton that have to pass
const findOne = async (queryRunner: QueryRunner) => {
const res = await queryRunner.query('SELECT * FROM public.users');
return res;
};
try {
//hear we are passing the function in to the class funciton
return this.userIdWrapper.userIdWrapper(findOne, userId);
} catch (err) {
console.log(err);
}
}
}
Dont forgot to provide the custom class service inside the provider of user service.
im tryng to get the response from this http.get
getChatId(emailTo): any {
var email = emailTo
const httpOptions = {
headers: new HttpHeaders({
'Accept': 'application/json',
'Content-Type': 'application/json',
'Token': this.token_value
})
};
this.httpClient.get("https://xxxx=" + email, httpOptions)
.subscribe(data => {
console.log(data['_body']);
return data
}, error => {
console.log(error);
return error
});
}
this inside my constructor
this.getChatId(this.emailTo).then((date) => {
var docRef = firebase.firestore().collection("xxx").doc(date.response);
docRef.onSnapshot((doc) => {
this.document = doc.data()
let chats_message = [];
for (let k in this.document.messages) {
chats_message.push(this.document.messages[k]);
}
chats_message.sort(function (a, b) { return a.id - b.id; })
this.messages_chat = chats_message;
this.content.scrollToBottom(300);//300ms animation speed
console.log("Array", this.messages_chat);
})
});
but it give me this error:
vendor.js:1823 ERROR Error: Uncaught (in promise): TypeError: Cannot
read property 'subscribe' of undefined TypeError: Cannot read property
'subscribe' of undefined
Subscribe is not a function in httpclient while the request. please follow the below code
import { Component } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { HttpClient } from '#angular/common/http';
#IonicPage()
#Component({
selector: 'page-sample',
templateUrl: 'sample.html',
})
export class SamplePage {
sampleDatas: Observable<any>;
constructor(public navCtrl: NavController, public httpClient: HttpClient) {
this.films = this.httpClient.get('https://swapi.co/api/films');
this.sampleDatas
.subscribe(data => {
console.log('my data: ', data);
})
}
You should rewrite your function as an Observable to interact with the httpclient. Preferably in a service file like ChatService. You can design the http request with models or any types you receive or send.
export class ChatService {
constructor(private http: HttpClient) {}
getChatId(emailTo: string): Observable<any> {
return this.httpClient.get<any>("https://xxxx=/" + email);
}
}
Call the http request on a page with the service injected in the constructor.
constructor(private chatService: ChatService) {}
getChatId() {
this.chatService.getChatId(this.emailTo).subscribe(
result => {
// do something with result
},
error => {
// do something with error
}
);
}
EDIT
If you work with models to pass and receive data in the http request, you can define them as type. https://blog.angular-university.io/angular-http/
import { User } from '../models/user';
export class ChatService {
constructor(private http: HttpClient) {}
getChatId(emailTo: string): Observable<User> {
return this.httpClient.get<User>("https://xxxx=/" + email);
}
}
I'm creating a service for my MongoDB Stitch connections and I'm having an issue where if I refresh my page I get an error saying:
client for app 'xyxyxyxyxyxy' has not yet been initialized
And when I try to initialize it I get an error saying it has already been initialized.
client for app 'xyxyxyxyxyxy' has already been initialized
Here is my service.
import { Injectable } from '#angular/core';
import { Stitch, RemoteMongoClient, UserApiKeyCredential} from 'mongodb-stitch-browser-sdk';
#Injectable({
providedIn: 'root'
})
export class AnalyticsService {
client: any;
credential: UserApiKeyCredential;
db: any;
constructor() {
console.log(Stitch.hasAppClient('xyxyxyxyxyxy'));
if (!Stitch.hasAppClient('xyxyxyxyxyxy')) {
this.client = Stitch.initializeDefaultAppClient('xyxyxyxyxyxy');
} else {
console.log('here');
this.client = Stitch.initializeAppClient('xyxyxyxyxyxy');
//this.client = Stitch.getAppClient('xyxyxyxyxyxy');
}
this.db = this.client.getServiceClient(RemoteMongoClient.factory, 'mongodb-atlas').db('DBNAME');
}
login() {
this.credential = new UserApiKeyCredential('APIKEY');
this.client.auth.loginWithCredential(this.credential)
.then(authId => {
console.log(authId);
});
}
logout() {
this.client.auth.logout()
.then(resp => {
console.log(resp);
});
}
insertData(collectionName: string, data: {}) {
this.db.collection(collectionName).insertOne(data)
.then(resp => {
console.log(resp);
});
}
getData(collectionName: string) {
this.db.collection(collectionName).find({})
.asArray().then(resp => {
console.log(resp);
});
}
}
Change the constructor to be like this and it fix the issue.
constructor() {
if (!Stitch.hasAppClient('xyxyxyxyxyxy')) {
this.client = Stitch.initializeDefaultAppClient('xyxyxyxyxyxy');
} else {
this.client = Stitch.defaultAppClient;
}
this.db = this.client.getServiceClient(RemoteMongoClient.factory, 'mongodb-atlas').db('DBNAME');
}
I am trying to implement a catch for 401 responses and tried obtaining a refresh token based on Angular 4 Interceptor retry requests after token refresh. I was trying to implement the same thing, but I never was able to Retry that request, and I am really not sure if that is the best approach to apply the refresh token strategy.
Here is my code:
#Injectable()
export class AuthInterceptorService implements HttpInterceptor {
public authService;
refreshTokenInProgress = false;
tokenRefreshedSource = new Subject();
tokenRefreshed$ = this.tokenRefreshedSource.asObservable();
constructor(private router: Router, private injector: Injector) { }
authenticateRequest(req: HttpRequest<any>) {
const token = this.authService.getToken();
if (token != null) {
return req.clone({
headers: req.headers.set('Authorization', `Bearer ${token.access_token}`)
});
}
else {
return null;
}
}
refreshToken() {
if (this.refreshTokenInProgress) {
return new Observable(observer => {
this.tokenRefreshed$.subscribe(() => {
observer.next();
observer.complete();
});
});
} else {
this.refreshTokenInProgress = true;
return this.authService.refreshToken()
.do(() => {
this.refreshTokenInProgress = false;
this.tokenRefreshedSource.next();
}).catch(
(error) => {
console.log(error);
}
);
}
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.authService = this.injector.get(AuthenticationService);
request = this.authenticateRequest(request);
return next.handle(request).do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// do stuff with response if you want
}
}, (err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
return this.refreshToken()
.switchMap(() => {
request = this.authenticateRequest(request);
console.log('*Repeating httpRequest*', request);
return next.handle(request);
})
.catch(() => {
return Observable.empty();
});
}
}
});
}
}
The issue is that SwitchMap is never reached in...
if (err.status === 401) {
return this.refreshToken()
.switchMap(() => {
and the do operator as well...
return this.authService.refreshToken()
.do(() => {
so that took me to my authService refreshToken method...
refreshToken() {
let refreshToken = this.getToken();
refreshToken.grant_type = 'refresh_token';
refreshToken.clientId = environment.appSettings.clientId;
return this.apiHelper.httpPost(url, refreshToken, null)
.map
(
response => {
this.setToken(response.data, refreshToken.email);
return this.getToken();
}
).catch(error => {
return Observable.throw('Please insert credentials');
});
}
}
It returns a mapped observable, and I know it needs a subscription if I replaced the do in...
return this.authService.refreshToken()
.do(() => {
With subscribe I'll break the observable chain I guess. I am lost and I've playing with this for a long time without a solution. :D
I'm glad that you like my solution. I'm going to put just the final solution here but if anybody wants to know the process that I fallowed go here: Refresh Token OAuth Authentication Angular 4+
Ok, First I created a Service to save the state of the refresh token request and Observable to know when the request is done.
This is my Service:
#Injectable()
export class RefreshTokenService {
public processing: boolean = false;
public storage: Subject<any> = new Subject<any>();
public publish(value: any) {
this.storage.next(value);
}
}
I noticed that It was better if I have two Interceptors one to refresh the token and handle that and one to put the Authorization Header if exist.
This the Interceptor for Refresh the Token:
#Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {
constructor(private injector: Injector, private tokenService: RefreshTokenService) {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const auth = this.injector.get(OAuthService);
if (!auth.hasAuthorization() && auth.hasAuthorizationRefresh() && !this.tokenService.processing && request.url !== AUTHORIZE_URL) {
this.tokenService.processing = true;
return auth.refreshToken().flatMap(
(res: any) => {
auth.saveTokens(res);
this.tokenService.publish(res);
this.tokenService.processing = false;
return next.handle(request);
}
).catch(() => {
this.tokenService.publish({});
this.tokenService.processing = false;
return next.handle(request);
});
} else if (request.url === AUTHORIZE_URL) {
return next.handle(request);
}
if (this.tokenService.processing) {
return this.tokenService.storage.flatMap(
() => {
return next.handle(request);
}
);
} else {
return next.handle(request);
}
}
}
So here I'm waiting to the refresh token to be available or fails and then I release the request that needs the Authorization Header.
This is the Interceptor to put the Authorization Header:
#Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(private injector: Injector) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const auth = this.injector.get(OAuthService);
let req = request;
if (auth.hasAuthorization()) {
req = request.clone({
headers: request.headers.set('Authorization', auth.getHeaderAuthorization())
});
}
return next.handle(req).do(
() => {},
(error: any) => {
if (error instanceof HttpErrorResponse) {
if (error.status === 401) {
auth.logOut();
}
}
});
}
}
And my main module is something like this:
#NgModule({
imports: [
...,
HttpClientModule
],
declarations: [
...
],
providers: [
...
OAuthService,
AuthService,
RefreshTokenService,
{
provide: HTTP_INTERCEPTORS,
useClass: RefreshTokenInterceptor,
multi: true
},
{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptor,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {
}
Please any feedback will be welcome and if I'm doning something wrong tell me. I'm testing with Angular 4.4.6 but I don't know if it work on angular 5, I think should work.
Below interceptors do this task for you
import {
throwError as observableThrowError,
Observable,
Subject,
EMPTY,
} from 'rxjs';
import { catchError, switchMap, tap, finalize } from 'rxjs/operators';
import { Injectable } from '#angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpSentEvent,
HttpHeaderResponse,
HttpProgressEvent,
HttpResponse,
HttpUserEvent,
HttpErrorResponse,
} from '#angular/common/http';
import { StoreService } from './store.service';
import { ApiService } from './api.service';
export const tokenURL = '/315cfb2a-3fdf-48c3-921f-1d5209cb7861'; //copied from api service
#Injectable()
export class SessionInterceptorService implements HttpInterceptor {
isRefreshingToken: boolean = false;
cachedRequests = [];
tokenSubject: Subject<string> = new Subject<string>();
constructor(
private readonly store: StoreService,
private readonly ApiService: ApiService
) {}
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<
| HttpSentEvent
| HttpHeaderResponse
| HttpProgressEvent
| HttpResponse<any>
| HttpUserEvent<any>
> {
let urlPresentIndex = this.cachedRequests.findIndex(
(httpRequest) => httpRequest.url == req.url
);
if (this.isRefreshingToken && !req.url.endsWith(tokenURL)) {
// check if unique url to be added in cachedRequest
if (urlPresentIndex == -1) {
this.cachedRequests.push(req);
return this.tokenSubject.pipe(
switchMap(() => next.handle(req)),
tap((v) => {
// delete request from catchedRequest if api gets called
this.cachedRequests.splice(
this.cachedRequests.findIndex(
(httpRequest) => httpRequest.url == req.url
),
1
);
return EMPTY;
})
);
} else {
//already in cached request array
return EMPTY;
}
}
return next.handle(this.updateHeader(req)).pipe(
catchError((error) => {
console.log(error);
if (error instanceof HttpErrorResponse) {
switch ((<HttpErrorResponse>error).status) {
case 400:
return this.handle400Error(error);
case 403 || 401:
if (req.url.endsWith(tokenURL)) {
return observableThrowError(error);
} else {
this.cachedRequests.push(req);
return this.handle401Error(req, next);
}
default:
return observableThrowError(error);
}
} else {
return observableThrowError(error);
}
})
);
}
handle400Error(error) {
if (
error &&
error.status === 400 &&
error.error &&
error.error.error === 'invalid_grant'
) {
// If we get a 400 and the error message is 'invalid_grant', the token is no longer valid so logout.
return this.logout();
}
return observableThrowError(error);
}
handle401Error(req: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshingToken) {
this.isRefreshingToken = true;
return this.ApiService.refreshToken().pipe(
switchMap((newToken: string) => {
if (newToken) {
this.store.updateAccessToken(newToken);
this.tokenSubject.next(newToken);
return next.handle(this.updateHeader(this.cachedRequests[0]));
}
// If we don't get a new token, we are in trouble so logout.
return this.logout();
}),
catchError((error) => {
// If there is an exception calling 'refreshToken', bad news so logout.
return this.logout();
}),
finalize(() => {
this.isRefreshingToken = false;
})
);
}
}
logout() {
console.log('logging it out');
// Route to the login page (implementation up to you)
return observableThrowError('');
}
/*
This method is append token in HTTP request'.
*/
updateHeader(req) {
const authToken = this.store.getAccessToken();
console.log(authToken);
req = req.clone({
headers: req.headers.set('X-RapidAPI-Key', `${authToken}`),
});
return req;
}
}
For more details you can read my medium article Token-Refresh-Interceptor-retry-failed-Requests
Check it out, how it works stackblitz
I'm building an observable data service based on the following article: https://coryrylan.com/blog/angular-2-observable-data-services
In the article he used an array as an example, here I will use the user object since I'm developing the user service.
Here's what I got:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Events, SqlStorage, Storage } from 'ionic-angular';
import { Subject } from 'rxjs/Subject';
export interface DataStore {
user: Object
}
#Injectable()
export class UserService {
private baseUrl: string;
private storage: Storage;
private _user$: Subject<Object>;
private dataStore: DataStore;
constructor(
private http: Http
) {
this.baseUrl = 'http://localhost:3000';
this.storage = new Storage(SqlStorage);
this._user$ = <Subject<Object>>new Subject();
this.dataStore = {
user: { name: '' }
};
}
set user$(user: Object) {
this.storage.set('user', JSON.stringify(user));
this.dataStore.user = user;
this._user$.next(this.dataStore.user);
}
get user$() {
return this._user$.asObservable();
}
loadUser() {
return this.storage.get('user').then(
((user: string): Object => {
this.dataStore.user = JSON.parse(user);
this._user$.next(this.dataStore.user);
return this.dataStore.user;
})
);
}
login(accessToken: string) {
return this.http
.post('http://localhost:3000/login', { access_token: accessToken })
.retry(2)
.map((res: Response): any => res.json());
}
logout(): void {
this.storage.remove('user');
}
}
To authenticate I call the login() function and set the user data if everything ok.
this.userService.login(this.data.accessToken)
.subscribe(
(user: Object) => {
this.userService.user$ = user;
this.nav.setRoot(EventListComponent);
},
(error: Object) => console.log(error)
);
I feel it is better set the user data inside the service. I could do the following:
login(accessToken: string) {
return this.http
.post('http://localhost:3000/login', {
access_token: accessToken
})
.retry(2)
.map((res: Response): any => res.json())
.subscribe(
(user: Object) => {
this.userService.user$ = user;
this.nav.setRoot(EventListComponent);
},
(error: Object) => console.log(error)
);
}
But I won't be able to subscribe to the login() function in the component since it's already subscribed. How could I redirect the user if everything ok or show an alert if anything goes wrong in the component but setting the user inside the service?
In the main component I load the user data and set the rootPage:
this.userService.loadUser().then(
(user: Object) => this.rootPage = EventListComponent,
(error: Object) => this.rootPage = LoginComponent
);
I thought that calling the loadUser() function at this time I would not have to call it again, but I have to call it in all components that I need the user data:
this.user = this.userService.user$;
this.userService.loadUser();
I don't think the service is the way it should, what could I improve? Is there any better way to achieve what I want? Any example or idea?