I have a simple component that retrieves data from the server.
#Component({
selector: 'app',
template: `
<h1>HEllo {{category}}</h1>
`,
providers: [
HTTP_PROVIDERS,
CategoryService
]
})
export class App implements OnInit {
private category;
constructor(private _categoryService: CategoryService){}
ngOnInit() {
this._categoryService.getCategories().subscribe(categories => this.category = categories.name);
}
}
And my service looks like
#Injectable()
export default class CategoryService {
constructor(private http: Http){}
private _categoriesUrl = 'http://localhost:3000/api/photo';
getCategories() {
return this.http.get(this._categoriesUrl)
.map(res => return res.json().data;)
.do(data => console.log(data))
.catch(this.handleError);
}
private handleError(error: Response) {
return Observable.throw(error.json().error || 'Server error');
}
}
It works well when I'm rendering it on client side but when I'm using angular2-universal-preview for rendering it on server side it throws an error
XMLHttpRequest is not defined
How could I make it working with server side rendering?
You need to add
providers: [
NODE_HTTP_PROVIDERS,
CategoryService
]
See also https://github.com/angular/universal/issues/292
I think it would be better to add NODE_HTTP_PROVIDERS to bootstrap(AppComponent, [NODE_HTTP_PROVIDERS]) instead of each (or some components) except when you have a good reason to request different instances of Http for each component.
Related
Needs to DI Kafka client in guard:
auth.guard.ts
export class AuthGuard implements CanActivate {
private _client: ClientKafka; <----- // TODO implement nestjs DI mechanism
public async canActivate(context: ExecutionContext): Promise<boolean> {
try {
const request = context.switchToHttp().getRequest();
const authorization: string = request.get('Authorization');
...code here just send data to jwt service...
return true;
} catch (err) {
return false;
}
}
}
I use new in canActivate for creating an instance of Kafka client in auth.guard.ts. But how to inject a class in guard with #Inject? I used to create #Global module, which provides and export Kafka client class, but it's not working...
Use This in the module for globally using the guard
providers: [{provide: APP_GUARD, useClass: AuthGuard}]
As for your question about injecting a class inside a guard, you need to inject it inside the constructor of the AuthGuard class
export class AuthGuard implements CanActivate {
constructor(private clientKafka : ClientKafka){}
}
if this doesn't work, try using
constructor(#Inject(private clientKafka : ClientKafka)){}
Hope this resolves your issue :)
I keep on getting 'can't access lexical declaration' error when i try to access this page but it works fine when i comment pouch db statements.
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import PouchDB from 'pouchdb';
#Component({
selector: 'app-newperson',
templateUrl: './newperson.page.html',
styleUrls: ['./newperson.page.scss'],
})
export class NewpersonPage implements OnInit {
private name;
private email;
private phone;
private db;
constructor(
private router : Router
) {
// this.setupDB();
}
setupDB() {
this.db = new PouchDB('contacts');
}
ngOnInit() {
// console.log(PouchDB);
this.setupDB();
this.name = 'John Doe';
}
goBack() {
this.router.navigate(["/home"]);
}
save() {
// this.db.post({
// name: this.name,
// email: this.email,
// phone: this.phone
// })
console.log('save clicked');
}
}
My primary goal at this stage is just getting started with pouchdb. Also the youtube video link :-
https://www.youtube.com/watch?v=HWbsN1QIIN8&t=1693s
Just making sure. Did you add pouchdb to your ionic project package.json? In my projects db is defined as an any.
private db: any;
Sorry for my bad english, I'm from Ukraine :)
Could you tell me how can I create my own service, that extends of Jwt service provided jwt module from npm package? I want to create my own JwtService for catch errors and isolate duplicate logic for token creation and verification. Please, help me how can I do it. Code samples attached.
import { BadRequestException, Injectable } from '#nestjs/common';
import { JwtService as NestJwtService, JwtVerifyOptions } from '#nestjs/jwt';
#Injectable()
export class OwnJwtService extends NestJwtService {
constructor() {
super({});
}
async verifyAsync<T>(token: string, options?: JwtVerifyOptions): Promise<T> {
try {
const res = await super.verifyAsync(token, options);
console.log('res', res);
return res;
} catch (error) {
// My own logic here ...
throw new BadRequestException({
error,
message: 'Error with verify provided token',
});
}
}
}
or maybe I need to inject nestjs jwt service to my own service ? example:
import { BadRequestException, Injectable } from '#nestjs/common';
import { JwtService as NestJwtService, JwtVerifyOptions } from '#nestjs/jwt';
#Injectable()
export class OwnJwtService {
constructor(private readonly jwtService: NestJwtService) {}
async verifyAsync<T>(token: string, options?: JwtVerifyOptions): Promise<T> {
try {
const res = await this.jwtService.verifyAsync(token, options);
console.log('res', res);
return res;
} catch (error) {
throw new BadRequestException({
error,
message: 'Error with verify provided token',
});
}
}
}
and
import { JwtModule as NestJwtModule } from '#nestjs/jwt';
import { ConfigModule, ConfigService } from '#nestjs/config';
import { Module } from '#nestjs/common';
import { OwnJwtService } from 'src/modules/jwt/jwt.service';
#Module({
imports: [
NestJwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
signOptions: {
expiresIn: process.env.JWT_EXPIRES_IN,
},
secret: process.env.JWT_SECRET,
secretOrPrivateKey: process.env.JWT_SECRET,
}),
inject: [ConfigService],
}),
],
providers: [OwnJwtService],
exports: [OwnJwtService],
})
export class JwtModule {}
but it doesn't work for me, and I have similar errors:
Error: Nest can't resolve dependencies of the OwnJwtService (?). Please make sure that the argument JwtService at index [0] is available in the AuthModule context.
First, notice that the JwtModule basically creates a module based on jsonwebtoken and your custom errors aren't meant to be dealt inside it.
Second, when you use registerAsync you are meant to get your ENV variables with the ConfigService as in configService.get('JWT_SECRET').
Third, your question is inefficient. The JwtModule already does everything you need. You just need to implement it. Again, just think of it as the jsonwebtoken package adapted for Nest. That's it.
On the signup, login and refreshtoken (if existing) routes you sign when you create a new token.
And in your requests middleware you verify.
One kind of a big issue with Nest is its documentation. It doesn't have everything you need. There might be more than one way to verify a route, but the most straightforward is just using Express middleware, as in a typical Express app.
To do this, you need to implement it in the AppModule like this:
#Module(...)
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer): MiddlewareConsumer | void {
consumer.apply(cookieParser(), AuthMiddleware).forRoutes('/');
}
}
In this example, I'm also registering the module cookieParser() because I send the tokens in a cookie. Other cookie modules will do, too. Both the NestModule and the MiddlewareConsumer come from #nestjs/common.
AuthMiddleware is a middleware I made using this skeleton...
export class AuthMiddleware implements NestMiddleware {
constructor(
private readonly configService: ConfigService,
private readonly jwtService: JwtService
) {}
async use(req: Request, res: Response, next: NextFunction) {
const { yourJwtToken } = req.cookies;
const isValidToken = this.jwtService.verify(
yourJwtToken,
this.configService.get('JWT_SECRET'),
);
if (!isValidToken) throw new UnauthorizedException();
// etc...
next();
}
}
Finally, what you might be asking to, is to apply the AuthGuard.
If you use the Passport ones, you need just to follow the documentation to apply them. They already throw errors if you. If you want to change it, just rewrite its methods.
You can also do it manually. Just use the console to generate a guard, and in there you can check authentication context.switchToHttp().getRequest() and return a boolean after checking the credentials and use the constructor to check the permissions if you want.
You might also skip the middleware config from above and implement the logic inside the guard if you will.
Again, I don't really think changing the JwtModule is the best idea here.
What I am trying to do is when the splash screen is loading, a http request is made to the server to pull some information and pass the response to another page.
Below is the code I am working with.
import { Component } from '#angular/core';
import { Platform, LoadingController } from 'ionic-angular';
import { StatusBar, Splashscreen } from 'ionic-native';
import { CacheService } from "ionic-cache/ionic-cache";
import { Apis } from './apis';
import { StayPage} from '../pages/stay/stay';
#Component({
templateUrl: 'app.html',
providers: [Apis]
})
export class MyApp {
rootPage = StayPage;
constructor(platform: Platform, cache: CacheService, public loadingCtrl: LoadingController, public Apis: Apis ) {
cache.setDefaultTTL(60 * 60);
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
this.Apis.types().subscribe( response => {
response.results;
StatusBar.styleDefault();
Splashscreen.hide();
}, err => {
this.Apis.error( err );
});
});
}
}
When I run the above code, the splash screen is stuck on loading and doesn't move to another page.
You need to make HTTP request in constructor or ngOnInit of StayPage.
export class StayPage implements OnInit {
...
constructor(public navCtrl: NavController,
public navParams: NavParams
public http: Http) { }
ngOnInit(){
this.http.get(apiUrl)
.subscribe(
responseSuccess => ...
responseError => ...
}
}
}
I can imagine following approaches to exchange Data between multi step forms:
1) Create a component for each form step and exchange data between components over #input, #output (e.g. you cannot change from step5 to 2)
2) Use the new property data in the new router (see here) (e.g. you cannot change from step5 to 2))
3) A shared Service (Dependency Injection) to store data (Component Interaction) (e.g. you can change from step5 to 2)
4) New rudiments with #ngrx/store (not really experienced yet)
Can you give some "gained experience values", what do you use and why?
See my edit below.
Using SessionStorage is not strictly the 'angular' way to approach this in my opinion—a shared service is the way to go. Implementing routing between steps would be even better (as each component can have its own form and different logic as you see fit:
const multistepRoutes: Routes = [
{
path: 'multistep',
component: MultistepComponent,
children: [
{
path: '',
component: MultistepBaseComponent,
},
{
path: 'step1',
component: MultistepStep1Component
},
{
path: 'step2',
component: MultistepStep2Component
}
]
}
];
The service multistep.service can hold the model and implement logic for components:
import { Injectable, Inject } from '#angular/core';
import { Router } from '#angular/router';
#Injectable()
export class MultistepService {
public model = {};
public baseRoute = '/multistep';
public steps = [
'step1',
'step2'
];
constructor (
#Inject(Router) public router: Router) { };
public getInitialStep() {
this.router.navigate([this.baseRoute + '/' + this.steps[0]]);
};
public goToNextStep (direction /* pass 'forward' or 'backward' to service from view */): any {
let stepIndex = this.steps.indexOf(this.router.url.split('/')[2]);
if (stepIndex === -1 || stepIndex === this.steps.length) return;
this.router.navigate([this.baseRoute + '/' + this.steps[stepIndex + (direction === 'forward' ? 1 : -1)]]);
};
};
Good luck.
EDIT 12/6/2016
Actually, now having worked with the form API for a while I don't believe my previous answer is the best way to achieve this.
A preferrable approach is to create a top level FormGroup which has each step in your multistep form as it's own FormControl (either a FormGroup or a FormArray) under it's controls property. The top level form in such a case would be the single-source of truth for the form's state, and each step on creation (ngOnInit / constructor) would be able to read data for its respective step from the top level FormGroup. See the pseudocode:
const topLevelFormGroup = new FormGroup({
step1: new FormGroup({fieldForStepOne: new FormControl('')}),
step2: new FormGroup({fieldForStepTwo}),
// ...
});
...
// Step1Component
class Step1Component {
private stepName: string = 'step1';
private formGroup: FormGroup;
constructor(private topLevelFormGroup: any /* DI */) {
this.formGroup = topLevelFormGroup.controls[this.stepName];
}
}
Therefore, the state of the form and each step is kept exactly where it should be—in the form itself!
Why not use session storage? For instance you can use this static helper class (TypeScript):
export class Session {
static set(key:string, value:any) {
window.sessionStorage.setItem(key, JSON.stringify(value));
}
static get(key:string) {
if(Session.has(key)) return JSON.parse(window.sessionStorage[key])
return null;
}
static has(key:string) {
if(window.sessionStorage[key]) return true;
return false;
}
static remove(key:string) {
Session.set(key,JSON.stringify(null)); // this line is only for IE11 (problems with sessionStorage.removeItem)
window.sessionStorage.removeItem(key);
}
}
And using above class, you can put your object with multi-steps-forms data and share it (idea is similar like for 'session helper' in many backend frameworks like e.g. php laravel).
The other approach is to create Singleton service. It can look like that (in very simple from for sake of clarity) (I not test below code, I do it from head):
import { Injectable } from '#angular/core';
#Injectable()
export class SessionService {
_session = {};
set(key:string, value:any) {
this._session[key]= value; // You can also json-ize 'value' here
}
get(key:string) {
return this._session[key]; // optionally de-json-ize here
}
has(key:string) {
if(this.get(key)) return true;
return false;
}
remove(key:string) {
this._session[key]=null;
}
}
And then in your main file where you bootstrap application:
...
return bootstrap(App, [
...
SessionService
])
...
And the last step - critical: When you want to use you singleton service in your component - don't put int in providers section (this is due to angular2 DI behavior - read above link about singleton services). Example below for go from form step 2 to step 3:
import {Component} from '#angular/core';
import {SessionService} from './sessionService.service';
...
#Component({
selector: 'my-form-step-2',
// NO 'providers: [ SessionService ]' due to Angular DI behavior for singletons
template: require('./my-form-step-2.html'),
})
export class MyFormStep2 {
_formData = null;
constructor(private _SessionService: SessionService) {
this._formData = this._SessionService.get('my-form-data')
}
...
submit() {
this._SessionService.set('my-form-data', this._formData)
}
}
It should looks like that.