【问题标题】:ember-power-select Custom Search Action and 'selected' with external dataember-power-select 自定义搜索操作并使用外部数据“选择”
【发布时间】:2019-11-07 23:35:28
【问题描述】:

概述

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

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

情况

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

所涉及的路线 (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。

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

  • 搜索要选择的选项;
  • 选择一个选项,然后;
  • 将该选择反映回数据存储中的guest 实例。 但是所做的选择并未反映在用户界面中 - 似乎没有做出任何选择

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

谢谢


编辑 1:我忘了说我试图改变 ember-power-select 的 onchange 属性,而不是看起来像这样

onchange=(action "nationalityChangeAction")

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

onchange=(action (mut item.nationality))

效果如下:

  • 所选值在表单中可见(正如您通常所期望的,但与我目前的努力不同)但是
  • 放入基础数据存储记录的值不是两个字符的国家/地区代码,而是返回 API 调用的数组实例,一个具有两个属性 {"name":"New Zealand","alpha2Code":"NZ"} 的对象。

型号

//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);
        },
    }
});

【问题讨论】:

    标签: javascript ember.js ember-power-select


    【解决方案1】:

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

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

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

    将调用该块来渲染每个选项,以及选定的选项。在这种情况下,选项是具有以下形状的国家对象:{"name":"American Samoa","alpha2Code":"AS"}。这就是你调用 {{country.name}} 来渲染它的原因。但是,使用您的方法,您传入的选定值不是具有 name 属性的对象。实际上甚至不是一个对象,而是美属萨摩亚的字符串“AS”,所以可以输出一个字符串的name属性。

    在您的情况下,您存储的信息(国家/地区代码)不足以在选择的触发器中显示一个漂亮的“美属萨摩亚”,而且由于您在进行搜索之前不知道这些国家/地区您无法使用该国家/地区代码查看国家/地区。

    如果您没有编辑表单,我的建议是将整个选定国家/地区存储在您传递给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 @@
    

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

    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();
    +        });
    +    }
    +  }),
    +
    

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

    【讨论】:

    • 非常感谢 Miguel 的帮助,非常感谢。
    【解决方案2】:

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

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

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

    所以你的权力选择结束的状态和这个类似:

    <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 之间的区别:

    在这种情况下,您可以提供搜索操作而不是选项(这是选项不是强制性的唯一情况),只要用户在搜索框上键入,搜索项就会被调用。

    [...]

    关于此操作,只需了解三件事: - 您应该返回一个集合或一个从该操作解析为集合的承诺。 - 您可以同时提供选项和搜索操作。这些选项将是初始选项集,但一旦用户执行搜索,就会显示该搜索的结果。

    因此,如果您使用options 或从search 操作返回集合,这对您的问题没有影响。这一切都归结为有一个selected 值,它不是绑定到options 或由search 操作返回的集合的一部分。

    这实际上是使用onchange=(action (mut item.nationality)) 时用户界面按预期工作的原因。在这种情况下,item.nationalitysearch(例如{"name":"New Zealand","alpha2Code":"NZ"})返回的集合中的选定对象,而不是alpha2Code 属性的值。

    我在回答中使用了angle bracket component invocation syntax。希望没问题。在我看来,它使阅读更容易。

    【讨论】:

    • 感谢您的回复,非常感谢。我想知道您是否可以稍微扩展您的答案?我从您所说的内容中意识到,有必要包含一个“选项”属性,并且这必须包含一个数组。我不太清楚数组应该包括什么。我认为如果我在“Z”上搜索并从结果列表中选择“新西兰”条目,那么“选定”属性的值不仅应该是“NZ”,而且......可能... 'options' 应该是一个单元素数组 `[{alpha2code:'NZ'}] ?谢谢。
    • 这不是optionssearch 的问题。在这种情况下,两者的工作方式相同。如果您传递 search 操作处理程序,则可以省略 options。在这种情况下,这些选项由 Ember Power Select 填充,并返回 search 操作。但在这两种情况下,selected 属性的值必须包含在选项中才能显示。这不是因为您使用对象数组作为选项(由search 返回)而是作为selected 的字符串。
    • 非常感谢您的评论,但我现在很困惑。我认为我们要说的是options 属性需要包含由搜索返回的相同对象的数组?在搜索完成之前,我不确定如何做到这一点。我只是想知道您能否通过编辑答案来表达您的建议,以便它使用与我使用相同的变量?
    • 试图扩展我的答案。不要被searchoptions 弄糊涂了。这对您的问题无关紧要。这一切都是为了拥有一组不包括所选项目的选项。
    • 谢谢。我今天没有机会看这个项目,但我会在周末这样做,我会在完成后更新问题。再次感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-06
    • 2013-12-21
    相关资源
    最近更新 更多