Aurelia ValueConverter fromView混淆与多选

时间:2015-08-30 19:04:11

标签: javascript aurelia aurelia-binding

我试图在多选表单的上下文中使用和理解Aurelia ValueConverter。我认为直截了当,对我来说是一个挑战。

我有一个表单来创建一个新的交易,它通过多选输入字段分配了多个类别。我已将表单中的输出绑定到new_deal.categorizations(在数据库中,交易通过分类具有类别)。

现在通过“蛮力”来创造。方法,我在发布到API之前将每个类别ID转换为{category_id:id}对象。

仅记录POST输出的示例:

  create(){
      var categorizations = this.new_deal.categorizations;
      this.new_deal.categorizations = categorizations.map(function (e) {
          return {category_id: e}
      });
      logger.debug ('POST: ', JSON.stringify(this.new_deal));
  }

示例输出:

POST:  {"name":"new deal","categorizations":[{"category_id":"1"},{"category_id":"2"}]}

但我认为最好通过ValueConverter来实现。

Plunker here包含完整代码,但基本上是:

app.js:

export class App {
  constructor(){
    this.categories = [{id: 1, name: 'test1'}, {id: 2, name: 'test2'}];
    this.new_deal = {
        name:       'new deal',
        categorizations: null,
    };
  }

  create(){
      var categorizations = this.new_deal.categorizations;
      this.new_deal.categorizations = categorizations.map(function (e) {return {category_id: e}});
      logger.debug ('POST: ', JSON.stringify(this.new_deal));
  }

  create2(){
      logger.debug ('POST: ', JSON.stringify(this.new_deal));
  }

}

export class CategoryToIDValueConverter {
    fromView(id) {
        return id ? id: null;
    }
}

和app.html:

<template>
  <h1>Testing ValueConverter</h1>
   <h3 >New Brute Force Deal</h3>
     <form role="form">
       <label>Name</label>
          <input type="text" placeholder="Ex. Buy One Get One Free" value.bind="new_deal.name">
       <label>Categories</label>
          <select value.bind="new_deal.categorizations" multiple size="2">
              <option repeat.for="category of categories" value.bind="category.id">${category.name}</option>
          </select>
       </form>
       <button type="submit" click.delegate="create()">Save</button>

  <h3>New ValueConverter Deal</h3>
    <form role="form">
      <label>Name</label>
          <input type="text" placeholder="Ex. Buy One Get One Free" value.bind="new_deal.name">
      <label>Categories</label>
          <select class="form-control" value.bind="new_deal.categorizations | categoryToID" multiple size="2">
            <option repeat.for="category of categories" value.bind="category.id">${category.name}</option>
          </select>
    </form>
    <button class="btn btn-success" type="submit" click.delegate="create2()">Save</button>
</template>

有了这个,我得到了

的输出
POST:  {"name":"new deal","categorizations":["1","2"]}

在app.js中的fromView中,我认为我可以改变:

return id ? id: null;

要返回对象而不是单个值:

return id ? {category_id: id} : null

但是这导致了这个错误:

Uncaught Error: Only null or Array instances can be bound to a multi-select.

进一步检查后,看起来id作为一个数组来自View ...

所以我从View修改为:

    fromView(id) {
        if(id){
          var categorizations = [];
          id.forEach(function(cat_id){
            categorizations.push({category_id: cat_id})
          });
          logger.debug(categorizations);
          logger.debug(Object.prototype.toString.call(categorizations));
          return categorizations;
        } else { return null; }
    }
}

尝试期望一个数组,然后构建一个要返回的分类对象数组,但正如您在this Plunker中看到的那样,它会在您单击时丢失选择(尽管调试日志显示正在创建的对象)

2 个答案:

答案 0 :(得分:0)

您有一个类别对象数组,每个对象都有name(字符串)和id(数字)。这些将用于填充允许多选的选择元素:

export class App {
  categories = [
    { id: 1, name: 'test1'},
    { id: 2, name: 'test2'}
  ];
}
<select multiple size="2">
  <option repeat.for="category of categories">${category.name}</option>
</select>

交易对象由name(字符串)和categorizations组成。分类对象如下所示:{ category_id: 1 }

export class App {
  categories = [
    { id: 1, name: 'test1'},
    { id: 2, name: 'test2'}];

  deal = {
    name: 'new deal',
    categorizations: [],
  }
}

我们希望将select元素的值绑定到交易对象的分类,这是一个对象数组。这意味着每个select元素的选项都需要有一个对象&#34; value&#34;。 HTMLOptionElement的value属性只接受字符串。我们分配给它的任何东西都将被强制转换为字符串。我们可以将分类对象存储在可以处理任何类型的特殊model属性中。有关这方面的更多信息,请参阅aurelia docs

<select multiple size="2">
  <option repeat.for="category of categories" model.bind="{ category_id: category.id }">${category.name}</option>
</select>

最后,我们需要将select元素的值绑定到交易对象的分类:

<select value.bind="deal.categorizations" multiple size="2">
  <option repeat.for="category of categories" model.bind="{ category_id: category.id }">${category.name}</option>
</select>

总之,视图和视图模型看起来像这样:

export class App {
  categories = [
    { id: 1, name: 'test1'},
    { id: 2, name: 'test2'}];

  deal = {
    name: 'new deal',
    categorizations: [],
  }

  createDeal() {
    alert(JSON.stringify(this.deal, null, 2));
  }
}
<template>
  <form submit.delegate="createDeal()">
    <label>Name</label>
    <input type="text" placeholder="Ex. Buy One Get One Free" value.bind="deal.name">

    <label>Categories</label>
    <select value.bind="deal.categorizations" multiple size="2">
      <option repeat.for="category of categories" model.bind="{ category_id: category.id }">${category.name}</option>
    </select>

    <button type="submit">Save</button>
  </form>
</template>

这是一个工作的掠夺者:http://plnkr.co/edit/KO3iFBostdThrHUA0QHY?p=preview

答案 1 :(得分:0)

在Aurelia Gitter频道的whayes帮助下找出它。所以我在正确的轨道上期望在fromView方法中有一个数组,但我还需要在ValueConverter中使用toView方法。

export class CategoryToIDValueConverter {
    toView(cats){
      if (cats){
        var ids = [];
        cats.forEach(function(categorization){
          ids.push(categorization.category_id);
        });
        return ids;
      } else { return null; }
    }
    fromView(id) {
        if(id){
          var categorizations = [];
          id.forEach(function(cat_id){
            categorizations.push({category_id: cat_id})
          });
          return categorizations;
        } else { return null; }
    }
}

我也试过了,但我最初假设我需要将转换器添加到表单的选择行和选项行中,如下所示:

<select class="form-control" value.bind="new_deal.categorizations | categoryToID" multiple size="2">
            <option repeat.for="category of categories" value.bind="category.id">${category.name}</option>
          </select>

但那确实不对。我只需要将categoryToID ValueConverter应用于选择行,它们都按预期工作。

工作Plunker显示蛮力方法在您单击保存之前不会更改模型,并且ValueConverter会在您更改选择时更改它。

最终的app.js

import {LogManager} from 'aurelia-framework';
let logger = LogManager.getLogger('testItems');

export class App {
  constructor(){
    this.categories = [{id: 1, name: 'test1'}, {id: 2, name: 'test2'}];
    this.new_deal = {
        name:       'new deal',
        categorizations: [],
    };
    setInterval(() => this.debug = JSON.stringify(this.new_deal, null, 2), 100);
  }

  create(){
      var categorizations = this.new_deal.categorizations;
      this.new_deal.categorizations = categorizations.map(function (e) {return {category_id: e}});
      alert(JSON.stringify(this.new_deal, null, 2));
  }

  create2(){
    alert(JSON.stringify(this.new_deal, null, 2));
  }
}

export class CategoryToIDValueConverter {
    toView(cats){
      if (cats){
        var ids = [];
        cats.forEach(function(categorization){
          ids.push(categorization.category_id);
        });
        return ids;
      } else { return null; }
    }
    fromView(id) {
        if(id){
          var categorizations = [];
          id.forEach(function(cat_id){
            categorizations.push({category_id: cat_id})
          });
          return categorizations;
        } else { return null; }
    }
}

最终app.html

<template>
  <h1>Testing ValueConverter</h1>
   <h3 >New Brute Force Deal</h3>
     <form role="form">
       <label>Name</label>
          <input type="text" placeholder="Ex. Buy One Get One Free" value.bind="new_deal.name">
       <label>Categories</label>
          <select value.bind="new_deal.categorizations" multiple size="2">
              <option repeat.for="category of categories" value.bind="category.id">${category.name}</option>
          </select>
       </form>
       <button type="submit" click.delegate="create()">Save</button>

  <h3>New ValueConverter Deal</h3>
    <form role="form">
      <label>Name</label>
          <input type="text" placeholder="Ex. Buy One Get One Free" value.bind="new_deal.name">
      <label>Categories</label>
          <select class="form-control" value.bind="new_deal.categorizations | categoryToID" multiple size="2">
            <option repeat.for="category of categories" value.bind="category.id">${category.name}</option>
          </select>
    </form>
    <button type="submit" click.delegate="create2()">Save</button>

  <!-- debug -->
  <pre><code>${debug}</code></pre>
</template>