Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatic ordering of exports in index.ts makes app crash

Every time I generate something in my shared folder the index.ts file is rebuilt and exports are put in alphabetical order. This seems to break dependencies for me. Changing order manually so that dependencies is exported before classes with the dependencies makes it work again.

If we have app/shared/auth.guard.ts:

import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';

import { AuthService, User } from './';

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private accountService: AuthService, private router: Router) { }

    canActivate(next: ActivatedRouteSnapshot): Observable<boolean> {
        let result = this.accountService.currentUser.first().map(user => user != null);

        let route: any[] = ['/login'];

        if (next.url.length) {
            route.push({ redirectUrl: next.url });
        }

        result.subscribe(isLoggedIn => {
            if (!isLoggedIn) {
                this.router.navigate(route);
            }
        });

        return result;
    }
}

and app/shared/account.service.ts:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

import { User } from './';

const LOCAL_STORAGE_KEY = 'currentUser';

@Injectable()
export class AuthService {
  private currentUserSubject: BehaviorSubject<User>;

  constructor() {
    this.currentUserSubject = new BehaviorSubject<User>(this.getUserFromLocalStorage())
    this.currentUserSubject.subscribe(user => this.setUserToLocalStorage(user));
  }

  logIn(userName: string, password: string) : Observable<User> {
    this.currentUserSubject.next({
      id: userName,
      userName: userName,
      email: userName
    });

    return this.currentUser.first();
  }

  logOut() {
    this.currentUserSubject.next(null);
  }

  get currentUser(): Observable<User> {
    return this.currentUserSubject.asObservable();
  }

  private getUserFromLocalStorage(): User {
    let userString = localStorage.getItem(LOCAL_STORAGE_KEY);

    if (!userString) {
      return null;
    }

    return JSON.parse(userString);
  }

  private setUserToLocalStorage(user: User) {
    if (user) {
      localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(user));
    }
    else {
      localStorage.removeItem(LOCAL_STORAGE_KEY);
    }
  }

}

This doesn't work:

export * from './auth.guard';
export * from './auth.service';

Unhandled Promise rejection: Error: Cannot resolve all parameters for 'AuthGuard'(undefined, Router). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'AuthGuard' is decorated with Injectable.

This works:

export * from './auth.service';
export * from './auth.guard';

From what I noticed this doesn't apply to all. For example can my user model be exported after auth service and it works fine.

I wish I didn't have to change this manually every time. Is there a workaround available? Could I structure the files in a different way?

Dependencies from package.json:

"@angular/common": "^2.0.0-rc.2",
"@angular/compiler": "^2.0.0-rc.2",
"@angular/core": "^2.0.0-rc.2",
"@angular/forms": "^0.1.0",
"@angular/http": "^2.0.0-rc.2",
"@angular/platform-browser": "^2.0.0-rc.2",
"@angular/platform-browser-dynamic": "^2.0.0-rc.2",
"@angular/router": "^3.0.0-alpha.7",
"bootstrap": "^3.3.6",
"es6-shim": "0.35.1",
"moment": "^2.13.0",
"ng2-bootstrap": "^1.0.17",
"reflect-metadata": "0.1.3",
"rxjs": "5.0.0-beta.6",
"slideout": "^0.1.12",
"systemjs": "0.19.26",
"zone.js": "0.6.12"

devDependencies:

"angular-cli": "1.0.0-beta.6"
like image 351
mollwe Avatar asked Jun 18 '16 23:06

mollwe


2 Answers

This is an issue with ordering of exports in barrels. It has been reported in the angular repo here: https://github.com/angular/angular/issues/9334

There are three workarounds:

Change the ordering of exports in your barrels

Change the ordering so that module dependencies are listed before their dependants.

In this example, AuthGuard depends on AuthService. AuthService is a dependency of AuthGuard. Therefore, export AuthService before AuthGuard.

export * from './auth.service';
export * from './auth.guard';

Don't use barrels at all.

This is not recommended since it means more imports are required.

In this example, you would import AuthService from its file rather than a barrel.

import { AuthService } from './auth.service';
import { User } from './';

Use the systemJS module format rather than commonJS

Change the typescript compiler options to compile to the SystemJS format rather than commonJS. This is done by changing tsconfig.json's compilerOptions.module from commonjs to system.

Note that when you change that configuration, you'll need to update the moduleId properties of all your component decorators from module.id to __moduleName and declare it in typings.d.ts as follows:

declare var __moduleName: string;

This module format is not the default in the Angular-CLI tool (official build tool created by the Angular team) and so may not be recommended or supported.


Note: I'm personally not satisfied with any of the workarounds.

like image 199
Michael Avatar answered Nov 16 '22 00:11

Michael


I had this issue and this was caused by circular reference. Explanations : within the 'provider service class' I had a reference to a UI page that the constructor referenced this same service causing the circular reference...

import { MyUIPage } from "../pages/example/myuipage";

So what I had to do, is remove the reference from the service and built a function receiving a callback. No need to reference the UI page from the service and error goes away.

public setCallBackSpy(callback)
{
   this.callBackSpy = callback;
}

In the app.component class constructor, the one referencing the service, I simply set the link to the callback function like below.

this.servicingClass.setCallBackSpy(this.myCallBackFunctionUsingUIPage);

Hope that helps, my first answer ever :)

like image 26
Floydus Avatar answered Nov 16 '22 01:11

Floydus