Angular 6: Property 'of' does not exist on type 'typeof Observable' - rxjs

I am using Angular 6
using "rxjs": "^6.0.0",
ERROR : Property 'of' does not exist on type 'typeof Observable'.
import { Injectable } from '#angular/core';
import { TranslateLoader } from '#ngx-translate/core';
import { Observable, Subject, pipe, of } from 'rxjs';
#Injectable()
export class MiTranslateLoaderService implements TranslateLoader {
getTranslation(lang: string): Observable<any> {
return Observable.of({
lbl_select: 'Select',
});
}
}

Since RxJS 6 the correct and recommended way of using of() (RxJS 5 in Observable.of()) is this:
import { of } from 'rxjs';
I think this import { of } from 'rxjs/observable/of'; will work only while you have rxjs-compat package installed.

There are some updates in rxjs: ( Its rxjs6)
import { of } from 'rxjs';
It will work only when your app has rxjs-compat package installed
You can import of from rxjs:
import { Observable,of } from 'rxjs';
And simply return of()
return of({
lbl_select: 'Select',
});
So your code will be:
import { Injectable } from '#angular/core';
import { TranslateLoader } from '#ngx-translate/core';
import { Observable, of } from 'rxjs';
#Injectable()
export class MiTranslateLoaderService implements TranslateLoader {
getTranslation(lang: string): Observable<any> {
return of({
lbl_select: 'Select',
});
}
}

This is working for me.
Angular CLI 6.0.8
RxJS 6.2.2
import {of} from 'rxjs/index';
this.dataService.currentData
.pipe(takeUntil(this.destroy$))
.pipe(switchMap((myData:MyDataType) =>
of(this.anotherService.get(myData._id))))
.pipe(map((response) => {
if(response instanceof Error) {
console.log('error:');
console.dir(response);
}
return response;
}))
.subscribe((data:any) => {
doStuff(data);
},
response => {
console.log('response error');
console.log(response)
},
() => {
console.log('response complete.');
});

With the release of version 6, RxJS changed its internal package structure
https://www.academind.com/learn/javascript/rxjs-6-what-changed/#import-statement-update-path
import 'rxjs/add/observable/of';
// or
import { of } from 'rxjs/observable/of';

You need to import of from rxjs/observable/of
import { of } from "rxjs/observable/of";
Usage:
return of({
lbl_select: 'Select',
});
Update: for rxjs version 6 without rxjs-compat, you need to import of from rxjs itself as mentioned by #martin.
import { of } from 'rxjs';
Migration guide to rxjs6

The solution is to return of(..) directly :
getTranslation(lang: string): Observable<any> {
return of({
lbl_select: 'Select',
});

Related

Connect NestJS to a websocket server

How can NestJS be use as a websocket client? I want to connect to a remote websocket server as a client using NestJS, but I didn't find any information about this implementation in the framework.
As Nestjs is simply a framework for Nodejs, so you need to find an NPM package that supports Websocket. For example, I use ws with #types/ws type definition, and create a Websocket client as a Nestjs service class:
// socket-client.ts
import { Injectable } from "#nestjs/common";
import * as WebSocket from "ws";
#Injectable()
export class WSService {
// wss://echo.websocket.org is a test websocket server
private ws = new WebSocket("wss://echo.websocket.org");
constructor() {
this.ws.on("open", () => {
this.ws.send(Math.random())
});
this.ws.on("message", function(message) {
console.log(message);
});
}
send(data: any) {
this.ws.send(data);
}
onMessage(handler: Function) {
// ...
}
// ...
}
// app.module.ts
import { Module } from "#nestjs/common";
import { WSService } from "./socket-client";
#Module({
providers: [WSService]
})
export class AppModule {}
I try it by another way. I write an adapter with socket.io-client. Then use this adapter in boostrap by method useWebSocketAdapter. After that i can write handle websocket event in gateway like the way working with socket server (use decorator #SubscribeMessage)
My Adapter file
import { WebSocketAdapter, INestApplicationContext } from '#nestjs/common';
import { MessageMappingProperties } from '#nestjs/websockets'
import * as SocketIoClient from 'socket.io-client';
import { isFunction, isNil } from '#nestjs/common/utils/shared.utils';
import { fromEvent, Observable } from 'rxjs';
import { filter, first, map, mergeMap, share, takeUntil } from 'rxjs/operators';
export class IoClientAdapter implements WebSocketAdapter {
private io;
constructor(private app: INestApplicationContext) {
}
create(port: number, options?: SocketIOClient.ConnectOpts) {
const client = SocketIoClient("http://localhost:3000" , options || {})
this.io = client;
return client;
}
bindClientConnect(server: SocketIOClient.Socket, callback: Function) {
this.io.on('connect', callback);
}
bindClientDisconnect(client: SocketIOClient.Socket, callback: Function) {
console.log("it disconnect")
//client.on('disconnect', callback);
}
public bindMessageHandlers(
client: any,
handlers: MessageMappingProperties[],
transform: (data: any) => Observable<any>,
) {
const disconnect$ = fromEvent(this.io, 'disconnect').pipe(
share(),
first(),
);
handlers.forEach(({ message, callback }) => {
const source$ = fromEvent(this.io, message).pipe(
mergeMap((payload: any) => {
const { data, ack } = this.mapPayload(payload);
return transform(callback(data, ack)).pipe(
filter((response: any) => !isNil(response)),
map((response: any) => [response, ack]),
);
}),
takeUntil(disconnect$),
);
source$.subscribe(([response, ack]) => {
if (response.event) {
return client.emit(response.event, response.data);
}
isFunction(ack) && ack(response);
});
});
}
public mapPayload(payload: any): { data: any; ack?: Function } {
if (!Array.isArray(payload)) {
return { data: payload };
}
const lastElement = payload[payload.length - 1];
const isAck = isFunction(lastElement);
if (isAck) {
const size = payload.length - 1;
return {
data: size === 1 ? payload[0] : payload.slice(0, size),
ack: lastElement,
};
}
return { data: payload };
}
close(server: SocketIOClient.Socket) {
this.io.close()
}
}
main.js
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import {IoClientAdapter} from './adapters/ioclient.adapter'
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new IoClientAdapter(app))
await app.listen(3006);
console.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();
then Gateway
import {
MessageBody,
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
WsResponse,
} from '#nestjs/websockets';
import { from, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Server } from 'socket.io';
#WebSocketGateway()
export class EventsGateway {
#WebSocketServer()
server: Server;
#SubscribeMessage('hello')
async identity(#MessageBody() data: number): Promise<number> {
console.log(data)
return data;
}
}
It a trick, but look so cool. Message handler can write more like nestjs style.

Problem with e2e testing with NestJS TestingModule, GraphQL code first and TypeOrm

I'm in struggle since few days with e2e testing my NestJS application using GraphQL code first approach and TypeOrm.
I'm trying to create a TestingModule by injecting nestjs GraphQLModule with autoSchemaFile and I'm always getting the error "Schema must contain uniquely named types but contains multiple types named ...".
Here a reproduction of my bug with minimal code:
character.entity.ts:
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { ObjectType, Field, ID } from 'type-graphql';
#Entity()
#ObjectType()
export class Character {
#PrimaryGeneratedColumn()
#Field(() => ID)
id: string;
#Column({ unique: true })
#Field()
name: string;
}
character.resolver.ts:
import { Query, Resolver } from '#nestjs/graphql';
import { Character } from './models/character.entity';
import { CharacterService } from './character.service';
#Resolver(() => Character)
export class CharacterResolver {
constructor(private readonly characterService: CharacterService) {}
#Query(() => [Character], { name: 'characters' })
async getCharacters(): Promise<Character[]> {
return this.characterService.findAll();
}
}
character.module.ts:
import { Module } from '#nestjs/common';
import { CharacterResolver } from './character.resolver';
import { CharacterService } from './character.service';
import { TypeOrmModule } from '#nestjs/typeorm';
import { Character } from './models/character.entity';
#Module({
imports: [TypeOrmModule.forFeature([Character])],
providers: [CharacterResolver, CharacterService],
})
export class CharacterModule {}
app.module.ts:
import { Module } from '#nestjs/common';
import { CharacterModule } from './character/character.module';
import { TypeOrmModule } from '#nestjs/typeorm';
import { Connection } from 'typeorm';
import { GraphQLModule } from '#nestjs/graphql';
#Module({
imports: [TypeOrmModule.forRoot(), GraphQLModule.forRoot({ autoSchemaFile: 'schema.gql' }), CharacterModule],
controllers: [],
providers: [],
})
export class AppModule {
constructor(private readonly connection: Connection) {}
}
and finally: character.e2e-spec.ts:
import { INestApplication } from '#nestjs/common';
import { Test, TestingModule } from '#nestjs/testing';
import { TypeOrmModule } from '#nestjs/typeorm';
import { CharacterModule } from '../src/character/character.module';
import { GraphQLModule } from '#nestjs/graphql';
describe('CharacterResolver (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot(),
GraphQLModule.forRoot({ playground: false, autoSchemaFile: 'schema.gql' }),
CharacterModule,
],
}).compile();
app = module.createNestApplication();
await app.init();
});
it('should create testing module', () => {
expect(1).toBe(1);
});
afterAll(async () => {
await app.close();
});
});
And after running npm run test:e2e:
Schema must contain uniquely named types but contains multiple types named "Character".
at typeMapReducer (../node_modules/graphql/type/schema.js:262:13)
at Array.reduce (<anonymous>)
at new GraphQLSchema (../node_modules/graphql/type/schema.js:145:28)
at Function.generateFromMetadataSync (../node_modules/type-graphql/dist/schema/schema-generator.js:31:24)
at Function.<anonymous> (../node_modules/type-graphql/dist/schema/schema-generator.js:16:33)
at ../node_modules/tslib/tslib.js:110:75
at Object.__awaiter (../node_modules/tslib/tslib.js:106:16)
at Function.generateFromMetadata (../node_modules/type-graphql/dist/schema/schema-generator.js:15:24)
I don't find any other way to create a testing module with graphql code first approach on official doc or while googling... Am I missing something ?
Your ormconfig.json need to look like this:
"entities": [
"src/**/*.entity.js"
],
"migrations": [
"src/migration/*.js"
],
"cli": {
"migrationsDir": "src/migration"
}
I.e you need to specify the entities being in the src, not dist folder. If not TypeGraphQL will generate the schema for each resolver twice. To get the generate and run migration commands to work you would have to setup a different ormconfig.json for your development environment.

Angular & RXJS - [ts] Property 'map' does not exist on type 'Observable<User>'

Since creating a new angular 6 project, some previous code that I've copied over doesn't seem to be working. This primarily seems to be rxjs syntax
On the .map, it displays the error:
[ts] Property 'map' does not exist on type 'Observable'<User>'.
I seem to be getting a similar error on another file with .take
Is anyone able to point me in the right direction to resolve this please?
import { Injectable } from '#angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate, Router } from '#angular/router';
import { Observable } from 'rxjs';
import { AngularFireAuth } from 'angularfire2/auth';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/do';
#Injectable()
export class LoginGuardService implements CanActivate {
constructor(
private router: Router,
private auth: AngularFireAuth
) { }
canActivate(): Observable<boolean> {
return this.auth.authState.map(authState => {
if (authState) this.router.navigate(['/folders']);
return !authState;
}).take(1);
}
}
Second Guard
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):
Observable<boolean> {
this.authGuardStateURL = state.url;
return this.auth.authState.pipe(
take(1)
.map(authState => !!authState)
.do(auth => !auth ? this.router.navigate(['/login']) : true)
)
}
I reckon you used Angular CLI to create your app. Angular 6 comes with RxJS 6 and since v5, RxJS has been using pipeable operators.
So your code should look like this:
import { Injectable } from '#angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate, Router } from '#angular/router';
import { Observable } from 'rxjs';
import { AngularFireAuth } from 'angularfire2/auth';
import { map, take, tap } from 'rxjs/operators';
#Injectable()
export class LoginGuardService implements CanActivate {
constructor(
private router: Router,
private auth: AngularFireAuth
) { }
canActivate(): Observable<boolean> {
return this.auth.authState.pipe(
map(authState => {
if (authState) this.router.navigate(['/folders']);
return !authState;
}),
take(1)
)
}
//Second Guard
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot): Observable<boolean> {
this.authGuardStateURL = state.url;
return this.auth.authState.pipe(
take(1),
map(authState => !!authState),
tap(auth => !auth ? this.router.navigate(['/login']) : true)
)
}
}
Notice how you import the operators now and how you put map and take inside pipe method.

Passing data between more than one component using single model and presist it?

I have this service:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class DataService<T> {
private subject: BehaviorSubject<Partial<T>> = new BehaviorSubject<Partial<T>>(null);
changeData(message: Partial<T>) {
this.subject.next(message);
}
clearData() {
this.subject.next(null);
}
getData(): Observable<Partial<T>> {
return this.subject.asObservable();
}
}
I'm using it like this.
Datepicker:
this.ds.changeData({
dateFrom: this.dateRange.start,
dateTo: this.dateRange.end
});
Main Component:
this.ds.getData().subscribe((data: FilterQuery) => {
console.log('Update data', data);
this.filterModel = data;
});
I would like to add few more components what is the best way to presist the data and build full query object? If you have any other suggestions, I will gladly appreciate it.

Response isn't working in Angular 4

I have a service to connect to my backend. But I have this problem:
The error:
ERROR in src/app/login/sevices/login.service.ts(18,14): error TS2345: Argument of type '(res: Response) => Promise' is not assignable to parameter of type '(value: Response, index: number) => Promise'.
Types of parameters 'res' and 'value' are incompatible.
Type 'Response' is not assignable to type 'Response'. Two different types with this name exist, but they are unrelated.
Property 'body' is missing in type 'Response'.
login.service.ts:
import {Injectable} from "#angular/core";
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Login } from'../data-login/models/login.model';
import 'rxjs/add/operator/catch';
#Injectable()
export class LoginService{
private url = 'http://localhost:8080/login';
constructor(private http: Http){}
loginQuery(login: Login){
return this.http.post(this.url,JSON.stringify(login))
.map((res:Response) => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
}
My Component:
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { LoginService } from '../sevices/login.service';
import { Login } from './models/login.model';
import {NgForm} from "#angular/forms";
import {AuthService} from "../../auth.service";
#Component({
selector: 'data-login-component',
templateUrl: './data-login.component.html',
styleUrls: ['./data-login.component.css']
})
export class DataLoginComponent implements OnInit {
cssClass: string;
login: Boolean = false;
constructor(private loginService: LoginService, private router: Router, private authService: AuthService) { }
ngOnInit() {
}
verifyLogin(change: Boolean){
if(change){
console.log('OK');
this.authService.login();
this.router.navigate(['home-aluno']);
}else{
console.log('ERROR');
}
}
onSingin(form: NgForm){
if( (form.value.code !== '') && (form.value.password !== '')){
this.loginService.loginQuery(new Login(form.value.code, form.value.password))
.subscribe(
result => this.verifyLogin(result)
);
}
}
}
My backend working fine. Where is my problem?

Resources