19

我有一个设置页面,用户可以在其中保存一些配置变量,例如用户名。用户还可以更改该页面上的用户名。但是当我转到另一个组件(页面)并返回时,不会保存用户名。

我还想在其他组件中显示用户名。我怎么做?使用全局变量?

结构: - App - app.ts(主) - setting.ts - someOtherComponent.ts

4

5 回答 5

38

您需要的是维护变量状态的服务。然后,您可以将该服务注入任何组件以设置或获取该变量。

这是一个示例(/services/my.service.ts):

import {Injectable} from "angular2/core";

@Injectable()
export class MyService {
    private myValue;

    constructor() {}

    setValue(val) {
        this.myValue = val;
    }

    getValue() {
        return this.myValue ;
    }
}

您可能希望将该服务放在应用程序引导函数的 providers 数组中(可能在您的主应用程序组件文件中,或者根据您喜欢的方式放在单独的文件中)。

在您的主应用程序文件 (/app.ts) 中:

import {MyService} from './services/my.service';
bootstrap(App, [MyService, COMMON_DIRECTIVES, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, HTTP_PROVIDERS]); // directives added here are available to all children

您不需要在数组中包含 COMMON_DIRECTIVES 和其他所有大写项,但通常包含这些项只是为了使您不必在编写的每个组件中配置它们。

然后您可以从这样的组件(/components/some-component.ts)中访问该服务:

import {MyService} from '../services/my.service';

@Component({
    selector: 'some-component',
    template: `
<div>MyValue: {{val}}</div> 
`
})
export class SomeComponent {
    constructor(private myService:MyService) {
    }

    get val() {
        return this.myService.getValue();
    }
}

您可能还想添加到服务中,以便将值保存到某个(半)永久位置,以便下次用户进入应用程序时可以访问它。

于 2016-01-03T03:26:12.583 回答
12

在您当前的情况下,这可能是一种矫枉过正的做法,但是如果您要将应用程序扩展为更大的应用程序,这可能会为您省去很多麻烦。也就是说,我相信 Angular 和类似 Redux 的 store 是一个非常强大的组合。我的建议是使用ngrx/store模块。

受 Redux 启发,RxJS 支持 Angular 应用程序的状态管理

来源:ngrx/storeGitHub 存储库

ngrx/store下面的代码应概述将其集成到应用程序中所需遵循的所有步骤。为简洁起见,我省略了您可能拥有的任何现有代码以及您需要添加的所有导入。

安装模块

npm install --save ngrx/core ngrx/store

创建一个减速器

export const SETTINGS_UPDATE = 'SETTINGS_UPDATE';

const initialState = {
    username: ''
};

export function settingsReducer(state: Object = initialState, action: Action) {
    switch (action.type) {
        case SETTINGS_UPDATE:
            return Object.assign({}, state, action.payload);
        default:
            return state;
    }
};

导入 StoreModule

@NgModule({
    imports: [
        // your other imports
        StoreModule.provideStore({
            settings: settingsReducer
        })
    ]
})
export class AppModule {
    //
}

为 Settings Reducer 创建接口

export interface SettingsStore {
    username: string;
}

创建商店界面

export interface AppStore {
    settings: SettingsStore;
}

创建设置服务

@Injectable()
export class SettingsService {

    settings$: Observable<SettingsStore>;

    constructor(private store: Store<AppStore>) {
        this.settings$ = this.store.select('settings');
    }

    public update(payload) {
        this.store.dispatch({ type: SETTINGS_UPDATE, payload });
    }

}

设置组件控制器

@Component({
    ...
})
export class SettingsComponent implements OnInit {

    settings$: Observable<SettingsStore>;

    constructor(private settingsService: SettingsService) {
        //
    }

    ngOnInit() {
        this.settings$ = this.settingsService.settings$;
    }

    public updateUsername() {
        this.settingsService.update({ username: 'newUsername' });
    }

}

设置组件模板

<p *ngIf="(settings$ | async)?.username">
    <strong>Username:</strong>
    <span>{{ (settings$ | async)?.username }}</span>
</p>

<button (click)="updateUsername()">Update Username</button>

通过上面概述的设置,您可以将其SettingsService注入任何组件并在其模板中显示该值。

同样,您也可以store通过 using 更新来自任何组件的值,this.settingsService.update({ ... });并且更改将反映在使用该值的所有位置 - 无论是通过async管道的组件还是通过.subscribe().

于 2017-06-06T12:48:28.170 回答
4

你需要一个服务,这样你就可以在任何你需要的地方注入它。:

@Injectable()
export class GlobalService{
  private value;

  constructor(){

  }

  public setValue(value){
    this.value = value;
  }

  public getValue(){
    return this.value;
  }
}

您可以在所有模块中提供此服务,但您有可能获得多个实例。因此,我们将使服务成为单例。

@NgModule({
  providers: [ /* DONT ADD THE SERVICE HERE */ ]
})
class GlobalModule {
  static forRoot() {
    return {
      ngModule: GlobalModule,
      providers: [ GlobalService ]
    }
  }
}

你应该forRoot只在你的AppModule

@NgModule({
  imports: [GlobalModule.forRoot()]
})
class AppModule {}
于 2017-06-06T08:12:49.557 回答
2

弗拉基米尔正确地提到了ngrx.

我会使用带有Subject/BehaviourSubjectObservabletoset以及get我需要的状态(值)的服务。

我们在这里讨论了通过使用 Angular 2 中的 Observable 和 Subject 来实现多个组件与服务之间的通信,您如何可以在多个不具有parent-childchild-parent关系的组件中共享值,并且无需ngrx将外部库作为存储 导入。

于 2017-06-07T05:11:31.580 回答
2

我有同样的问题,大部分组件都需要loggedInUser信息。例如:用户名、首选语言、用户的首选时区等。

因此,为此,一旦用户登录,我们可以执行以下操作:

如果所有信息都在 JWT 令牌或 /me RESTful Api 中,那么您可以在其中一项服务中解码令牌。

例如:user.service.ts

@Injectable()
export class UserService {

  public username = new BehaviorSubject<string>('');
  public preferredLanguage = new BehaviorSubject<string>('');
  public preferredTimezone = new BehaviorSubject<string>('');

  constructor(
    private router: Router,
    private jwtTokenService: JwtTokenService
  ) {
    let token: string = localStorage.getItem('token'); // handled for page hard refresh event
    if (token != null) {
      this.decode(token);
    }
  }

  private decode(token: string) {
    let jwt: any = this.jwtTokenService.decodeToken(token);
    this.username.next(jwt['sub']);
    this.preferredLanguage.next(jwt['preferredLanguage']);
    this.preferredTimezone.next(jwt['preferredTimezone']);
  }

  public setToken(token: any) {
    localStorage.setItem('auth_token', token);
    this.decode(token);
  }

}

其中包含三个公共 Behavior 主题,无论谁在听这个变量,如果它发生变化,都会得到它的值。请检查 rxjs 编程风格。

现在在需要这个变量的地方,只需从那个组件订阅。

例如:NavComponent 肯定需要用户名。因此代码如下所示:

export class NavComponent implements OnInit {

    public username: string;

    constructor(
        private userService: UserService,
        private router: Router) {
    }        

    ngOnInit() {
        this.userService.username.subscribe(data => {
          this.username = data;
        });        
    }
}

因此,反应式编程为处理依赖变量提供了更好的解决方案。

如果页面是硬刷新,上面也解决了这个问题,因为我将值存储在 localstorage 中并在构造函数中获取它。

请注意:假设登录的用户数据来自 JWT 令牌,如果数据来自 Restful Api,我们也可以使用相同的服务(UserHttpService)。例如:api/我

于 2017-06-11T17:50:18.367 回答