Trying to use stomp.js in angular2 - javascript

I am trying to implement a Stomp Websocket client using stomp.js.
I am using angular2 with typescript and webpack and am really new to all of those technologies.
My angular2 project was built on this seed:
https://github.com/angular/angular2-seed
As a guide for implementing the stomp.js client I used https://github.com/sjmf/ng2-stompjs-demo
The error I am currently getting is the following:
?d41d:73 EXCEPTION: TypeError: Cannot read property 'client' of undefined in [null]
The error is happening in this method:
public configure() : void {
// Check for errors
if (this.state.getValue() != STOMPState.CLOSED) {
throw Error("Already running!");
}
let scheme : string = 'ws';
if( AppSettings.IS_SSL ) {
scheme = 'wss';
}
this.client = Stomp.client(
scheme + '://'
+ AppSettings.HOST + ':'
+ AppSettings.WEBSOCK_PORT
+ AppSettings.WEBSOCK_ENDPOINT
);
this.client.heartbeat.incoming = AppSettings.HEARTBEAT;
}
So Stomp seems to be undefined.
I am importing:
import {Stomp} from "stompjs";
I have installed stomp.js with npm like this
npm install --save stompjs
And my stompjs module looks like this:
declare module "stompjs" {
export interface Client {
heartbeat: any;
debug(...args: string[]);
connect(...args: any[]);
disconnect(disconnectCallback: () => any, headers?: any);
send(destination: string, headers?:any, body?: string);
subscribe(destination: string, callback?: (message: Message) => any, body?: string);
unsubscribe();
begin(transaction: string);
commit(transaction: string);
abort(transaction: string);
ack(messageID: string, subscription: string, headers?: any);
nack(messageID: string, subscription: string, headers?: any);
}
export interface Message {
command: string;
headers: any;
body: string;
ack(headers?: any);
nack(headers?: any);
}
export interface Frame {
constructor(command: string, headers?: any, body?: string);
toString(): string;
sizeOfUTF8(s: string);
unmarshall(datas: any);
marshall(command: string, headers?, body?);
}
export interface Stomp {
client: Client;
Frame: Frame;
over(ws: WebSocket);
}
}
I think I am missing the connection between my module and the actual library, but I don't really know how to that and I can't figure it out from the github demo either.
Thanks in advance for any help!

Did you try to export a variable too alongside the interface?
export var Stomp: Stomp;

Using Stomp.js directly in Angular2 (or Angular4) is definitely non trivial.
For a proper Angular2 (and Angular4) type StompService using Observables please refer to https://github.com/stomp-js/ng2-stompjs.
There are sample apps for Angular2 and Agular4 as well.

Related

How to create and use a terminal command with nest-commander?

So, I have this NestJS project, and for learning purposes I want to create a command with nest-commander that would be executable on terminal (that way I could call a function from other services), also for learning purposes, whenever I call this command, it should call a function on a service file that get's a user from the database.
It would look like this :
> run myCommand -username UsernameString
Whenever that command is called from the terminal, I would call getUser() from AnotherService to find my user with that specific UsernameString.
I read the docs and couldn't figure much of it, so...
How do I call a command from terminal?
Is it possible to call the same command within the application?
So basically if we take this example:
import { Module } from '#nestjs/common';
import { Command, CommandFactory, CommandRunner, Option } from 'nest-commander';
interface BasicCommandOptions {
string?: string;
boolean?: boolean;
number?: number;
}
#Command({ name: 'basic', description: 'A parameter parse' })
export class BasicCommand extends CommandRunner {
async run(
passedParam: string[],
options?: BasicCommandOptions,
): Promise<void> {
if (options?.number) {
this.runWithNumber(passedParam, options.number);
} else if (options?.string) {
this.runWithString(passedParam, options.string);
} else {
this.runWithNone(passedParam);
}
}
#Option({
flags: '-n, --number [number]',
description: 'A basic number parser',
})
parseNumber(val: string): number {
return Number(val);
}
#Option({
flags: '-s, --string [string]',
description: 'A string return',
})
parseString(val: string): string {
return val;
}
#Option({
flags: '-b, --boolean [boolean]',
description: 'A boolean parser',
})
parseBoolean(val: string): boolean {
return JSON.parse(val);
}
runWithString(param: string[], option: string): void {
console.log({ param, string: option });
}
runWithNumber(param: string[], option: number): void {
console.log({ param, number: option });
}
runWithNone(param: string[]): void {
console.log({ param });
}
}
#Module({
providers: [BasicCommand],
})
export class AppModule {}
async function bootstrap() {
await CommandFactory.run(AppModule);
}
bootstrap();
You can run it using that method:
ts-node ./test.ts basic -s test-value -n 1234
First you call the name of the command then the params

NestJs: Make sure your class is decorated with an appropriate decorator

I am using graphql-request as a GraphQL client to query a headless CMS to fetch stuff, modify and return to the original request/query. headless cms is hosted separately fyi.
I have the following code :
#Query(returns => BlogPost)
async test() {
const endpoint = 'https://contentxx.com/api/content/project-dev/graphql'
const graphQLClient = new GraphQLClient(endpoint, {
headers: {
authorization: 'Bearer xxxxxxx',
},
})
const query = gql`
{
findContentContent(id: "9f5dde89-7f9b-4b9c-8669-1f0425b2b55d") {
id
flatData {
body
slug
subtitle
title
}
}
}`
return await graphQLClient.request(query);
}
BlogPost is a model having the types :
import { Field, ObjectType } from '#nestjs/graphql';
import { BaseModel } from './base.model';
import FlatDateType from '../resolvers/blogPost/types/flatDatatype.type';
#ObjectType()
export class BlogPost extends BaseModel {
#Field({ nullable: true })
id!: string;
#Field((type) => FlatDateType)
flatData: FlatDateType;
}
and FlatDateType has the following code
export default class FlatDateType {
body: string;
slug: string;
subtitle: string;
title: string;
}
it throws the following exception :
Error: Cannot determine a GraphQL output type for the "flatData". Make
sure your class is decorated with an appropriate decorator.
What is missing in here?
How is your graphql server supposed to understand the type of FlatDataType when there's no information about it being passed to the graphql parser? You need to add the graphql decorators to it as well. #ObjectType(), #Field(), etc.
FlatDataType is not defined as #ObjectType(), therefore type-graphql (or #nestjs/graphql) can't take it as an output in GraphQL.

How to use validation in NestJs with HTML rendering?

NestJS uses validation with validation pipes and
#UsePipes(ValidationPipe)
If this fails it throws an exception. This is fine for REST APIs that return JSON.
How would one validate parameters when using HTML rendering and return
{ errors: ['First error'] }
to an hbs template?
You can create an Interceptor that transforms the validation error into an error response:
#Injectable()
export class ErrorsInterceptor implements NestInterceptor {
intercept(
context: ExecutionContext,
call$: Observable<any>,
): Observable<any> {
return call$.pipe(
// Here you can map (or rethrow) errors
catchError(err => ({errors: [err.message]}),
),
);
}
}
You can use it by adding #UseInterceptors(ErrorsInterceptor) to your controller or its methods.
I've been driving myself half mad trying to find a "Nest like" way to do this while still retaining a degree of customisability, and I think I finally have it. Firstly, we want an error that has a reference to the exisiting class-validator errors, so we create a custom error class like so:
import { ValidationError } from 'class-validator';
export class ValidationFailedError extends Error {
validationErrors: ValidationError[];
target: any;
constructor(validationErrors) {
super();
this.validationErrors = validationErrors;
this.target = validationErrors[0].target
}
}
(We also have a reference to the class we tried to validate, so we can return our object as appropriate)
Then, in main.ts, we can set a custom exception factory like so:
app.useGlobalPipes(
new ValidationPipe({
exceptionFactory: (validationErrors: ValidationError[] = []) => {
return new ValidationFailedError(validationErrors);
},
}),
);
Next, we create an ExceptionFilter to catch our custom error like so:
#Catch(ValidationFailedError)
export class ValidationExceptionFilter implements ExceptionFilter {
view: string
objectName: string
constructor(view: string, objectName: string) {
this.view = view;
this.objectName = objectName;
}
async catch(exception: ValidationFailedError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
response.render(this.view, {
errors: exception.validationErrors,
[this.objectName]: exception.target,
url: request.url,
});
}
}
We also add an initializer, so we can specify what view to render and what the object's name is, so we can set up our filter on a controller method like so:
#Post(':postID')
#UseFilters(new ValidationExceptionFilter('blog-posts/edit', 'blogPost'))
#Redirect('/blog-posts', 301)
async update(
#Param('id') postID: string,
#Body() editBlogPostDto: EditBlogPostDto,
) {
await this.blogPostsService.update(postID, editBlogPostDto);
}
Hope this helps some folks, because I like NestJS, but it does seem like the docuemntation and tutorials are much more set up for JSON APIs than for more traditional full stack CRUD apps.

Type 'Observable<Object>' is not assignable to type 'Observable<IUser[]>'

In my Api service I have this simple getUsers function to fetch all of the users on the api.
public getUsers(url: string): Observable<IUser[]> {
return this._http.get(url);
}
This is my IUser interface, I have made all the field optional for now.
export interface IUser {
id?: string;
first_name?: string;
last_name?: string;
location?: string;
followers?: string;
following?: string;
checkins?: string;
image?: string;
}
and here is how I have used the service in my component:
export class SocialOverviewContainerComponent implements OnInit {
public userData = [];
public showForm = {};
private _apiService: ApiService;
constructor(apiService: ApiService) {
this._apiService = apiService
}
public ngOnInit(): void {
this.getUsersData();
}
public getUsersData() {
this._apiService.getUsers(ApiSettings.apiBasepath + 'users/')
.subscribe(users => {
this.userData = users;
})
}
}
and this is is the Type error I get when it compiles
ERROR in src/app/services/api.service.ts(18,5): error TS2322: Type 'Observable<Object>' is not assignable to type 'Observable<IUser[]>'.
Type 'Object' is not assignable to type 'IUser[]'.
The 'Object' type is assignable to very few other types. Did you mean to use the 'any' type instead?
Property 'includes' is missing in type 'Object'.
I thought this might be because my response doesn't match the interface, which I have double check and it does. And I have also made the field optional for now to make sure.
I know I can fix this by casting the observable as any, but doesn't that defeat the point of using Typescript?
Any help on where I am going wrong would be great
Thanks in advance
There are two ways to do this, and it depends on which version of RxJS / Angular that you are using. Here are the two ways to do it depending on your versions:
// Using RxJS v4 / Angular v2-4. I assume that you're using the HttpModule...
import 'rxjs/add/operator/map';
public getUsers(url: string): Observable<IUser[]> {
return this._http.get(url)
.map((response: Response) => <IUser[]>response.json());
}
// Using RxJS v5 / Angular 5+ (at the moment). I assume that you're using the HttpClientModule, as the HttpModule was deprecated in Angular v4.
public getUsers(url: string): Observable<IUser[]> {
return this._http.get<IUser[]>(url);
}
Typical, that 5 mins after I post, I find a solution.
Trick was to cast the .get function in the service the same as the type on the getUsers function as below:
public getUsers(url: string): Observable<IUser[]> {
return this._http.get<IUser[]>(url);
}
Hope this helps other people out as well
in my case the below worked
getUsers(): Observable<User[]> {
return <Observable<User[]>> this.http.get(this.pathAPI + 'user', super.header())
.pipe(catchError(super.handleError));
}
I am using Angular 6
In my case couple of param values is a number type. I need to convert to string to solve the issue.
const params = new HttpParams()
.set('someparam1', myNumberproperty1.toString())
.set('someparam2',myNumberproperty2.toString());

Angular2 Service Returns Undefined

For some reason my services aren't working. I've been lurking SO for two days trying to find similar questions, but they don't fit my problem.
Service.ts:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { CarObject } from './make';
#Injectable()
export class EdmundsService {
private stylesurl = 'REDACTED';
constructor(private http: Http) { }
getCars(): Observable<CarObject[]> {
return this.http.get(this.stylesurl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || { };
}
private handleError (error: Response | any) {
// In a real world app, we might use a remote logging infrastructure
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
These are my 'models':
class Style {
id: number;
name: string;
make: Make;
model: Model;
year: Year;
submodel: Submodel;
trim: string;
states: string[];
engine: Engine;
transmission: Transmission;
options: Options[];
colors: Color[];
drivenWheels: string;
numOfDoors: string;
squishVins: string[];
categories: Categories;
MPG: MPG;
manufacturerOptionCode: string;
}
export class CarObject {
styles: Style[];
stylesCount: number;
}
My component:
import { CarObject } from './make';
import { EdmundsService } from './edmunds-search-result.service';
#Component({REDACTED
providers: [EdmundsService] })
export class EdmundsSearchResultComponent implements OnInit {
cars: CarObject[];
errorMessage: string;
constructor(private _edmundsService: EdmundsService) { }
getCars(): void {
this._edmundsService.getCars()
.subscribe(
cars => this.cars = cars,
error => this.errorMessage = <any>error);
}
ngOnInit(): void {
this.getCars();
}
}
Component HTML:
{{ cars.stylesCount | async }}
Sample API Response: http://pastebin.com/0LyZuPGW
Error Output:
EXCEPTION: Error in ./EdmundsSearchResultComponent class
EdmundsSearchResultComponent - inline template:0:0 caused by:
Cannot read property 'stylesCount' of undefined
CarObject was designed to match the API Response, so it could be okay to remove the array brackets ( [] )
I don't know why this won't display the object data on my template despite closely following the Tour Of Heroes HTTP/Services tutorial.
What I am trying to do is make an HTTP request from variable 'styleurl' (which I see is successfully made by checking the 'Network' tab in chrome dev tools.) Using this API Response, I want my CarObject to 'consume' the json response, and be available to my component/template.
In your component you're reserving your car property, but you don't initialize it, so it remains undefined.
At the time your HTML renders the promise isn't fulfilled yet, your car is still undefined but you try to access a property from it.
A couple solutions:
preset it:
cars: CarObject = new CarObject(); // or <CarObject>{}
use the elvis operator in your template:
{{ cars?.stylesCount }}
use ngIf:
<div *ngIf="cars">{{ cars.styleCount }}</div>
There are probably a couple of more ways to handle this case.
See my update at the bottom regarding your usage of the async pipe. It probably leads to errors as well in the way you're trying to use it.
Besides, i would suggest reading up on TypeScript types as well as general best practices for angular and typescript especially regarding the usage of models, interfaces and such. Also using Observables would be a good idea instead of Promises.
There are some issues in your code, so this is just a hint, but elaborating on them has no place here i think and aren't the source of your problem.
Hope i could help.
Update:
About your usage of the async pipe:
The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted.
You use it on an array of CarObjects which btw shouldn't be an array.
Take a look at the documentation for the async pipe for proper usage.

Categories