nestjs + Passport + GqlAuthGuard produces Cannot read property 'logIn' of undefined - nestjs-passport

I have followed the nestjs example of how to integrate passport with apollo but it constantly crashes with the following exception when I call a guarded resolver.
Looking into it in detail when the #nestjs/passport auth-guard class is extended, it does not call the getRequest function of the child class, instead it calls the pre-existing one in the class (as if inheritance never took place)
[Nest] 25029 - 05/26/2022, 8:29:40 PM ERROR [ExceptionsHandler] Cannot read property 'logIn' of undefined
TypeError: Cannot read property 'logIn' of undefined
at authenticate (.../node_modules/passport/lib/middleware/authenticate.js:96:21)
at ...node_modules/#nestjs/passport/dist/auth.guard.js:96:3
at new Promise (<anonymous>)
at ...node_modules/#nestjs/passport/dist/auth.guard.js:88:83
at JwtAuthGuard.<anonymous> (...node_modules/#nestjs/passport/dist/auth.guard.js:49:36)
at Generator.next (<anonymous>)
at fulfilled (.../node_modules/#nestjs/passport/dist/auth.guard.js:17:58)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
I have the following setup
import { ExecutionContext, Injectable } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { GqlExecutionContext } from '#nestjs/graphql';
#Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private readonly reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isGuestAllowed = this.reflector.getAllAndOverride<boolean>(IS_GUEST_ALLOWED_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isGuestAllowed) {
return true;
}
// Add your custom authentication logic here
// for example, call super.login(request) to establish a session.
return super.canActivate(context);
}
handleRequest(err, user, info) {
// You can throw an exception based on either "info" or "err" arguments
if (err || !user) {
throw err || new UnauthorizedException();
}
return user;
}
}
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: JwtConstants.secret,
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV}`,
}),
TypeOrmModule.forRoot(),
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
debug: true,
playground: true,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
installSubscriptionHandlers: true,
context: ({ req }) => ({ req }),
}),
RecipesModule,
AuthModule,
UsersModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
#Resolver((of) => Recipe)
export class RecipesResolver {
constructor(private readonly recipesService: RecipesService) {}
#UseGuards(GqlAuthGuard)
#Query((returns) => Recipe)
async recipe(#CurrentUser() user: any, #Args('id') id: string): Promise<Recipe> {
const recipe = await this.recipesService.findOneById(id);
if (!recipe) {
throw new NotFoundException(`Recipe with ID "${id}" not found`);
}
return recipe;
}
}
Using the following package versions.
"dependencies": {
"#nestjs/apollo": "^10.0.12",
"#nestjs/common": "^8.0.0",
"#nestjs/config": "^2.0.1",
"#nestjs/core": "^8.0.0",
"#nestjs/graphql": "^10.0.12",
"#nestjs/jwt": "^8.0.1",
"#nestjs/passport": "^8.2.1",
"#nestjs/platform-express": "^8.0.0",
"#nestjs/typeorm": "^8.0.4",
"apollo-server-express": "^3.8.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"cross-env": "^7.0.3",
"graphql": "^16.5.0",
"graphql-query-complexity": "^0.11.0",
"graphql-subscriptions": "^2.0.0",
"passport": "^0.6.0",
"passport-local": "^1.0.0",
"pg": "^8.7.3",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"typeorm": "^0.3.6",
"uuid": "^8.3.2"
},

The issue was making the JwtGuard global (not visible in the source code above). It was getting queued before the JqlGuard which it would seem as if my getRequest was never called (was running a different class but being js there was no clear indication as to which class is actually running).

I've got the same problem with a custom GraphQL driver. I forgot to add a setter for context and to override getRequest in global JwtAuthGuard :
import { Module } from '#nestjs/common';
import { GraphQLModule as NestGraphQLModule } from '#nestjs/graphql';
import { AbstractGraphQLDriver, GqlModuleOptions } from '#nestjs/graphql';
import { createHandler } from 'graphql-http/lib/use/express';
import { TestResolver } from './resolvers/test';
class GraphQLDriver extends AbstractGraphQLDriver {
async start(options: GqlModuleOptions): Promise<void> {
const { schema } = await this.graphQlFactory.mergeWithSchema(options);
const { httpAdapter } = this.httpAdapterHost;
httpAdapter.use(
'/api/graphql',
createHandler({
schema,
context(req, params) { // <-- Context was missing
return { req, params };
}
})
);
}
async stop() {}
}
#Module({
imports: [
NestGraphQLModule.forRoot<GqlModuleOptions>({
driver: GraphQLDriver,
typePaths: ['../types/graphql/**/*.gql']
})
],
providers: [TestResolver]
})
export class GraphQLModule {}
import { ExecutionContext, Injectable } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { GqlContextType, GqlExecutionContext } from '#nestjs/graphql';
#Injectable()
export class JwtAuthGuard extends AuthGuard('access-token') {
constructor(private reflector: Reflector) {
super();
}
// Must override getRequest to handle graphql context type
getRequest(context: ExecutionContext) {
switch (context.getType<GqlContextType>()) {
case 'graphql':
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
default: // 'http' | 'ws' | 'rpc'
return context.switchToHttp().getRequest();
}
}
}

Related

Scene phase not initialized project nextJS

The phase does not initialize when I start the project.
this is all the dependencies that I'm using the project
"#nextui-org/react": "^1.0.0-beta.9"
"#reduxjs/toolkit": "^1.8.3",
"immer": "^9.0.15",
"next": "12.2.3",
"phaser": "^3.55.2",
"react":"17.0.2",
"react-dom": "17.0.2",
"react-redux": "^8.0.2",
"styled-components": "^5.3.5",
"swiper": "^8.3.1"
_app.tsx
import type { AppProps } from 'next/app'
import dynamic from 'next/dynamic'
import Head from 'next/head'
import { Fragment } from 'react'
import GlobalStyles from '../styles/Globals'
function MyApp({ Component, pageProps }: AppProps) {
const PhaserGameNoSSR = dynamic(() => import('../phaser'), {
ssr: false,
})
return (
<Fragment>
<Head>
<title>project</title>
<meta name="description" content="Metaverse Bits Website" />
</Head>
<GlobalStyles />
<PhaserGameNoSSR />
<Component {...pageProps} />
</Fragment>
)
}
export default MyApp
phaseGame.tsx
import { useRef } from 'react'
import { useGame } from '../hooks/useGame'
import { gameConfig } from './phaserConfig'
function PhaserGame() {
const parentEl = useRef<HTMLDivElement>(null)
useGame(gameConfig, parentEl)
return <div ref={parentEl} id="phaser-container" />
}
export default PhaserGame
Config phase game config
import { Types } from 'phaser'
import Background from '../scenes/background'
import { Bootstrap } from '../scenes/bootstrap'
export const gameConfig: Types.Core.GameConfig = {
title: 'project',
type: Phaser.AUTO,
pixelArt: true,
scale: {
mode: Phaser.Scale.ScaleModes.RESIZE,
width: window.innerWidth,
height: window.innerHeight,
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 },
debug: false,
},
},
autoFocus: true,
scene: [Bootstrap, Background],
}
first scene background
import Phaser from 'phaser'
export default class Background extends Phaser.Scene {
private backdropKey!: string
constructor() {
super('background')
}
create() {
const sceneHeight = this.cameras.main.height
const sceneWidth = this.cameras.main.width
this.backdropKey = 'background_initial'
const backdropImage = this.add.image(sceneWidth / 2, sceneHeight / 2, this.backdropKey)
const scale = Math.max(sceneWidth / backdropImage.width, sceneHeight / backdropImage.height)
backdropImage.setScale(scale).setScrollFactor(0)
}
}
And config init phase scene
import { Scene } from 'phaser'
export class Bootstrap extends Scene {
constructor() {
super('bootstrap')
}
preload() {
this.load.image('background_initial', 'assets/background.png')
}
create(): void {
console.log('launched')
}
private launchBackground() {
this.scene.launch('background')
}
changeBackgroundMode() {
this.scene.stop('background')
}
}

Child component not re-rendering on Mobx observable/compute change

I have a store file named Authstore.js as below
import { observable, computed, action, makeObservable } from 'mobx';
export default class AuthStore {
constructor(stores) {
makeObservable(this);
this.stores = stores;
this.stop();
}
#observable isAuthed = false;
#observable authIsPending = true;
#observable user = null;
#observable loginError = null;
#observable
currentOrgNameHeader = '';
#action
stop = () => {
if (this.userRef) {
this.userRef.off('value', this.userCallback);
this.userRef = null;
this.userCallback = null;
}
this.isAuthed = false;
this.authIsPending = true;
this.user = null;
// this.loginError = null; // need to keep error after login failure
};
#computed
get loginErrorMessage() {
return this.loginError
? authErrors[this.loginError] || 'Unexpected error'
: '';
}
#computed
get isWrongPassword() {
return this.loginError === 'auth/wrong-password';
}
#computed
get isPasswordResetExpired() {
return this.loginError === 'auth/invalid-action-code';
}
#computed
get settings() {
return this.profile.settings || {};
}
initialise = () => {
if (this.isInitialised) {
return;
}
this.isInitialised = true;
checkAccountExists = email => {
if (!email) {
this.loginError = 'cw/no-email';
return Promise.reject('cw/no-email');
}
if (!/^[^#\s]+#[^#\s]+\.[^#\s]+$/.test(email)) {
this.loginError = 'cw/invalid-email';
return Promise.reject('cw/invalid-email');
}
this.loginError = null;
return auth
.fetchSignInMethodsForEmail(email)
.then(res => res.includes('password'))
.catch(this.processLoginError);
};
signIn = (email, password) => {
if (!email || !password) {
this.loginError = 'cw/no-email-password';
return Promise.reject('cw/no-email-password');
}
this.loginError = null;
return auth
.signInWithEmailAndPassword(email, password)
.catch(this.processLoginError);
};
processLoginError = error => {
this.loginError = error.code || 'cw/unexpected';
return Promise.reject(error);
};
signOut = () => {
return auth.signOut();
};
}
And then I have login component in this I have multiple child components
Login.js
import React, { Component } from 'react';
import theme from '../../styles/theme';
import { observer, inject } from 'mobx-react';
#inject('authStore','uiStore')
#observer
export default class Login extends Component {
constructor(){
super();
this.state = {
redirect: null,
authForm:'email',
email: ''
}
}
componentWillMount() {
const { uiStore, authStore, location } = this.props;
const { setTitle, setLogoIndent } = uiStore;
setTitle('Login');
setLogoIndent('ready');
document.body.style.backgroundColor = 'black';
document.body.style.color = 'white';
document.body.style.backgroundImage = 'none';
authStore.signOut();
// const { mode } = queryString.parse(location.search);
// this.isPasswordReset = mode === 'resetPassword';
// const from = location.state && location.state.from;
// if (from && from.pathname !== '/') {
// this.setState({ redirect: from });
// }
}
render() {
const { email, authForm} = this.state;
const { authStore } = this.props;
console.log(authStore ,'authStore.isAuthed')
console.log("loginErrorMessage",this.props.authStore.loginErrorMessage);
if (authStore.isAuthed) {
const to = { pathname: `/o/` };
return <Navigate to={{ ...to, state: 'login' }} />;
}
return (
<Wrapper>
<ScrollToTopOnMount />
<Logo shade="light" height="150px" width="220px" />
{authForm === 'email' && (
<EmailForm
authStore={authStore}
email={email}
onSubmit={(accountExists, email) => {
const authForm = accountExists ? 'login' : 'register';
this.setState({ authForm, email });
}}
/>
)}
<Footer />
<Shape>
<Asterisk animation="rotate" color={theme.color.yellow} />
</Shape>
</Wrapper>
);
}
}
Child component EmailForm.js
import React, { Component } from 'react';
import bowser from 'bowser';
import { observer, inject } from 'mobx-react';
import { Form, Field, Icon, Input, Title, MainButton } from './styles';
import theme from '../../../styles/theme/index';
import { Error } from '../../../components/CommonComponents/Text/Text';
#inject('authStore')
#observer
export default class LoginForm extends Component {
constructor(props){
super(props);
this.state = {
isBusy: false
};
this.Email = React.createRef();
}
componentDidMount() {
this.Email.current.value = this.props.email;
if (!bowser.msie) {
this.Email.current.focus();
}
}
handleSubmit = event => {
event.preventDefault();
if (this.state.isBusy) return;
this.setState({ isBusy: true });
const email = ( this.Email.current.value || '').trim();
this.props.authStore
.checkAccountExists(email)
.then(exists => this.props.onSubmit(exists, email))
.catch(error => this.setState({ isBusy: false }));
};
render() {
const { loginErrorMessage, clearLoginError } = this.props.authStore;
console.log("loginErrorMessageES",loginErrorMessage);
return (
<Form onSubmit={this.handleSubmit}>
<Title>Let's get ready!</Title>
<Field mt="35px" mb="45px">
<Icon icon="user" />
<Input
ref={this.Email}
placeholder="Email"
autoCorrect="off"
autoCapitalize="none"
onKeyDown={() => clearLoginError()}
/>
</Field>
<MainButton type="submit">Continue</MainButton>
{loginErrorMessage && (
<Error mt="30px" color={theme.color.red} textColor={theme.color.red}>
{loginErrorMessage}
</Error>
)}
</Form>
);
}
}
When login form was submitted without email the loginErrorMessage need to be displayed using observer but the child component is not re-rendering on the observable change. Please let me know if I'm missing anything.
package.json
{
"dependencies": {
"#babel/plugin-proposal-class-properties": "^7.18.6",
"#babel/plugin-proposal-decorators": "^7.18.6",
"bowser": "^2.11.0",
"create-react-app": "^5.0.1",
"customize-cra": "^1.0.0",
"firebase": "7.24.0",
"mobx": "6.3.0",
"mobx-react": "6.3.0",
"mobx-react-lite": "3.0.0",
"moment": "^2.29.3",
"polished": "^4.2.2",
"query-string": "^7.1.1",
"re-base": "^4.0.0",
"react": "^18.2.0",
"react-app-rewired": "^2.2.1",
"react-datetime": "^3.1.1",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
"react-scripts": "^5.0.1",
"react-select": "^5.3.2",
"react-textarea-autosize": "^8.3.4",
"styled-components": "^5.3.5",
"styled-map": "^3.3.0",
"styled-system": "^5.1.5"
},
"babel": {
"plugins": [
[
"#babel/plugin-proposal-decorators",
{
"legacy": true
}
],
[
"#babel/plugin-proposal-class-properties",
{
"loose": false
}
]
]
},
"scripts": {
"start": "react-app-rewired start",
"start:windows": "react-app-rewired start",
"build": "react-app-rewired build"
}
}
But if I remove the inject and observer from the child component class and passed the authstore as props to the child component then everything is working fine.
Please find the code sandbox link to reproduce the issue - https://codesandbox.io/s/sathish-7m3174?file=/src/routes/Login/Login.js
Steps to reproduce the issue:
Click the login button without entering the email
At this time the loginErrorMessage #compute will be updated in Mobx(AuthStore.js) and it will trigger the child component to re-render to show the error message but it is not happening somehow.
Please check and advise on this.

Reusing a react application inside angularjs application

I want to reuse a small react application entirely within an angularjs application. Is there a way to achieve the same.
I have used ngReact to load react inside angular js, and it works fine. However how do i reuse the application entirely without much changes.
Note: I know its a bad practice , but due to legacy code, have to implement it.
Any leads is appreciated. TIA !
Solution Implementation
package.json
"dependencies": {
...
"react": "^16.12.0",
"react-dom": "^16.12.0",
...
},
"devDependencies": {
...
"#types/react": "^16.9.17",
"#types/react-dom": "^16.9.4",
...
}
tsconfig.json
{
...
"jsx": "react",
...
}
Wrapper Component
The wrapper is responsible for detecting changes and re-rendering the wrapped React component so that its props take effect, eventually unmounting the wrapped component when the wrapper is destroyed.
MyReactComponentWrapper.tsx
import {
AfterViewInit,
Component,
ElementRef,
EventEmitter,
Input,
OnChanges,
OnDestroy,
Output,
SimpleChanges,
ViewChild,
ViewEncapsulation
} from '#angular/core';
import { MyReactComponent } from 'src/components/my-react-component/MyReactComponent';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
const containerElementName = 'myReactComponentContainer';
#Component({
selector: 'app-my-component',
template: `<span #${containerElementName}></span>`,
styleUrls: ['./MyReactComponent.scss'],
encapsulation: ViewEncapsulation.None,
})
export class MyComponentWrapperComponent implements OnChanges, OnDestroy, AfterViewInit {
#ViewChild(containerElementName, {static: false}) containerRef: ElementRef;
#Input() public counter = 10;
#Output() public componentClick = new EventEmitter<void>();
constructor() {
this.handleDivClicked = this.handleDivClicked.bind(this);
}
public handleDivClicked() {
if (this.componentClick) {
this.componentClick.emit();
this.render();
}
}
ngOnChanges(changes: SimpleChanges): void {
this.render();
}
ngAfterViewInit() {
this.render();
}
ngOnDestroy() {
ReactDOM.unmountComponentAtNode(this.containerRef.nativeElement);
}
private render() {
const {counter} = this;
ReactDOM.render(<div className={'i-am-classy'}>
<MyReactComponent counter={counter} onClick={this.handleDivClicked}/>
</div>, this.containerRef.nativeElement);
}
}
MyReactComponent.tsx
import * as React from 'react';
import { FunctionComponent, useEffect, useRef, useState } from 'react';
import './MyReactComponent.scss';
export interface IMyComponentProps {
counter: number;
onClick?: () => void;
}
export const MyReactComponent: FunctionComponent<IMyComponentProps> = (props: IMyComponentProps) => {
const timerHandle = useRef<number | null>(null);
const [stateCounter, setStateCounter] = useState(42);
useEffect(() => {
timerHandle.current = +setInterval(() => {
setStateCounter(stateCounter + 1);
}, 2500);
return () => {
if (timerHandle.current) {
clearInterval(timerHandle.current);
timerHandle.current = null;
}
};
});
const {counter: propsCounter, onClick} = props;
const handleClick = () => {
if (onClick) {
onClick();
}
};
return <div className={`my-graph-component`}>
<div className={'comp-props'}>Props counter: {propsCounter}
<span onClick={handleClick}
className={'increase-button'}>click to increase</span>
</div>
<div className={'comp-state'}>State counter: {stateCounter}</div>
</div>;
};

Trying to get a unique list of objects from an array in angular

Have an observable being returned from my service.ts as shown here:
import { Injectable, ErrorHandler } from '#angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders } from
'#angular/common/http'
import { Observable } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { PokeResponse } from '../PokeResponse';
#Injectable({
providedIn: 'root'
})
export class PokeApiService {
private url = "https://pokemon-go1.p.rapidapi.com/pokemon_stats.json";
constructor(private http:HttpClient) { }
getPokeData(): Observable<PokeResponse> {
return this.http.get<PokeResponse>(this.url,
{
headers : new HttpHeaders({
'x-rapidapi-key': 'a6cef4cbcamsh05b29346394d4a4p1bafacjsn2a92406ac103'
})
})
.pipe(
tap(data => console.log('Pokedata/Error' + JSON.stringify(data))
),
catchError(this.handleError)
);
}
private handleError(err:HttpErrorResponse) {
console.log('PokemonService: ' + err.message);
return Observable.throw(err.message);
}
}
This is my response:
export interface PokeResponse{
list:results[];
results:{
pokemon_id:number;
pokemon_name:string;
base_attack:number;
base_defense:number;
base_stamina:number;
}[];
}
export interface results{
pokemon_id:number;
pokemon_name:string;
base_attack:number;
base_defense:number;
base_stamina:number;
}
export class Pokemon implements results{
pokemon_id:number;
pokemon_name:string;
base_attack:number;
base_defense:number;
base_stamina:number;
constructor(_id:number, _name:string, _atk:number, _def:number, _stam:number) {
_id = this.pokemon_id;
_name = this.pokemon_name;
_atk = this.base_attack;
_def = this.base_defense;
_stam = this.base_stamina;
}
}
And this is my component.ts:
import { Component, OnInit } from '#angular/core';
import { PokeApiService } from 'src/app/services/poke-api.service';
import { PokeResponse, results, Pokemon } from 'src/app/PokeResponse';
import { Observable } from 'rxjs';
#Component({
selector: 'app-list-pokemon',
templateUrl: './list-pokemon.component.html',
styleUrls: ['./list-pokemon.component.css']
})
export class ListPokemonComponent implements OnInit {
constructor(private pokeService: PokeApiService) { }
ngOnInit(): void {
this.getPokeDetails();
}
pokeData:PokeResponse;
errorMessage:any;
pokeArray:results;
getPokeDetails() : boolean {
this.pokeService.getPokeData().subscribe(
pokeData => {
this.pokeData=pokeData;
console.table(pokeData);
},
error => this.errorMessage = <any>error
);
return false;
}
}
In my console I'm getting back a console.table of my observable like this
I'm trying to filter out the names of Pokemon which are the same as others, which could also be achieved by just filtering out any of the pokemon_ids as all the stats match regardless of the type.
So far I've tried:
console.log(this.pokeArray);,
using [...Set()], forEach(), and Array.from()
Any help or suggestions on how I can make this question any clearer would be greatly appreciated.
Try this, using filter:
// list-pokemon.component.ts
export class ListPokemonComponent implements OnInit {
uniqueListPoke = [];
flags = {};
constructor(private pokeService: PokeApiService) { }
ngOnInit(): void {
this.getPokeDetails();
}
pokeData:PokeResponse;
errorMessage:any;
pokeArray:results;
getPokeDetails() : boolean {
this.pokeService.getPokeData().subscribe(
pokeData => {
this.uniqueListPoke = pokeData.filter((entry) => {
if (this.flags[entry.pokemon_name]) {
// console.log('flags', false);
return false;
}
this.flags[entry.pokemon_name] = true;
return true;
});
console.log(JSON.stringify(this.uniqueListPoke));
},
error => this.errorMessage = <any>error
);
return false;
}
}
The working example:
https://stackblitz.com/edit/angular-distinct-poke?file=src/app/hello.component.ts

Detect if Observable not found using combineLatest

I need to modify my code where loading detail category will first look whether it is not already loaded in the statement, and if not then detail loads. Thanks for help!
Constructor of CategoryProvider:
private _obServers = {
'categoryList': undefined,
'category': undefined,
'idCategory': new Subject<Number>()
};
constructor(){
this.categories = new Observable(observer => this._obServers.categoryList = observer).share();
this._categoryObservable = this.categories
.combineLatest(this._obServers.idCategory, (categories, idCategory) => {
return categories.filter(category => category.id === idCategory)[0];
})
.distinctUntilChanged((oldCategory, newCategory) => {
return oldCategory.id === newCategory.id;
});
}
CategoryList:
loadCategories(search?:string):void{
this._http
.get('/services/category/list?search=' + search)
.map(res => res.json())
.subscribe(data => {
this._obServers.categoryList.next(this.createCategoryEntities(data));
});
}
CategoryDetail:
loadCategory(categoryId:number){
this._obServers.idCategory.next(categoryId);
//If category not loaded I need to load it
}
I have followed this way https://github.com/JonatanSCS/Angular-2-Tutorial/blob/master/node_modules/rxjs/src/add/observable/combineLatest.ts
import { Component, Injectable, Inject, provide } from '#angular/core';
import { HTTP_PROVIDERS } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { combineLatestStatic } from 'rxjs/operator/combineLatest.js';
import { MessageApi } from '../providers/lb-service/lb-services.provider'
import { EstimatesService } from './estimates.service';
#Component({
pipes: [TranslatePipe],
})
#Injectable()
export class InvoiceService {
constructor(private _message:MessageApi,
#Inject(EstimatesService) _estimates:EstimatesService) {
this._message = _message;
this._estimates = _estimates;
Observable.combineLatest = combineLatestStatic;
declare module 'rxjs/Observable' {
namespace Observable {
export let combineLatest: typeof combineLatestStatic;
}
}
Observable.combineLatest(
this._estimates.getEstimates(),
this._message.findOne({
where: {
moduleTag: 'monthlyStat',
'dynamic.date': "2016-07-01" //new Date
},
fields: {
dynamic: true
}
}),this._message.findOne({
where: {
moduleTag: 'areaPriceSE1',
'dynamic.date': ''
},
fields: {
dynamic: true
}
})
).subscribe(res => console.log("observable", res));

Resources