Im newbee for angular 7 and now trying to implement CanActive, but im getting error :
Can anyone guide me to overcome this. My code samples are as follows :
auth.guard.ts :
import { Injectable } from '#angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth-service.service';
import {Router} from '#angular/router';
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService,
private myRoute: Router){
}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
if (this.auth.isLoggednIn()){
return true;
} else {
this.myRoute.navigate(["login"]);
return false;
}
}
}
Using a promise in an if condition is always a bad idea, since it does not get resolved. You could return the promise itself, using resolve to pass the resulting boolean further down the line:
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Promise<boolean> | boolean {
return new Promise(resolve =>
this.auth.isLoggednIn()
.then(status: boolean => {
if(status === false) {
this.myRoute.navigate(["login"]);
}
resolve(status);
})
.catch(() => {
this.myRoute.navigate(["login"]);
resolve(false);
// ... or any other way you want to handle such error case
})
}
}
Please try this:
return this.auth.isLoggednIn() ? true : this.myRoute.navigate(["login"]);
I got the SAME error message when I did the following:
canDeactivate(component: CreateEmployeeComponent): boolean {
if(component.createEmployeeForm.dirty) {
return confirm('Are you sure you want to discard your changes?');
}
return true;
}
So I solved the problem in the following way:
canDeactivate(component: CreateEmployeeComponent): boolean {
return component.createEmployeeForm.dirty
? confirm('Are you sure you want to discard your changes?')
: true;
}
I'm not sure it works for you, but you can at least give it a try.
I just got this same error.
The reason I got this was I had declared (in my routing module app-routing.module.ts) my guard to be canLoad but later on I changed the method (in guard file) to CanActivate, so naturally Angular got confused.
Check that the class given into routes (in this case MyGuardClass) {path: 'aPath', component: myComponent, canActivate: [MyGuardClass]} implements CanActivate as documented here
Related
I want to ask something, yesterday I was using Dart version 2.10, after I upgraded to version 2.13, I can't run this code can someone help me
class AmtException implements Exception {
String errMsg() => 'Amount should be greater than zero';
}
void withdraw_amt(int amt) {
if (amt <= 0) {
throw new AmtException();
}
}
void main() {
try {
withdraw_amt(-1);
} catch (e) {
print(e.errMsg());
} finally {
print('Ending requested operation.....');
}
}
This output :
Error: The method 'errMsg' isn't defined for the class 'Object'.
- 'Object' is from 'dart:core'.
Try correcting the name to the name of an existing method, or defining a method named 'errMsg'.
print(e.errMsg());
^^^^^^
The final code:
class AmtException implements Exception {
String errMsg() => 'Amount should be greater than zero';
}
void withdraw_amt(int amt) {
if (amt <= 0) {
throw new AmtException();
}
}
void main() {
try {
withdraw_amt(-1);
} on AmtException catch (e) {
print(e.errMsg());
} finally {
print('Ending requested operation.....');
}
}
Thanks #jamesdlin
I can't figure out how to pass my app object to my TypeGrapQL resolvers.
I created my types and resolvers and setup a graphql server using express-graphql. I was able to run the graph, but no luck in passing the app object to use the registered services.
My graphql.service.ts looks like this:
import { ServiceAddons } from '#feathersjs/feathers'
import { graphqlHTTP } from 'express-graphql'
import 'reflect-metadata'
import { buildSchemaSync } from 'type-graphql'
import { Container } from 'typedi'
import { Application } from '../../declarations'
import { Graphql } from './graphql.class'
import { ArticleResolver } from './resolvers/article.resolver'
// Add this service to the service type index
declare module '../../declarations' {
interface ServiceTypes {
graphql: Graphql & ServiceAddons<any>
}
}
export default async function (app: Application): Promise<void> {
const schema = buildSchemaSync({
resolvers: [__dirname + '/resolvers/*.resolver.ts'],
container: Container,
})
app.use(
'/graphql',
graphqlHTTP({
schema: schema,
graphiql: true,
})
)
}
and here's one of my resolver classes article.resolver.ts
import { Arg, Query, Resolver } from 'type-graphql'
import { Service } from 'typedi'
import { Application } from '../../../declarations'
import { Category } from '../types/category.type'
#Service()
#Resolver(Category)
export class CategoryResolver {
constructor(private readonly app: Application) {}
#Query((returns) => [Category])
async categories() {
try {
const result = await this.app.service('category').find()
return (result as any).data // TODO: Refactor to return result with pagination details
} catch (err) {
console.log('Categories resolver error', err)
return []
}
}
}
I can't do this.app.service() as this.app is undefined
Im a little confused on how dependency injection works in TypeGrapQL, any help is appreciated.
Thanks
I managed to make it work, here's my solution if anyone has the same problem:
I created a Graphql class decorated with #Service from typedi that takes in an app object as such
import { Service } from 'typedi'
import { Application } from '../../declarations'
#Service()
export class Graphql {
app: Application
//eslint-disable-next-line #typescript-eslint/no-unused-vars
constructor(app: Application) {
this.app = app
}
}
In my graphql.service.ts I initiated the class and passed down the instance to the typedi container
import { buildSchemaSync } from 'type-graphql'
import { Container } from 'typedi'
import { Application } from '../../declarations'
import { Graphql } from './graphql.class'
export default async function (app: Application): Promise<void> {
const graphql = new Graphql(app)
Container.set('graphql', graphql)
const schema = buildSchemaSync({
resolvers: [__dirname + '/resolvers/category.resolver.ts'],
container: Container, // Pass the container to the resolvers
})
// Initialize our express graphql server
}
And Finally in my resolvers I am decorating the resolver with #Service and injecting the graphql instance to the constructor:
import { Application } from '../../../declarations'
import { Graphql } from '../graphql.class'
import { Inject, Service } from 'typedi'
#Service()
#Resolver(Category)
export class CategoryResolver {
app: Application
constructor(#Inject('graphql') private readonly graphql: Graphql) {
this.app = this.graphql.app
}
// Queries and Mutations
}
This solved it to me, hope it comes with any help to you 😊
I'm trying to create a decorator that requires dependency injection.
For example:
#Injectable()
class UserService{
#TimeoutAndCache(1000)
async getUser(id:string):Promise<User>{
// Make a call to db to get all Users
}
}
The #TimeoutAndCache returns a new promise which does the following:
if call takes longer than 1000ms, returns a rejection and when the call completes, it stores to redis (so that it can be fetched next time).
If call takes less than 1000ms, simply returns the result
export const TimeoutAndCache = function timeoutCache(ts: number, namespace) {
return function log(
target: object,
propertyKey: string,
descriptor: TypedPropertyDescriptor<any>,
) {
const originalMethod = descriptor.value; // save a reference to the original method
descriptor.value = function(...args: any[]) {
// pre
let timedOut = false;
// run and store result
const result: Promise<object> = originalMethod.apply(this, args);
const task = new Promise((resolve, reject) => {
const timer = setTimeout(() => {
if (!timedOut) {
timedOut = true;
console.log('timed out before finishing');
reject('timedout');
}
}, ts);
result.then(res => {
if (timedOut) {
// store in cache
console.log('store in cache');
} else {
clearTimeout(timer);
// return the result
resolve(res);
}
});
});
return task;
};
return descriptor;
};
};
I need to inject a RedisService to save the evaluated result.
One way I could inject Redis Service in to the UserService, but seems kind ugly.
You should consider using an Interceptor instead of a custom decorator as they run earlier in the Nest pipeline and support dependency injection by default.
However, because you want to both pass values (for cache timeout) as well as resolve dependencies you'll have to use the mixin pattern.
import {
ExecutionContext,
Injectable,
mixin,
NestInterceptor,
} from '#nestjs/common';
import { Observable } from 'rxjs';
import { TestService } from './test/test.service';
#Injectable()
export abstract class CacheInterceptor implements NestInterceptor {
protected abstract readonly cacheDuration: number;
constructor(private readonly testService: TestService) {}
intercept(
context: ExecutionContext,
call$: Observable<any>,
): Observable<any> {
// Whatever your logic needs to be
return call$;
}
}
export const makeCacheInterceptor = (cacheDuration: number) =>
mixin(
// tslint:disable-next-line:max-classes-per-file
class extends CacheInterceptor {
protected readonly cacheDuration = cacheDuration;
},
);
You would then be able to apply the Interceptor to your handler in a similar fashion:
#Injectable()
class UserService{
#UseInterceptors(makeCacheInterceptor(1000))
async getUser(id:string):Promise<User>{
// Make a call to db to get all Users
}
}
Trying to use Mockito to test my BLoC, the BLoC makes a server call using a repository class and the server call function is supposed to throw a custom exception if the user is not authenticated.
But when I am trying to stub the repository function to throw that custom exception, the test just fails with the following error:
sunapsis Authorization error (test error): test description
package:mockito/src/mock.dart 342:7 PostExpectation.thenThrow.<fn>
package:mockito/src/mock.dart 119:37 Mock.noSuchMethod
package:sunapsis/datasource/models/notifications_repository.dart 28:37 MockNotificationRepository.getNotificationList
package:sunapsis/blocs/notification_blocs/notification_bloc.dart 36:10 NotificationBloc.fetchNotifications
test/blocs/notification_blocs/notification_bloc_test.dart 53:48 main.<fn>.<fn>.<fn>
===== asynchronous gap ===========================
dart:async scheduleMicrotask
test/blocs/notification_blocs/notification_bloc_test.dart 53:7 main.<fn>.<fn>
And this is what my BLoC code looks like: fetchNotifications function calls the repository function and handles the response and errors. There are two catchError blocks, one handles AuthorizationException case and other handles any other Exception. Handling AuthorizationException differently because it will be used to set the Login state of the application.
notification_bloc.dart
import 'dart:async';
import 'package:logging/logging.dart';
import 'package:rxdart/rxdart.dart';
import 'package:sunapsis/datasource/dataobjects/notification.dart';
import 'package:sunapsis/datasource/models/notifications_repository.dart';
import 'package:sunapsis/utils/authorization_exception.dart';
class NotificationBloc {
final NotificationsRepository _notificationsRepository;
final Logger log = Logger('NotificationBloc');
final _listNotifications = PublishSubject<List<NotificationElement>>();
final _isEmptyList = PublishSubject<bool>();
final _isLoggedIn = PublishSubject<bool>();
Observable<List<NotificationElement>> get getNotificationList =>
_listNotifications.stream;
Observable<bool> get isLoggedIn => _isLoggedIn.stream;
Observable<bool> get isEmptyList => _isEmptyList.stream;
NotificationBloc({NotificationsRepository notificationsRepository})
: _notificationsRepository =
notificationsRepository ?? NotificationsRepository();
void fetchNotifications() {
_notificationsRepository
.getNotificationList()
.then((List<NotificationElement> list) {
if (list.length > 0) {
_listNotifications.add(list);
} else {
_isEmptyList.add(true);
}
})
.catchError((e) => _handleErrorCase,
test: (e) => e is AuthorizationException)
.catchError((e) {
log.shout("Error occurred while fetching notifications $e");
_listNotifications.sink.addError("$e");
});
}
void _handleErrorCase(e) {
log.shout("Session invalid: $e");
_isLoggedIn.sink.add(false);
_listNotifications.sink.addError("Error");
}
}
This is what my repository code looks like:
notifications_repository.dart
import 'dart:async';
import 'package:logging/logging.dart';
import 'package:sunapsis/datasource/dataobjects/notification.dart';
import 'package:sunapsis/datasource/db/sunapsis_db_provider.dart';
import 'package:sunapsis/datasource/network/api_response.dart';
import 'package:sunapsis/datasource/network/sunapsis_api_provider.dart';
import 'package:sunapsis/utils/authorization_exception.dart';
/// Repository class which makes available all notifications related API functions
/// for server calls and database calls
class NotificationsRepository {
final Logger log = Logger('NotificationsRepository');
final SunapsisApiProvider apiProvider;
final SunapsisDbProvider dbProvider;
/// Optional [SunapsisApiProvider] and [SunapsisDbProvider] instances expected for unit testing
/// If instances are not provided - default case - a new instance is created
NotificationsRepository({SunapsisApiProvider api, SunapsisDbProvider db})
: apiProvider = api ?? SunapsisApiProvider(),
dbProvider = db ?? SunapsisDbProvider();
/// Returns a [Future] of [List] of [NotificationElement]
/// Tries to first look for notifications on the db
/// if notifications are found that list is returned
/// else a server call is made to fetch notifications
Future<List<NotificationElement>> getNotificationList([int currentTime]) {
return dbProvider.fetchNotifications().then(
(List<NotificationElement> notifications) {
if (notifications.length == 0) {
return getNotificationsListFromServer(currentTime);
}
return notifications;
}, onError: (_) {
return getNotificationsListFromServer(currentTime);
});
}
}
The function getNotificationsListFromServer is supposed to throw the AuthorizationException, which is supposed to be propagated through getNotificationList
This is the test case that is failing with the error mentioned before:
test('getNotification observable gets error on AuthorizationException',
() async {
when(mockNotificationsRepository.getNotificationList())
.thenThrow(AuthorizationException("test error", "test description"));
scheduleMicrotask(() => notificationBloc.fetchNotifications());
await expectLater(
notificationBloc.getNotificationList, emitsError("Error"));
});
And this is what the custom exception looks like:
authorization_exception.dart
class AuthorizationException implements Exception {
final String error;
final String description;
AuthorizationException(this.error, this.description);
String toString() {
var header = 'sunapsis Authorization error ($error)';
if (description != null) {
header = '$header: $description';
}
return '$header';
}
}
PS: When I tested my repository class and the function throwing the custom exception those tests were passed.
test('throws AuthorizationException on invalidSession()', () async {
when(mockSunapsisDbProvider.fetchNotifications())
.thenAnswer((_) => Future.error("Error"));
when(mockSunapsisDbProvider.getCachedLoginSession(1536333713))
.thenAnswer((_) => Future.value(authorization));
when(mockSunapsisApiProvider.getNotifications(authHeader))
.thenAnswer((_) => Future.value(ApiResponse.invalidSession()));
expect(notificationsRepository.getNotificationList(1536333713),
throwsA(TypeMatcher<AuthorizationException>()));
});
Above test passed and works as expected.
I am a new college grad working my first full time role and I might be doing something wrong. I will really appreciate any feedback or help, everything helps. Thanks for looking into this question.
You're using thenThrow to throw an exception, but because the mocked method returns a Future you should use thenAnswer.
The test would be like that:
test('getNotification observable gets error on AuthorizationException', () async {
// Using thenAnswer to throw an exception:
when(mockNotificationsRepository.getNotificationList())
.thenAnswer((_) async => throw AuthorizationException("test error", "test description"));
scheduleMicrotask(() => notificationBloc.fetchNotifications());
await expectLater(notificationBloc.getNotificationList, emitsError("Error"));
});
I think you are using the wrong TypeMatcher class. You need to use the one from the testing framework and not the one from the Flutter framework.
import 'package:flutter_test/flutter_test.dart';
import 'package:matcher/matcher.dart';
class AuthorizationException implements Exception {
const AuthorizationException();
}
Future<List<String>> getNotificationList(int id) async {
throw AuthorizationException();
}
void main() {
test('getNotification observable gets error on AuthorizationException',
() async {
expect(getNotificationList(1536333713),
throwsA(const TypeMatcher<AuthorizationException>()));
});
}
I'm trying to make a global click event directive. But document:click does not work for me.
import 'package:angular/angular.dart';
#Directive(
selector: '[clickOutside]'
)
class ClickOutsideDirective {
#HostListener('click', [r'$event.target'])
void onClick(targetElement){
print('Target:' + targetElement.toString());
}
}
When changing document:click to click I get expected behavior. But of course not globally. What am I doing wrong?
The document: and similar event scopes were removed in Dart.
Use instead
import 'dart:html';
class ClickOutsideDirective implements OnInit, OnDestroy {
StreamSubscription _docClickSub;
ngOnInit() {
_docClickSub = document.onClick.listen((event) {
print('Target:' + event.target.toString());
});
}
ngOnDestroy() {
_docClickSub?.cancel();
_docClickSub = null;
}
}