What is analogous to Promise.resolve for an Observable? - rxjs

Here's an example of some code I picked up for an Angular2 tutorial by Max Schwarzmueller on YouTube: https://www.youtube.com/playlist?list=PL55RiY5tL51olfU2IEqr455EYLkrhmh3n.
import {Injectable} from "angular2/core";
import {CONTACTS} from "./mock-contact";
#Injectable()
export class ContactService {
getContacts() {
return Promise.resolve(CONTACTS);
}
insertContact(contact: Contact) {
Promise.resolve(CONTACTS)
.then(
(contacts: Contact[]) => contacts.push(contact)
);
}
}
In this example, the CONTACTS object is static JSON. A promise isn't necessary here, but was used to show usage of a service in the real world.
I pretty much understand it, but I'm trying to migrate this idea into a service where I'm using an observable instead of a promise.
I want to make a change to the CONTACTS array, and then have the Observable emit .then again to tell all the observers to do their thing one more time.
On an observable, what would be analogous to Promise.resolve here? The documentation for RxJS observables is a lot to wade through.
If this is just a dumb idea, or there's a better way, please let me know.

With
getContacts() {
return Observable.of(CONTACTS);
}
the observable will complete after it emitted the CONTACTS.
See also http://paqmind.com/posts/rxjs-error-and-completed-events-demystified/

The following is functioning code that works as far as my original question goes. I've replaced the JSON formatted array from CONTACTS with data pulled from a local JSON file, and I'm dealing with products instead of contacts.
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Product } from './product';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class ProductsService {
private _productsUrl = "./products.json";
private _productObservable: Observable<Product[]>;
private _products: Product[];
constructor(private _http: Http) {
}
getProducts() : Observable<Product[]> {
this._productObservable = this._http.get(this._productsUrl)
.map((response:Response) => {
if (this._products == null)
{
this._products = response.json();
}
return this._products;
})
.catch((error:any) => Observable.throw(error || 'Server error'));
return this._productObservable;
}
deleteProduct()
{
this._products.pop();
return Observable.of(this._products);
}
}

Related

How to use enhancers (pipes, guards, interceptors, etc) with Nestjs Standalone app

The Nestjs module system is great, but I'm struggling to figure out how to take full advantage of it in a Serverless setting.
I like the approach of writing my domain logic in *.service.ts files, while using *.controller.ts files to take care of non-business related tasks such as validating an HTTP request body and converting to a DTO before invoking methods in a service.
I found the section on Serverless in the nestjs docs and determined that for my specific use-case, I need to use the "standalone application feature".
I created a sample nestjs app here to illustrate my problem.
The sample app has a simple add() function to add two numbers. I use class-validator for validation on the AddDto class.
// add.dto.ts
import { IsNumber } from 'class-validator'
export class AddDto {
#IsNumber()
public a: number;
#IsNumber()
public b: number;
}
And then, via some Nestjs magic, I am able to get built-in validation using the AddDto inside my controller by doing the following:
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Use `ValidationPipe()` for auto-validation in controllers
app.useGlobalPipes(
new ValidationPipe({ transform: true })
)
await app.listen(3000);
}
// app.controller.ts
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Post('add')
add(#Body() dto: AddDto): number {
// Request body gets auto validated and converted
// to an instance of `AddDto`, sweet!
return this.appService.add(dto.a, dto.b);
}
}
// app.service.ts
#Injectable()
export class AppService {
add(a: number, b: number): number {
return a + b
}
}
So far, so good. The problem now arises when using this in AWS with a Lambda function, namely:
I want to re-use the business logic in app.service.ts
I want to re-use built in validation that happens when making an HTTP request to the app, such as in the example above.
I want to use the standalone app feature so I don't have to spin up an entire nest server in Lambda
The docs hint on this being a problem:
Be aware that NestFactory.createApplicationContext does not wrap controller methods with enhancers (guard, interceptors, etc.). For this, you must use the NestFactory.create method.
For example, I have a lambda that receives messages from AWS EventBridge. Here's a snippet from the sample app:
// standalone-app.ts
interface IAddCommand {
a: number;
b: number;
}
export const handler = async (
event: EventBridgeEvent<'AddCommand', IAddCommand>,
context: any
) => {
const appContext = await NestFactory.createApplicationContext(AppModule);
const appService = appContext.get(AppService);
const { a, b } = event.detail;
const sum = appService.add(a, b)
// do work on `sum`, like cache the result, etc...
return sum
};
// lambda-handler.js
const { handler } = require('./dist/standalone-app')
handler({
detail: {
a: "1", // is a string, should be a number
b: "2" // is a string, should be a number
}
})
.then(console.log) // <--- prints out "12" ("1" + "2") instead of "3" (1 + 2)
I don't get "free" validation of the event's payload in event.detail like I do with #Body() dto: AddDto when making a HTTP POST request to /add. Preferentially, the code would throw a validation error in the above example. Instead, I get an answer of "12" -- a false positive.
Hopefully, this illustrates the crux of my problem. I still want to validate the payload of the event before calling appService.add(a, b), but I don't want to write custom validation logic that already exists on the controller in app.controller.ts.
Ideas? Anyone else run into this before?
It occurred to me while writing this behemoth of a question that I can simply use class-validator and class-transformer in my Lambda handler.
import { validateOrReject } from 'class-validator'
import { plainToClass } from 'class-transformer'
import { AddDto } from 'src/dto/add.dto'
export const handler = async (event: any, context: any) => {
const appContext = await NestFactory.createApplicationContext(AppModule);
const appService = appContext.get(AppService);
const data = getPayloadFromEvent(event)
// Convert raw data to a DTO
const dto: AddDto = plainToClass(AddDto, data)
// Validate it!
await validateOrReject(dto)
const sum = appService.add(dto.a, dto.b)
// do work on `sum`...
}
It's not as "free" as using app.useGlobalPipes(new ValidationPipe()), but only involves a few extra lines of code.
It worked for me with the following lambda file for nestjs.
import { configure as serverlessExpress } from '#vendia/serverless-express';
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '#nestjs/common';
let cachedServer;
export const handler = async (event, context) => {
if (!cachedServer) {
const nestApp = await NestFactory.create(AppModule);
await nestApp.useGlobalPipes(new ValidationPipe());
await nestApp.init();
cachedServer = serverlessExpress({
app: nestApp.getHttpAdapter().getInstance(),
});
}
return cachedServer(event, context);
};

Possible memory leak in NativeScript app if user reopens his app multiple times

I'm not sure where is the bug, maybe I'm using rxjs in a wrong way. ngDestroy is not working to unsubscribe observables in NativeScript if you want to close and back to your app. I tried to work with takeUntil, but with the same results. If the user close/open the app many times, it can cause a memory leak (if I understand the mobile environment correctly). Any ideas? This code below it's only a demo. I need to use users$ in many places in my app.
Tested with Android sdk emulator and on real device.
AppComponent
import { Component, OnDestroy, OnInit } from '#angular/core';
import { Subscription, Observable } from 'rxjs';
import { AppService } from './app.service';
import { AuthenticationService } from './authentication.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnDestroy, OnInit {
public user$: Observable<any>;
private subscriptions: Subscription[] = [];
constructor(private appService: AppService, private authenticationService: AuthenticationService) {}
public ngOnInit(): void {
this.user$ = this.authenticationService.user$;
this.subscriptions.push(
this.authenticationService.user$.subscribe((user: any) => {
console.log('user', !!user);
})
);
}
public ngOnDestroy(): void {
if (this.subscriptions) {
this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe());
}
}
async signIn() {
await this.appService.signIn();
}
async signOut() {
await this.appService.signOut();
}
}
AuthenticationService
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { shareReplay } from 'rxjs/operators';
import { AppService } from './app.service';
#Injectable({
providedIn: 'root',
})
export class AuthenticationService {
public user$: Observable<any>;
constructor(private appService: AppService) {
this.user$ = this.appService.authState().pipe(shareReplay(1)); // I'm using this.users$ in many places in my app, so I need to use sharereplay
}
}
AppService
import { Injectable, NgZone } from '#angular/core';
import { addAuthStateListener, login, LoginType, logout, User } from 'nativescript-plugin-firebase';
import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';
const user$ = new BehaviorSubject<User>(null);
#Injectable({
providedIn: 'root',
})
export class AppService {
constructor(private ngZone: NgZone) {
addAuthStateListener({
onAuthStateChanged: ({ user }) => {
this.ngZone.run(() => {
user$.next(user);
});
},
});
}
public authState(): Observable<User> {
return user$.asObservable().pipe(distinctUntilChanged());
}
async signIn() {
return await login({ type: LoginType.PASSWORD, passwordOptions: { email: 'xxx', password: 'xxx' } }).catch(
(error: string) => {
throw {
message: error,
};
}
);
}
signOut() {
logout();
}
}
ngOnDestroy is called whenever a component is destroyed (following regular Angular workflow). If you have navigated forward in your app, previous views would still exist and would be unlikely to be destroyed.
If you are seeing multiple ngOnInit without any ngOnDestroy, then you have instantiated multiple components through some navigation, unrelated to your subscriptions. You should not expect the same instance of your component to be reused once ngOnDestroy has been called, so having a push to a Subscription[] array will only ever have one object.
If you are terminating the app (i.e. force quit swipe away), the whole JavaScript context is thrown out and memory is cleaned up. You won't run the risk of leaking outside of your app's context.
Incidentally, you're complicating your subscription tracking (and not just in the way that I described above about only ever having one pushed). A Subscription is an object that can have other Subscription objects attached for termination at the same time.
const subscription: Subscription = new Subscription();
subscription.add(interval(100).subscribe((n: number) => console.log(`first sub`));
subscription.add(interval(200).subscribe((n: number) => console.log(`second sub`));
subscription.add(interval(300).subscribe((n: number) => console.log(`third sub`));
timer(5000).subscribe(() => subscription.unsubscribe()); // terminates all added subscriptions
Be careful to add the subscribe call directly in .add and not with a closure. Annoyingly, this is exactly the same function call to make when you want to add a completion block to your subscription, passing a block instead:
subscription.add(() => console.log(`everybody's done.`));
One way to detect when the view comes from the background is to set callbacks on the router outlet (in angular will be)
<page-router-outlet
(loaded)="outletLoaded($event)"
(unloaded)="outletUnLoaded($event)"></page-router-outlet>
Then you cn use outletLoaded(args: EventData) {} to initialise your code
respectively outletUnLoaded to destroy your subscriptions.
This is helpful in cases where you have access to the router outlet (in App Component for instance)
In case when you are somewhere inside the navigation tree you can listen for suspend event
Application.on(Application.suspendEvent, (data: EventData) => {
this.backFromBackground = true;
});
Then when opening the app if the flag is true it will give you a hint that you are coming from the background rather than opening for the first time.
It works pretty well for me.
Hope that help you as well.

Preloader not shown one or more http service call simultaneously using httpInterceptors?

I want to implement the global pre-loader concept in angular using httpInterceptors, its not working suppose if two http service call simultaneously called, 'finalize' will trigger only after one or more http api calls end, but its not happened, the preloader is also not shown correctly, it hide after first api call finished. Please suggest what i missed and tell me how to handle it. Is this the right place to handle the global error and preloader concept?
app.component.html
<preloader [loading]="appService.loading"></preloader>
app.component.ts:
const url1 = "https://jsonplaceholder.typicode.com/albums/1";
const url2 = "https://jsonplaceholder.typicode.com/albums/2";
forkJoin(
this.http.get(url1),
this.http.get(url2),
).subscribe(console.log);
HttpserviceInterceptor:
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
} from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { finalize, tap } from 'rxjs/operators';
import { AppCommonService } from './app.common.service';
#Injectable()
export class HttpserviceInterceptor implements HttpInterceptor {
constructor(
private appService: AppCommonService,
private notification: NotificationsWrapperService,
) {}
public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.appService.showPreLoader()
return next.handle(request).pipe(
//tslint:disable-next-line: no-empty
tap(() => {
}, (error: any) => {
this.notification.error(error);
}),
finalize(() => this.appService.hidePreLoader()),
);
}
}
AppCommonService:
public showPreloader(): void {
//this.showPreloader$.next(true);
this.loading = true;
}
public hidePreLoader(): void {
//this.showPreloader$.next(false);
this.loading = false;
}
Lets suppose that you perform two requests. The first one takes one second to complete and the second one takes 5 seconds. Based on the code you provided the flow of the loader will be the following.
First request is performed and this.loading is set to true
Second request is performed and this.loading is set to true again
First requests finishes, and this.loading is set to false (wrong)
Second requests finishes, and this.loading is set to false
To make the loader appear as long as a request is active, you should try to keep the number of the requests that are currently performed by the web browser. Lets assume that you initialize a private integer named currentNumberOfRequests, and set it's value to 0.
So when a requests is performed, you should always set the {this.loading} flag to true and increase the this.currentNumberOfRequests by 1, and when a requests succeeds or fails (ideally in the finally clause), you should decrease the this.currentNumberOfRequests by 1. Now, if the this.currentNumberOfRequests is 0 you should hide the loader.

Angular 5 - Cannot resolve observable in Observable.catch()

My first time here, I hope that you can help me.
I'm trying to create a response interceptor on Angular 5. I want to handle responses with status [202] successfully.
I'm doing a call to a webservice, the interceptor is working well it catches the error. If I return Observable.throw('') this will arrive to the error function on subscribe, but when I try to return a new Observable to resolve the exception, it is not arriving on success method of subscribe.
The interceptor code:
import {Injectable} from '#angular/core';
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor,
HttpRequest, HttpResponse} from '#angular/common/http';
import {Observable} from 'rxjs/Rx';
#Injectable()
export class Handle2xxResponseInterceptor implements HttpInterceptor{
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
return next.handle(req).catch(error => {
if (error instanceof HttpErrorResponse) {
if (error.status === 202) {
return Observable.of(error); // or return Observable.empty();
}
}
return Observable.throw(error);
});
}
}
Thank you people.
You're not able to catch 202 because 202 isn't an http error code and catch only catches errors. Instead, just use a normal map or do operator or higher order operator like switchMap to do whatever you want with it:
return next.handle(req).switchMap(response => {
if (response.status === 202) {
// process error
return Observable.empty();
}
return Observable.of(response);
});

Loading component data asynchronously server side with Mobx

I'm having an issue figuring out how to have a react component have an initial state based on asynchronously fetched data.
MyComponent fetches data from an API and sets its internal data property through a Mobx action.
Client side, componentDidMount gets called and data is fetched then set and is properly rendered.
import React from 'react';
import { observer } from 'mobx-react';
import { observable, runInAction } from 'mobx';
#observer
export default class MyComponent extends React.Component {
#observable data = [];
async fetchData () {
loadData()
.then(results => {
runInAction( () => {
this.data = results;
});
});
}
componentDidMount () {
this.fetchData();
}
render () {
// Render this.data
}
}
I understand that on the server, componentDidMount is not called.
I have something like this for my server:
import React from 'react';
import { renderToString } from 'react-dom/server';
import { useStaticRendering } from 'mobx-react';
import { match, RouterContext } from 'react-router';
import { renderStatic } from 'glamor/server'
import routes from './shared/routes';
useStaticRendering(true);
app.get('*', (req, res) => {
match({ routes: routes, location: req.url }, (err, redirect, props) => {
if (err) {
console.log('Error', err);
res.status(500).send(err);
}
else if (redirect) {
res.redirect(302, redirect.pathname + redirect.search);
}
else if (props) {
const { html, css, ids } = renderStatic(() => renderToString(<RouterContext { ...props }/>));
res.render('../build/index', {
html,
css
});
}
else {
res.status(404).send('Not found');
}
})
})
I have seen many posts where an initial store is computed and passed through a Provider component. My components are rendered, but their state is not initialized. I do not want to persist this data in a store and want it to be locally scope to a component. How can it be done ?
For server side rendering you need to fetch your data first, then render. Components don't have a lifecycle during SSR, there are just render to a string once, but cannot respond to any future change.
Since your datafetch method is async, it means that it cannot ever affect the output, since the component will already have been written. So the answer is to fetch data first, then mount and render components, without using any async mechanism (promises, async etc) in between. I think separating UI and data fetch logic is a good practice for many reasons (SSR, Routing, Testing), see this blog.
Another approach is to create the component tree, but wait with serializing until all your promises have settled. That is the approach that for example mobx-server-wait uses.

Resources