Subscribe Observable<Type> - javascript

Subscribe to Observable:
checkAllowEmail(control: FormControl) {
this.userService.getUserByEmail(control.value)
.subscribe((user: UserDto) => {
console.log(user);
if (user !== undefined) {
console.log(this.isAllowEmail);
this.isAllowEmail = false;
console.log(this.isAllowEmail);
}
});
}
Return Observable from method:
getUserByEmail(email: string): Observable<UserDto> {
return this.http.get(`http://localhost:9092/api/v1/users?email=${email}`)
.map((response: Response) => response.json())
.map((user: UserDto) => user ? user : undefined);
}
Class UserDto:
export class UserDto {
constructor(
public email: string,
public name: string,
public role: string
) {}
}
Response from BE-side:
{"name":"art","email":"art#mail.ru","role":"user"}
Why I can change variable isAllowEmail to false in if statement in checkAllowEmail method?

I don't think you still have to use the map function, when using the HttpClient (since angular version 4). Try using
getUserByEmail(email: string): Observable<UserDto> {
return this.http.get<UserDto>(`http://localhost:9092/api/v1/users?email=${email}`);
}
Else use something like Postman or even curl from the console to verify that you api returns the correct/expected responses.
Also check if you use 'use strict'; in that case this will refere to the callable you pass to the subscribe method and not the surrounding component class.

Related

Listen for value changes in an object property, slice and subscribe to the changed value

I use the Async local-storage with angular to store data in the indexedDB.
The library has a method to listen to / watch for changes.
This method returns an Observable with the complete object.
What I want is to slice the changed property value, object, or the array of the object and subscribe to it.
My example object would be:
{
fetched: boolean,
loaded: boolean,
item: null, // object
list: [], // object arry
}
Now I need to watch for the changes in each property as an Observable.
fetched$.subscribe((fetched: boolean)) => {}
loaded$.subscribe((loaded: boolean)) => {}
item$.subscribe((item: any)) => {}
list$.subscribe((list: any[])) => {}
Here is the code I used to slice the changed value so far.
// slice an individual value
function sliceValue<T>(
repo: BaseRepository<T>,
key: string
): Observable<any> {
if (!repo.key || !repo.store.has(repo.key)) {
return of (null);
}
return repo.store.watch(repo.key).pipe(
distinctUntilChanged((prev, curr) => {
if (!prev || !curr) return false;
return prev[KEY.D][key] === curr[KEY.D][key];
}),
switchMap((ctx: any) => repo.store.get(repo.key)),
map((ctx: any) => ctx[KEY.D][key]));
}
// State class
export class TestState extends BaseRepository<TestModel> {
public fetched$: Observable<boolean> = sliceValue(this, 'fetched');
public loaded$: Observable<boolean> = sliceValue(this, 'loaded');
public item$: Observable<any> = sliceObject(this, 'item');
public list$: Observable<any[]> = sliceList(this, 'list');
constructor(
public readonly store: StorageMap,
) {
super(store);
}
}
But I do not know the efficient way to do it.
If there is any other best way to do it, I would like to know and try it out.
Thank you very much.

Angular TypeScript: waiting for a void method to run before another method

I have a method in an Angular component that pulls data via HttpClient subscription, and assigns it to an attributes this.allData, then instantiates an empty dictionary of parameters based on this, to pass to a second function:
export class TestComponent implements OnInit {
allData: object[] = []
activeData: object = {}
constructor(private http: HttpClient) { }
ngOnInit(): void {
this.getData()
this.makeRequestBasedOnData()
}
getData() {
this.http.get(this.url).subscribe(res => {
for (let datum of res["data"]) {
this.allData.push({
"key": Object.keys(datum)[0],
"values": Object.values(datum)[0]
})
this.activeData[Object.keys(datum)[0]] = ""
}
})
}
makeRequestBasedOnData() {
let testParams = this.activeData
console.log(testParam)
}
}
I need these steps to happen sequentially. At the moment, logging the testParams in makeRequestBasedOnData() simply shows an empty object {}. When I try to return arbitrarily in the first method, I get a TypeScript error that you cannot assign a promise to type void.
How do I enforce synchronicity here even though neither method actually returns anything?
You can return Observable from getData method and proceed with any other methods within subscribe:
ngOnInit(): void {
this.getData().subscribe(() => this.makeRequestBasedOnData());
}
getData() {
return this.http.get(this.url).pipe(
tap(res => {
// update allData
})
);
}
where:
pipe method allows us to provide any kind of transformation with the data returned from http.get(...) call.
tap is a rxjs operator for side-effects, meaning that we can do everything we want with the response without modifying Observable flow.

How to use query parameters in Nest.js?

I am a freshman in Nest.js.
And my code as below
#Get('findByFilter/:params')
async findByFilter(#Query() query): Promise<Article[]> {
}
I have used postman to test this router
http://localhost:3000/article/findByFilter/bug?google=1&baidu=2
Actually, I can get the query result { google: '1', baidu: '2' }. But I'm not clear why the url has a string 'bug'?
If I delete that word just like
http://localhost:3000/article/findByFilter?google=1&baidu=2
then the postman will shows statusCode 404.
Actually, I don't need the word bug, how to custom the router to realize my destination just like http://localhost:3000/article/findByFilter?google=1&baidu=2
Here's another question is how to make mutiple router point to one method?
Query parameters
You have to remove :params for it to work as expected:
#Get('findByFilter')
async findByFilter(#Query() query): Promise<Article[]> {
// ...
}
Path parameters
The :param syntax is for path parameters and matches any string on a path:
#Get('products/:id')
getProduct(#Param('id') id) {
matches the routes
localhost:3000/products/1
localhost:3000/products/2abc
// ...
Route wildcards
To match multiple endpoints to the same method you can use route wildcards:
#Get('other|te*st')
will match
localhost:3000/other
localhost:3000/test
localhost:3000/te123st
// ...
If you have you parameter as part or url: /articles/${articleId}/details, you wold use #Param
#Get('/articles/:ARTICLE_ID/details')
async getDetails(
#Param('ARTICLE_ID') articleId: string
)
IF you want to provide query params /article/findByFilter/bug?google=1&baidu=2, you could use
#Get('/article/findByFilter/bug?')
async find(
#Query('google') google: number,
#Query('baidu') baidu: number,
)
We can use #Req()
import { Controller, Get, Req } from '#nestjs/common';
import { Request } from 'express';
(...)
#Get(':framework')
getData(#Req() request: Request): Object {
return {...request.params, ...request.query};
}
/nest?version=7
{
"framework": "nest",
"version": "7"
}
read more
You can use the #Req decorator, and use param object, see :
#Get()
findAll(
#Req() req: Request
): Promise<any[]> {
console.log(req.query);
// another code ....
}
For better explaining I wrote a pagination example with number transformer class:
class QueryDto {
#Type(() => Number)
#IsInt()
public readonly page: number;
#Type(() => Number)
#IsInt()
public readonly take: number;
}
#Injectable()
class QueryTransformPipe implements PipeTransform {
async transform(value: QueryRequestDto, { metatype }: ArgumentMetadata) {
if (!metatype) {
return value;
}
return plainToInstance(metatype, value);
}
}
#Controller()
class YourController {
#Get()
// also you can use it with pipe decorator
// #UsePipes(new QueryTransformPipe())
public async getData(#Query(new QueryTransformPipe()) query?: QueryRequestDto): Promise<any[]> {
// here you get instanceof QueryTransformPipe
// and typeof query.page === 'number' && typeof query.take === 'number'
}
}

angular load local file then make http calls to return observables

I am new to Angular, JS, and observables. I have a typescript class called DataService. I want it to load a list of URLs from a JSON formatted local file, and then have some way to call those URLs (to a handful of REST APIs) and return observables. The problem I am having is my code is not waiting for the config file to be loaded before the REST API functions get called.
I thought I could have the DataService constructor load the configuration file, and then have unique functions for each REST API call, but that isn't working
my code:
export class DataService {
configFile
constructor(private http: HttpClient) {
this.http.get('/assets/restApiUrlListConfig.json').subscribe(config => {
this.configFile = config;
});
}
getUrlFromConfigFile(name: string): string {
...
this returns the URL from the config file
...
}
getUrlAData(): Observable {
return this.http.get( getUrlFromConfigFile('A') )
}
}
My other components have code like this:
export class SomeComponent implements OnInit {
someComponentAData
constructor(private data: DataService) { }
ngOnInit() {
this.data.getUrlAData().subscribe(
data => {
this.someComponentAData = data
}
)
}
I am getting an error that the observable returned from the dataservice is undefined. Which I believe is because the constructor hasn't finished loading the config file, which I think is why the function getUrlAData isn't returning anything.
I feel like I'm not correctly handling these async calls, but I'm at a loss for how to tell my code to :
create the data service object
load the data file before anything else can be done
allow the other functions to be called asyncronously AFTER the config file is loaded
Angular CLI: 6.2.3
Node: 8.12.0
OS: win32 x64
Angular: 6.1.8
Edit 1: attempting to implement suggested solution
My DataService
configFile
configObservable: Observable<any>;
someSubscribeObj
constructor(private http: HttpClient) {
this.someSubscribeObj = this.http.get('/assets/restApiUrlListConfig.json').subscribe(config => {
this.someSubscribeObj = undefined;
this.configFile = config;
});
}
getObsFromConfigFile(name: string): Observable<any> {
//...
if (this.configFile != undefined) {
console.log('this.restApiUrlListConfig[name]',this.configFile[name])
return of(this.configFile[name])
}
else
return of(this.someSubscribeObj.pipe(map(c => c[name])))
//this.configObservable
//...
}
getUrlAData(): Observable<any> {
return this.getObsFromConfigFile('A').pipe(mergeMap(url => this.http.get(url)))
}
My other component:
constructor( private data: DataService ) { }
ngOnInit() {
//this.data.loggedIn.pipe((p) => p);
this.data.getUrlAData().subscribe(
data => {
this.urlAData = data
}
)
}
I was unable to store the "subscribe" into the observable, so I created a generic Any type varable, but at runtime I get a problem with the pipe command:
TypeError: this.someSubscribeObj.pipe is not a function
at DataService.push../src/app/services/data.service.ts.DataService.getObsFromConfigFile
(data.service.ts:67)
at DataService.push../src/app/services/data.service.ts.DataService.getUrlAData
(data.service.ts:74)
Edit 2: the unfortunate workaround
I am currently using two nested subscriptions to get the job done basically
http.get(config_file_url).subscribe(
config => {
http.get( config['A'] ).subscribe( adata => { do things };
http.get config['B'].subscribe( bdata => {do things };
}
)
I feel like I should be able to use a mergeMap of some sort, but I couldn't get them to work as I thought they would.
You need to wait on that async call, I would use a flatmap to get the value out of an observable.
export class DataService {
configFile
configObservable: Observable<any>;
constructor(private http: HttpClient) {
this.configObservable = this.http.get('/assets/restApiUrlListConfig.json').pipe(
map(config => {
this.configObservable = undefined;
this.configFile = config;
return configFile;
})
);
}
getUrlFromConfigFile(name: string): Observable<string> {
...
return of(configFile[name]) if configFile is set else return configObservable.pipe(map(c => c[name]));
...
}
getUrlAData(): Observable<string> {
return this.getUrlFromConfigFile('A').pipe(map(url => this.http.get(url)))
}
}
Basically you want to store the observable and keep using it till it completes, after it completes you can just wrap the config in an observable. The reason for wrapping it is to make the interface consistent, otherwise you have to have an if before every get.

Angular 6 - How to assign a member to a result of a subscribtion in

I have the following code:
m_SystemOptions: KeyValueEntity[];
OnInitializeFramework(): any
{
this.GetSystemSettings().subscribe(
response =>
{
if (response.IsSuccess)
{
this.m_SystemOptions = response.Entities;
}
else
{
this.UnexpectedMessage(response.ResponseMessage);
}
});
}
Ideally, I would like it to be something like this:
this.m_SystemOptions = this.GetSystemSettings().SomeMagic()
or
this.m_SystemOptions = SomeMagic(this.GetSystemSettings());
or at the very worst:
SomeMagic(this.GetSystemSettings(), this.m_SystemOptions);
this.GetSystemSettings is a function that returns the same type as this.m_SystemOptions
Since my code has this structure dozens of times, I would like to shorten it
All I am missing is SomeMagic()
EDIT:
I missed a small but important part
GetSystemSettings() does not return the same type directly but wrapped in some control structure that is shared across the application
Inside of it there is basically just a call to httpClient . get() that returns a subscribtion
I would like not to touch that part, its fine and generic
What I would want is to remove the repeating part I have posted above that handles both response success and failure, and change it to something more generic to make the code in the component more readable
export class BaseResponse
{
IsSuccess: boolean;
ResponseCode: number;
ResponseMessage: string;
ResponseExtendedMessage: string;
}
export abstract class BaseEntitiesResponse<TEntity extends BaseEntity> extends BaseResponse { Entities: TEntity[] }
export class KeyValueEntity extends BaseEntity
{
Key: string;
Value: string;
}
//Get the settings
private GetSystemSettings(): Observable<BaseEntitiesResponse<KeyValueEntity>>
{
return this.api.ExecuteGetAction("System", "SystemOptions");
}
It's not ideal to subscribe upon calling the method. You should subscribe only when you need it, that's why Observable is called "lazy".
your GetSystemSettings should look something like:
this.GetSystemSettings().pipe(map(
response =>
{
if (response.IsSuccess)
{
return response.Entities;
}
else
{
return []; // Error message can be handled more gracefully :)
}
}));
and subscribe to the method
this.GetSystemSettings().subscribe(entities => this.m_SystemOptions = entities);

Categories