65.9K
CodeProject 正在变化。 阅读更多。
Home

Angular 2+ 的持久化用户设置

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (5投票s)

2017年9月6日

MIT

4分钟阅读

viewsIcon

16997

downloadIcon

158

介绍如何在 Angular 2+ 前端应用程序中处理持久化用户设置。

引言

Angular 2 是一个相对较新的框架,目前可用的在线帮助和代码片段较少。因此,共享的代码部分/模块对于帮助其他人处理这个出色的框架可能很有价值。在本文中,我介绍了一个用于在 Angular 2 应用程序中处理持久化用户设置的小组件和服务。设置服务包含处理设置的核心功能:存储、保存和检索设置,而设置组件保存并管理显示设置的视图,与用户交互并将用户输入传输到设置服务。要使用本教程,您需要知道如何创建 Angular 2+ 应用程序以及如何创建和连接组件和服务。

设置数据

我们设置项目的起点是定义用户设置的数据结构。用户设置通常是单选题的答案(通过单选按钮显示)。在我们的应用程序中,用户可以选择以英语或德语显示站点 {'language': ['English', 'German']},或者以摄氏度或华氏度显示温度。注意:多选题也是用户设置的可能类型(通过复选标记显示)。这些可以作为单选题设置的扩展来处理。为了简单起见,在本文中,我们仅处理单选题设置。

单选题设置最容易用 Javascript 对象字面量表示,其中键是设置的类型,值是该键的可能值的数组。生成的对象字面量包含用户可用的所有选项。该组件将通过访问器函数(getSettingsValues())使用这些 settingsValues 来填充视图;并且服务将使用设置对象字面量来检查允许值。服务和组件完全依赖于这个对象字面量,因此更改或添加设置不需要对代码进行任何其他更改,也不会破坏任何现有代码。

  private static settingsValues: {} = {
    'LANGUAGE': ['EN', 'DE', 'HU', 'RO'],
    'FORECAST_PERIOD': ['FULL_DAY', 'DAYTIME'],
    'WIND_SPEED': ['KNOTS', 'KMH', 'MPH', 'MS'],
    'WIND_DIRECTION': ['ARROW', 'CARDINAL', 'DEGREE'],
    'TEMPERATURE': ['C', 'F'],
    'PRESSURE': ['HPA', 'MB', 'INCHES'],
    'PRECIPITATION': ['MM3H', 'IN3H'],
    'ALTITUDE': ['M', 'FT'],
    'LAPSE_RATE': ['C100M', 'F1000FT']
  };
  
  public getSettingsValues() {
    return SettingsService.SettingsValues;
  }

填充视图

在 settings-component 中,我们存储键和键值对在两个对象中以构建视图。我们还将用户选择的当前设置存储在第三个对象中。需要 currentSettings 以在视图中显示当前值(通过 ngModel)。

  private settingsKeys = Object.keys(this.settingsService.getSettingsValues());
  private settingsValues = this.settingsService.getSettingsValues();
  private currentSettings = {};

初始化组件时,我们用从 settings-service 检索到的当前用户设置的 {key: value} 对填充 currentSettings

  initSettings() {
    for (const key of this.settingsKeys) {
      this.currentSettings[key] = this.settingService.getSetting(key);
    }
  }
  
  ngOnInit() {
    this.initSettings();
  }

视图是根据 settingsKeys 和 settingsValues 生成的。我们有一个外部 ngFor 循环来在单选组中显示 settingsKeys。注意:translate pipe 用于将键翻译成用户可读的选定语言文本。ngx-translate 用于翻译,但本文中未详细介绍。有一个内部 ngFor 循环,用于将可用值显示为每个设置键的单选按钮。

<form role="form">
    <div class="form-group">
	
      <div *ngFor="let key of settingsKeys">
        <label>{{key|translate}}</label><br>	  
        <label *ngFor="let value of settingsValues[key]"
                class="radio-inline">
          <input type="radio"
                (click)="saveSetting(key, value)"
                [(ngModel)]="currentSettings[key]"
                name="{{key}}"
                value="{{value}}">
                {{value|translate}}
        </label>
      </div>

    </div>
</form>

当用户单击单选按钮值时,它会触发函数 saveSetting(key, value),并将键和选定值作为参数传递。此函数反过来调用 settings-service 的 setSetting() 函数

  saveSetting(key: string, value: string) {
    this.settingService.setSetting(key, value);
  }

连接服务

现在我们已经设置了视图,我们需要做的就是实现 settings-service 中的 getter 和 setter 函数。有三个公共函数:getSettingsValues()getSetting()setSetting()。为了实现这些函数,我们有许多不同的选项可用:我们可以将设置存储在本地对象中(非持久化),或者将它们持久化到 localStorage 或服务器。在本文中,我们选择使用 localStorage 在会话之间持久化设置。如果 localStorage 不可用,我们可以退回到将设置存储在 JSON 对象中,以便至少在会话中具有持久性。首先,我们实现将设置存储在对象中的代码

  private settingsObject = {}; // store settings of a session if localStorage is not available

  private initSettingsObject() {
    // create settings object to store settings for session if localStorage is not available
    // settingsObject serves also as a fallback before the user sets any personal settings
    for (const key of Object.keys(SettingsService.forecastSettingsValues)) {
      this.settingsObject[key] = SettingsService.forecastSettingsValues[key][0];
    }    
  }

  constructor() {
    this.initSettingsObject();
  }

我们终于到达了 settings-service 的公共函数:getSetting()setSetting()。首先,我们检查设置是否有效(getSetting() 的键和 setSetting() 的 {key: value} 对),然后从本地存储中检索设置或将其保存到本地存储或 settingsObject(如果本地存储或本地存储中的设置不可用)。

  public getSetting(key: string): string {
    if (!this.isValidKey(key)) {
      console.log('settings-service.getForecastSetting() - key is invalid: ' + key);
      return '';
    }
    
    if ((typeof(Storage) !== 'undefined') && localStorage.getItem(key)) {
      return localStorage.getItem(key);
    } else {
      return this.settingsObject[key];
    }
  }

  public setSetting(key: string, value: string) {
    
    if (!this.isValidSetting(key, value)) {
      console.log('settings-service: {' + key + ': ' + value + '} setting was incorrect');
      return;
    }

    if ((typeof(Storage) !== 'undefined')) {
      localStorage.setItem(key, value);
    } else {
      this.settingsObject[key] = value;
    }
  }

settings-service 中还有两个辅助函数来检查键或 {key: value} 是否有效:isValidKey()isValidSetting()

  public isValidKey(key: string) {
    if (SettingsService.forecastSettingsValues.hasOwnProperty(key)) {
          return true;
        } else {
          return false;
        }    
  }

  public isValidSetting(key: string, value: string) {
    if (SettingsService.forecastSettingsValues.hasOwnProperty(key) &&
        SettingsService.forecastSettingsValues[key].includes(value)) {
          return true;
        } else {
          return false;
        }
  }

结论

代码是健壮的,因为更改设置不需要对代码或 html 进行任何更改。但是,代码中存在一个设计缺陷:当在应用程序的其他部分查询设置时,调用 settingsService.getSetting(),代码必须知道键的字符串字面量(例如 'TEMPERATURE')。因此,如果更改了键,则使用此键的代码的其他部分将会中断。

© . All rights reserved.