Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
677 views
in Technique[技术] by (71.8m points)

typescript - 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"
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...