如果使用SelectionOptions,如何使用material-dropdown-select进行更改检测

时间:2017-09-09 07:53:52

标签: angular dart material-design angular-material2

我有一个material-dropdown-select的以下实现,它运行得很漂亮。

<material-dropdown-select [buttonText]="organizer?.name">
  <material-select-item *ngFor="let sailingClub of sailingClubs"
    {{sailingClub.name}}
  </material-select-item>
</material-dropdown-select>

我的数据来自这个属性:

Iterable<SailingClub> get sailingClubs => _store.state.sailingClubs.values;

关于它的好处是,一旦后端(firebase)将新项目添加到列表中,项目就会自动更新。

现在我想将其更改为使用可搜索的下拉列表。 为此,我必须将数据转换为SelectionOptions,并且我有问题。一切正常,但我不再进行变化检测。 这并不奇怪,因为我现在必须创建一个静态的SelectionOptions实例,这是我向视图组件提交的唯一内容。

<material-dropdown-select [buttonText]="organizer?.name"
  [options]="filteredSailingClubs"
  [itemRenderer]="displayNameRenderer">
  <div header>
    <material-select-searchbox
      label="Search..."
      [filterable]="filteredSailingClubs">
    </material-select-searchbox>
  </div>
</material-dropdown-select>

这就是我创建filteredSailingClubs

的方法
filteredSailingClubs = new StringSelectionOptions(_store.state.sailingClubs.values, toFilterableString: displayNameRenderer);

并访问名称属性

ItemRenderer<SailingClub> displayNameRenderer = (SailingClub item) => item.name;

3 个答案:

答案 0 :(得分:1)

您是否正在收听Firebase数据库进行更改?

我不确定_store或其任何属性到底是什么,但是从Firebase DatabaseRef,您可以监听更改并更新您的选择选项。然后,更改检测应检测到选择选项已更改。

sailingClubsRef.onValue((value) {
  filteredSailingClubs = new StringSelectionOptions(
    value.snapshot.val(),
    toFilterableString: displayNameRenderer,
  );  
});

答案 1 :(得分:1)

这是我当前的方法。将其作为更好的可读性的答案。

首先,我必须为计算值添加新变量,并手动跟踪变化。

StringSelectionOptions<SailingClub> filteredSailingClubs;
SelectionModel<SailingClub> singleSelectModel;
Map<String, SailingClub> oldSailingClubs;
SailingClub oldOrganizer;

然后在我的组件的构造函数中,我可以创建filteredSailingClubs。但是这个StringSelection将是空的,因为那时所有选项都没有从firebase加载。

filteredSailingClubs = new StringSelectionOptions(_store.state.sailingClubs.values, toFilterableString: displayNameRenderer);

并且最终能够更新filteredSailingClubs我必须添加它。

void ngDoCheck() {
  if (oldSailingClubs != _store.state.sailingClubs) {
    oldSailingClubs = _store.state.sailingClubs;
    filteredSailingClubs =
        new StringSelectionOptions(_store.state.sailingClubs.values, toFilterableString: displayNameRenderer);
  }
  if (oldOrganizer != organizer) {
    oldOrganizer = organizer;
    singleSelectModel = new SelectionModel<SailingClub>.withList(selectedValues: [organizer]);
    singleSelectModel.selectionChanges.listen(update);
  }
}

这是另外一个回调,它允许我在gui

中更改选择时更新值
void update(List<SelectionChangeRecord> record) {
  if (record.isNotEmpty && record.first.added.isNotEmpty) {
    onOrganizerChanged(record.first.added.first);
  }
}

不幸的是,这是很多锅炉板代码只是为了使列表可搜索。在所有变更检测由Angular处理并完全隐藏之前。所以我想知道我做错了。

我也不想改变商店的结构。它基本上是一个redux模式存储(使用greencat)并且是不可变的。 我也不希望我的商店知道关于gui的任何信息,例如选择模型等。

我的下一步是从材料下拉选择本身,看看我是否可以隐藏那里的复杂性。

答案 2 :(得分:0)

好的,这是我的最终解决方案。它在我看来并不是很好,因为它仍然需要相当多的代码,但至少它的效果非常好。 代码量的主要原因是map.values列表本身不稳定。因此map.values与旧值的每个新比较都将返回true。即使地图没有变化。因此我们必须保存原始地图。

StringSelectionOptions<SailingClub> _filteredSailingClubs;
Map<String, SailingClub> _oldSailingClubs;
SelectionModel<SailingClub> _singleSelectModel;
SailingClub _oldOrganizer;
StreamSubscription _selectionListener;

StringSelectionOptions<SailingClub> get filteredSailingClubs {
  // We have to save the old state since the Iterable itself is unstable and would change all the time
  if (_oldSailingClubs != _store.state.sailingClubs) {
    _oldSailingClubs = _store.state.sailingClubs;
    _filteredSailingClubs =
      new StringSelectionOptions(_store.state.sailingClubs.values, toFilterableString: displayNameRenderer);
  }
  return _filteredSailingClubs;
}

SelectionModel<SailingClub> get singleSelectModel {
  // We have to update the selection model on organizer change
  if (_singleSelectModel == null || _oldOrganizer != organizer) {
    _oldOrganizer = organizer;
    _singleSelectModel = new SelectionModel<SailingClub>.withList(selectedValues: [organizer]);
    _selectionListener?.cancel();
    _selectionListener = _singleSelectModel.selectionChanges.listen(update);
  }
  return _singleSelectModel;
}