NullInjectorError: No provider for ActivatedRouteSnapshot - ngxs

constructor(private activatedSnapshot: ActivatedSnapshot, private store: Store) {}
route() {
this.store.dispatch(new Navigate(
['..', 'contacts'],
null,
{ relativeTo: this.activatedSnapshot }
));
}
Is there something I'm supposed to import?

Try injecting ActivatedRoute instead of ActivatedSnapshot.
So the code will become:
constructor(private activatedRoute: ActivatedRoute, private store: Store) {}
route() {
this.store.dispatch(new Navigate(
['..', 'contacts'],
null,
{ relativeTo: this.activatedRoute }
));
}

Related

Undefined user.roles with NestJS RoleGuard

I've created the RoleGuard by following the official NestJs documentation. However, I keep getting a 403 error even if my users have the correct Role.
app.module.ts
#Module({
imports: [
MongooseModule.forRoot(process.env.MONGO_DB_URI),
AuthModule,
UserModule,
ProductModule,
StoreModule,
OrderModule,
],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_GUARD,
useClass: JwtAuthGuard,
},
{
provide: APP_GUARD,
useClass: RolesGuard,
},
],
})
export class AppModule {}
According to the documentation:
role.enum.ts
export enum Role {
BUYER = 'buyer',
SELLER = 'seller',
ADMIN = 'admin',
}
roles.decorators.ts
import { SetMetadata } from '#nestjs/common';
import { Role } from './role.enum';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);
roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '#nestjs/common';
import { Reflector } from '#nestjs/core';
import { Role } from './role.enum';
import { ROLES_KEY } from './roles.decorators';
#Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user.roles?.includes(role));
}
}
I called a route with #Roles(Role.BUYER) and logged the user and user.roles to get { userId: undefined, email: 'mary#gmail.com' } and undefined.
My user entity:
user.entity.ts
import { Document } from 'mongoose';
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Role } from 'src/auth/roles/role.enum';
export type UserDocument = UserEntity & Document;
#Schema({
collection: 'users',
timestamps: true,
versionKey: false,
})
export class UserEntity {
#Prop({ required: true })
username: String;
#Prop({ required: true })
password: String;
#Prop({ required: true })
email: String;
#Prop({ required: true })
telephone: String;
#Prop({ required: true, type: String, enum: Role, default: Role.BUYER })
roles: Role[];
#Prop()
storeId: String;
}
export const UserSchema = SchemaFactory.createForClass(UserEntity);
EDIT:
jwt.strategy.ts
#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, email: payload.email };
}
}
Your JwtStrategy does not return a roles property, so req.user.roles will always be undefined. You need to return a roles property as a part of the JwtStrategy#validate method to read req.user.roles later on

TypeGraphql - #inputtype on typeorm

Hello I need to check if there is an email in the database already:
with this:
return User.findOne({ where: { email } }).then((user) => {
if (user) return false;
return true;
});
I have the following inputtypes:
#InputType()
export class RegisterInput {
#Field()
#IsEmail({}, { message: 'Invalid email' })
email: string;
#Field()
#Length(1, 255)
name: string;
#Field()
password: string;
}
I would like to know if there is any way for me to validate the email in the inputtype? or just in my resolve:
#Mutation(() => User)
async register(
#Arg('data')
{ email, name, password }: RegisterInput,
): Promise<User> {
const hashedPassword = await bcrypt.hash(password, 12);
const user = await User.create({
email,
name,
password: hashedPassword,
}).save();
return user;
}
Actually you can register your own decorator for class-validator
For example it can look something like this:
isEmailAlreadyExists.ts
import {
registerDecorator,
ValidationOptions,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
import { UserRepo } from '../../repositories/UserRepo';
import { InjectRepository } from 'typeorm-typedi-extensions';
#ValidatorConstraint({ async: true })
export class isEmailAlreadyExist
implements ValidatorConstraintInterface {
#InjectRepository()
private readonly userRepo: UserRepo;
async validate(email: string) {
const user = await this.userRepo.findOne({ where: { email } });
if (user) return false;
return true;
}
}
export function IsEmailAlreadyExist(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
constraints: [],
validator: isEmailAlreadyExist,
});
};
}
If you're injecting dependencies than you should in inject it in class-validator too. Simply add to your main file this:
import { Container } from 'typedi';
import * as classValidator from 'class-validator';
classValidator.useContainer(Container);
...
const schema = await buildSchema({
resolvers: [...],
container: Container,
});
Then you can use decorator in your InputType
import { InputType, Field } from 'type-graphql';
import { IsEmailAlreadyExist } from '../../../utils/validators/isEmailAlreadyExist';
#InputType()
export class YourInput {
#Field()
#IsEmailAlreadyExist()
email: string;
}
I actually just figured this out myself for my own project.
You can simply add a validation on the email from RegisterInput argument and throw an error if the email already exists.
import { Repository } from 'typeorm'
import { InjectRepository } from 'typeorm-typedi-extensions'
...
// Use dependency injection in the resolver's constructor
constructor(
#InjectRepository(User) private readonly userRepository: Repository<User>
) {}
...
// Your mutation
#Mutation(() => User)
async register(
#Arg('data')
{ email, name, password }: RegisterInput,
): Promise<User> {
const hashedPassword = await bcrypt.hash(password, 12);
const userWithEmail = this.userRepository.find({ email: email })
// If a user with the email was found
if (userWithEmail) {
throw new Error('A user with that email already exists!')
}
const user = await User.create({
email,
name,
password: hashedPassword,
}).save();
return user;
}
To use the InjectRepository make sure you add a "container" to your buildSchema function:
import { Container } from 'typedi'
...
const schema = await buildSchema({
resolvers: [...],
container: Container
})
Let me know if this works out for you? Thanks!

Tying to update PWA: swUpdate.isEnabled is true but not calling the subscribed method even when ngsw-config.json is altered

services/pwa.service.ts:
import { Injectable } from '#angular/core';
import { SwUpdate } from '#angular/service-worker';
import {Observable} from "rxjs/Observable";
import "rxjs/add/observable/interval";
#Injectable()
export class PwaService {
public promptEvent: any;
constructor(private swUpdate: SwUpdate) {
alert('swUpdate isEnabled:' + swUpdate.isEnabled);// => alerts true
if (swUpdate.isEnabled) {
Observable.interval(10)
.subscribe(() => swUpdate.checkForUpdate().then(() => alert('checking for swUpdate')));//<= Not triggered
}
}
public checkForUpdates(): void {
this.swUpdate.available.subscribe(event => this.promptUser());
}
private promptUser(): void {
alert('updating to new version');//<=Not triggered either
this.swUpdate.activateUpdate()
.then(() => document.location.reload());
}
}
services/index.ts:
providers: [
....
{ provide: SwUpdate, useClass: SwUpdate }
]
app.modules.ts:
imports: [
....
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
]
providers: [
...
PwaService,
]
app.component.ts:
import { PwaService } from './services/pwa.service';
....
constructor(public Pwa: PwaService) {
this.Pwa.checkForUpdates();
}
ngsw-config.json(just minor change from lazy to prefetch) to trigger update:
....
"installMode": "prefetch",
....
This worked for me on all devices:
export class PwaUpdateService {
updateSubscription;
constructor(public updates: SwUpdate) {
}
public checkForUpdates(): void {
this.updateSubscription = this.updates.available.subscribe(event => this.promptUser());
if (this.updates.isEnabled) {
// Required to enable updates on Windows and ios.
this.updates.activateUpdate();
interval(60 * 60 * 1000).subscribe(() => {
this.updates.checkForUpdate().then(() => {
// console.log('checking for updates');
});
});
}
// Important: on Safari (ios) Heroku doesn't auto redirect links to their https which allows the installation of the pwa like usual
// but it deactivates the swUpdate. So make sure to open your pwa on safari like so: https://example.com then (install/add to home)
}
promptUser(): void {
this.updates.activateUpdate().then(() => {
window.location.reload();
});
}
}

Passing an additional value to custom validator in Angular2?

I have a validator that checks if a users email address is unique, to do this I need to also pass in the users id so that it doesn't include itself in the unique checks. What is the best way to achieve this?
From what I can tell the validator only has access to the control value. I'm hooking up my validator like this:
<input #emailAddress="ngForm" type="text" [(ngModel)]="user.emailAddress" ngControl="emailAddress" required userExists />
Currently the only way I've been able to achieve it is by setting a static value on the validator, which is not ideal! Here's my full code for the validator:
import { NG_ASYNC_VALIDATORS, Control } from '#angular/common';
import { Directive, provide, forwardRef, Attribute } from '#angular/core';
import { UserService } from './user.service';
import { User } from './user.model';
interface ValidationResult {
[key: string]: boolean;
}
#Directive({
selector: '[userExists][ngModel]',
providers: [
provide(NG_ASYNC_VALIDATORS, {
useExisting: forwardRef(() => UserExistsValidator),
multi: true
})
]
})
export class UserExistsValidator {
public static user: User;
constructor(private _userService: UserService) { }
validate(control: Control): Promise<ValidationResult> {
return new Promise((resolve, reject) => {
this._userService.exists(control.value, UserExistsValidator.user.id).subscribe(
(response: any) => {
if (response.exists)
return resolve({ userExists: { valid: false } });
else
return resolve(null);
},
(error: any) => { console.log(error); }
)
});
}
}
I would use a shared service
#Injectable()
class ValidatorParam {
value:string; // could also be an observable
}
#Directive({
selector: '[userExists][ngModel]',
providers: [
{ provide: NG_ASYNC_VALIDATORS,
useExisting: forwardRef(() => UserExistsValidator),
multi: true
})
]
})
export class UserExistsValidator {
public static user: User;
constructor(private _userService: UserService, private _param:ValidatorParam) { }
validate(control: Control): Promise<ValidationResult> {
return new Promise((resolve, reject) => {
this._param.... // don't know what you want to do with it
this._userService.exists(control.value, UserExistsValidator.user.id).subscribe(
(response: any) => {
if (response.exists)
return resolve({ userExists: { valid: false } });
else
return resolve(null);
},
(error: any) => { console.log(error); }
)
});
}
}
#Component({
selector: '...',
providers: [ValidatorParam],
template: `
<input #emailAddress="ngForm" type="text" [(ngModel)]="user.emailAddress" ngControl="emailAddress" required userExists />
`})
export class MyComponent {
constructor(private _validatorParam:ValidatorParam) {
this._validatorParam.value = xxx;
}
}
This way you can only have one service per component. If you have several input elements in this component, then they need to share the service.
Caution: not tried myself.

Angular2 template driven async validator

I have a problem with defining asynchrous validator in template driven form.
Currently i have this input:
<input type="text" ngControl="email" [(ngModel)]="model.applicant.contact.email" #email="ngForm" required asyncEmailValidator>
with validator selector asyncEmailValidator which is pointing to this class:
import {provide} from "angular2/core";
import {Directive} from "angular2/core";
import {NG_VALIDATORS} from "angular2/common";
import {Validator} from "angular2/common";
import {Control} from "angular2/common";
import {AccountService} from "../services/account.service";
#Directive({
selector: '[asyncEmailValidator]',
providers: [provide(NG_VALIDATORS, {useExisting: EmailValidator, multi: true}), AccountService]
})
export class EmailValidator implements Validator {
//https://angular.io/docs/ts/latest/api/common/Validator-interface.html
constructor(private accountService:AccountService) {
}
validate(c:Control):{[key: string]: any} {
let EMAIL_REGEXP = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*#([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
if (!EMAIL_REGEXP.test(c.value)) {
return {validateEmail: {valid: false}};
}
return null;
/*return new Promise(resolve =>
this.accountService.getUserNames(c.value).subscribe(res => {
if (res == true) {
resolve(null);
}
else {
resolve({validateEmailTaken: {valid: false}});
}
}));*/
}
}
Email regex part is working as expected and form is being validated successfuly if regex is matching. But after that I want to check if e-mail is not already in use, so im creating promise for my accountService. But this doesn't work at all and form is in failed state all the time.
I've read about model driven forms and using FormBuilder as below:
constructor(builder: FormBuilder) {
this.email = new Control('',
Validators.compose([Validators.required, CustomValidators.emailFormat]), CustomValidators.duplicated
);
}
Which have async validators defined in third parameter of Control() But this is not my case because im using diffrent approach.
So, my question is: is it possible to create async validator using template driven forms?
You could try to register the provider of your async validator with the NG_ASYNC_VALIDATORS key and not the NG_VALIDATORS one (only for synchronous validators):
#Directive({
selector: '[asyncEmailValidator]',
providers: [
provide(NG_ASYNC_VALIDATORS, { // <------------
useExisting: EmailValidator, multi: true
}),
AccountService
]
})
export class EmailValidator implements Validator {
constructor(private accountService:AccountService) {
}
validate(c:Control) {
return new Promise(resolve =>
this.accountService.getUserNames(c.value).subscribe(res => {
if (res == true) {
resolve(null);
}
else {
resolve({validateEmailTaken: {valid: false}});
}
}));
}
}
See this doc on the angular.io website:
https://angular.io/docs/ts/latest/api/forms/index/NG_ASYNC_VALIDATORS-let.html
worth noting that the syntax has changed since then, now i am using angular 4, and here below a rewrite:
import { Directive, forwardRef } from '#angular/core';
import { AbstractControl, Validator, NG_ASYNC_VALIDATORS } from '#angular/forms';
import { AccountService } from 'account.service';
#Directive({
selector: '[asyncEmailValidator]',
providers: [
{
provide: NG_ASYNC_VALIDATORS,
useExisting: forwardRef(() => EmailValidatorDirective), multi: true
},
]
})
export class EmailValidatorDirective implements Validator {
constructor(private _accountService: AccountService) {
}
validate(c: AbstractControl) {
return new Promise(resolve =>
this._accountService.isEmailExists(c.value).subscribe(res => {
if (res == true) {
resolve({ validateEmailTaken: { valid: false } });
}
else {
resolve(null);
}
}));
}
}
I am able to correctly call validate custom validators using user service. One problem i was getting was that, I kept my custom validator inside Validators.compose(). After taking out of the compose function everything works.
import { Directive } from '#angular/core';
import { AsyncValidator, AbstractControl, ValidationErrors, NG_ASYNC_VALIDATORS, AsyncValidatorFn } from '#angular/forms';
import { Observable } from 'rxjs';
import { UserService } from '../Services/user.service';
import { map } from 'rxjs/operators';
export function UniqueUsernameValidator(userService: UserService): AsyncValidatorFn {
return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
const q = new Promise((resolve, reject) => {
setTimeout(() => {
userService.isUsernameTaken(control.value).subscribe((data: any) => {
// console.log('top: ' + data + ' type: ' + typeof data);
if (data === false) {
resolve(null);
} else {
resolve({
usernameTaken: {
valid: true
}
});
}
}, () => {
resolve({
usernameTaken: {
valid: false
}
});
});
}, 1000);
});
return q;
};
}
#Directive({
selector: '[appUniqueUsername]',
providers: [{ provide: NG_ASYNC_VALIDATORS, useExisting: UniqueUsernameValidatorDirective, multi: true }, UserService]
})
export class UniqueUsernameValidatorDirective implements AsyncValidator {
constructor(private userService: UserService) { }
validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return UniqueUsernameValidator(this.userService)(control);
}
}

Resources