ember-power-select 自定义搜索操作和 'selected' 与外部数据

ember-power-select Custom Search Action and 'selected' with external data

概览

我在 Ember.js 3.8 项目中使用 ember-power-select - 一半有效,一半无效!

为了使问题更具可读性,我将所有代码放在了问题的底部。

情况

select 配置为从 API 端点获取数据,并为用户提供一组可能的选项以 select 来自。

涉及的路由 (routes/guest/new-using-ember-power-select.js) 会对模型 (models/guest.js) 进行 createRecord,然后,理想情况下,对两个表单元素 ( templates/guests/new-using-ember-power-select.js 和 templates/components/guest-form-ember-power-select.hbs) 反映回数据存储中的该记录。

问题

这适用于文本输入,但我无法使其适用于 ember-power-select.

在当前配置(如下所示)中,用户可能:

非常感谢有人指出我在这里做错了什么。我觉得这可能是一件小事,但我确实想到我必须通过组件中的属性来管理 select 的状态,并且只有在提交表单时才会更新底层数据存储.. .. 我不想那样做,但我很想知道这是否被认为是最好的主意。

谢谢


编辑 1: 我忘了说我试图改变 ember-power-[ 的 onchange 属性 =108=] 这样就不会像这样

onchange=(action "nationalityChangeAction")

...看起来像这样...

onchange=(action (mut item.nationality))

效果是:


型号

//app/models/guest.js
import DS from 'ember-data';
import { validator, buildValidations } from 'ember-cp-validations';

const Validations = buildValidations({
  name: [
    validator('presence', true),
  ],
  nationality: [
    validator('presence', true),
  ],
});



export default DS.Model.extend( Validations, {
  name: DS.attr('string'),
  nationality: DS.attr('string')
});

路线

//app/routes/guest/new-using-ember-power-select.js
import Route from '@ember/routing/route';

export default Route.extend({
  model() {
    return this.store.createRecord('guest', {
      name: "",
      nationality: ""
    });
  },
  actions: {
    updateNationality(slctnValue) {
      this.controller.model.set('nationality' , slctnValue);
    },
  }
});

模板

//app/templates/guests/new-using-ember-power-select.js
<h2>Guest: Add New</h2>
<div class="well well-sm">
  Demonstration of 'ember-power-select'
</div>
{{guest-form-ember-power-select
  item=model
  changeNationalityHandler="updateNationality"
  updateRecordHandler="updateRecord"
  cancelHandler="cancelAndExit"
}}
{{outlet}}

组件模板

//app/templates/components/guest-form-ember-power-select.hbs
<div class="form-vertical">
  {{!-- Guest Name --}}
  <div class="form-group">
    <label class="control-label">Name</label>
    <div class="">
      {{  input type="text"
          value=item.name
          class="form-control"
          placeholder="The name of the Guest"
          focus-out=(action (mut this.errMsgDspCntrl.nameError) true)
      }}
    </div>
    {{#if this.errMsgDspCntrl.nameError}}
      <div class="text-danger">
        {{v-get item 'name' 'message'}}
      </div>
    {{/if}}
  </div>
  <div class="form-group">
    <label class="control-label">Countries (using power-select)</label>
    <div class="">
      {{#power-select
        searchPlaceholder="Text to provide user info about what they can search on"
        search=(action "searchCountries")
        selected=item.nationality
        onchange=(action (mut item.nationality))
        as |countries|
      }}
        {{countries.name}}
      {{/power-select}}
    </div>
    {{#if this.errMsgDspCntrl.nationalityError}}
      <div class="text-danger">
        {{v-get item 'nationality' 'message'}}
      </div>
    {{/if}}
  </div>
  {{!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--}}
  {{!-- Buttons --}}
  <div class="form-group">
    <div class="">
      <button type="submit" class="btn btn-default" {{action "buttonSaveClicked" item}}>{{buttonLabel}}</button>
      <button type="button" class="btn btn-default" {{action "buttonCancelClicked" item}} >Cancel</button>
    </div>
  </div>
</div>
{{yield}}

组件

//app/components/guest-form-ember-power-select.js
import Component from '@ember/component';

export default Component.extend({
    actions:{
        searchCountries(term) {
          //Response to :
          //
          //https://restcountries.eu/rest/v2/name/z?fields=name;alpha2Code
          //
          //
          //looks like this
          //  [
          //    ...
          //    {"name":"New Zealand","alpha2Code":"NZ"}
          //    ...
          //  ]
          //

          let url = `https://restcountries.eu/rest/v2/name/${term}?fields=name;alpha2Code`
          let dbg = fetch(url)
            .then(function(response) {
              return response.json();
            });
          return dbg;
        },
        nationalityChangeAction(slctn){
            this.sendAction('changeNationalityHandler', slctn.alpha2Code);
        },
    }
});

selected 属性 必须是提供给 Ember Power Select 的选项中包含的元素。在您的场景中,您没有使用 options 属性 而是通过 search 操作设置选项,但这并没有太大的区别。

您的搜索操作 return 一组对象(例如 [{"name":"New Zealand","alpha2Code":"NZ"}])。 nationalityChangeActionselected 值设置为 alpha2Code 的值。因此 selected 不包含在选项中:

[{"name":"New Zealand","alpha2Code":"NZ"}].includes('NZ') // false

因此您的 Power Selects 结束的状态与此类似:

<PowerSelect
  @options={{array
    (hash foo="bar")
  }}
  @selected="bar"
/>

您正在做的事情的简化版本如下所示:

<PowerSelect
  @options={{array
    (hash foo="bar")
  }}
  @selected={{selected}}
  @onchange={{action (mut selected) value="foo"}}
/>

请查看 Ember Power Select documentation 了解使用 optionssearch 的区别:

When that's the case you can provide a search action instead of options (it's the only situation where the options are not mandatory) that will be invoked with the search term whenever the user types on the search box.

[...]

There is only three things to know about this action: - You should return a collection or a promise that resolves to a collection from this action. - You can provide both options and a search action. Those options will be the initial set of options, but as soon as the user performs a search, the results of that search will be displayed instead.

因此,如果您使用 options 或 return 从 search 操作收集,这对您的问题没有影响。这一切都归结为 selected 值不属于绑定到 options 或 return 由 search 操作编辑的集合的一部分。

这实际上是您的 UI 在使用 onchange=(action (mut item.nationality)) 时如预期工作的原因。在这种情况下,item.nationality 是由 search(例如 {"name":"New Zealand","alpha2Code":"NZ"})编辑的集合 return 中的选定对象,而不是 alpha2Code 属性.

我在回答中使用 angle bracket component invocation syntax。希望很好。在我看来,这样更容易阅读。

我将回答显示一些差异,这些差异与使 select 在您的存储库中工作所需的更改有关:https://github.com/shearichard/emberjs-select-addon-comparison

要理解的关键是 ember-power-select 收到一个块,在你的情况下

as |country|}}
  {{country.name}}
{{/power-select}}

将调用该块来呈现每个选项,还有 selected 选项。在这种情况下,选项是具有这种形状的国家/地区对象:{"name":"American Samoa","alpha2Code":"AS"}。这就是为什么你调用 {{country.name}} 来渲染它。但是,使用您的方法,您传入的 selected 值不是名称为 属性 的对象。实际上连对象都不是,而是美属萨摩亚的字符串"AS",所以可以输出字符串的名字属性.

在您的情况下,您存储的信息(国家代码)不足以在 select 的触发器中显示漂亮的 "American Samoa",并且由于您不了解国家/地区在进行搜索之前,您无法查看具有该国家/地区代码的国家/地区。

如果您没有编辑表单,我的建议是将整个 selected 国家存储在一个 属性 中,也就是您传递给 selected 的那个。

diff --git a/app/components/guest-form-ember-power-select.js b/app/components/guest-form-ember-power-select.js
index edf9390..2467d85 100644
--- a/app/components/guest-form-ember-power-select.js
+++ b/app/components/guest-form-ember-power-select.js
@@ -25,6 +25,8 @@ export default Component.extend({
   //messages
   nameOfErrMsgDspCntrl : 'errMsgDspCntrl',

+  nationality: undefined,
+
   actions:{

     searchCountries(term) {
@@ -73,7 +75,7 @@ export default Component.extend({
     },

     nationalityChangeAction(slctn){
-      //this.set(this.myValue, slctn);
+      this.set('nationality', slctn);
       this.sendAction('changeNationalityHandler', slctn.alpha2Code);
     },

diff --git a/app/templates/components/guest-form-ember-power-select.hbs b/app/templates/components/guest-form-ember-power-select.hbs
index 56f007d..5c69834 100644
--- a/app/templates/components/guest-form-ember-power-select.hbs
+++ b/app/templates/components/guest-form-ember-power-select.hbs
@@ -24,7 +24,7 @@
       {{#power-select
         searchPlaceholder="Text to provide user info about what they can search on"
         search=(action "searchCountries")
-        selected=item.nationality
+        selected=nationality
         onchange=(action "nationalityChangeAction")
         as |countries|
       }}
@@ -36,14 +36,14 @@

只要您不想编辑您以前创建的用户的国籍,甚至可能是几周前,这就有效。在这种情况下,您不会参考国家,只有国家代码。在那种情况下,如果您的 API 允许,我建议使 selected 成为一个计算 属性,returns 承诺使用用户的国家/地区代码解析国家/地区对象。似乎确实如此,所以最好的解决方案是

diff --git a/app/components/guest-form-ember-power-select.js b/app/components/guest-form-ember-power-select.js
index edf9390..f889734 100644
--- a/app/components/guest-form-ember-power-select.js
+++ b/app/components/guest-form-ember-power-select.js
@@ -1,4 +1,5 @@
 import Component from '@ember/component';
+import { computed } from '@ember/object';

 export default Component.extend({
   buttonLabel: 'Save',
@@ -25,6 +26,16 @@ export default Component.extend({
   //messages
   nameOfErrMsgDspCntrl : 'errMsgDspCntrl',

+  nationality: computed('item.nationality', function() {
+    let countryCode = this.get('item.nationality');
+    if (countryCode) {
+      return fetch(`https://restcountries.eu/rest/v2/alpha/${countryCode}?fields=name;alpha2Code`)
+        .then(function (response) {
+          return response.json();
+        });
+    }
+  }),
+

最后一个将获取您知道代码的国家/地区的信息。