3

与えられたモデル:

MyModel = Backbone.Model.extend({
  defaults: {
    name: '',
    age: -1,
    height: '',
    description: ''
  }
});

およびモデルをリストとしてレンダリングするビュー:

MyView  = Backbone.View.extend({
  tagName: 'ul',
  className: 'MyView',

  render() {
    var values = {
      name: this.model.get('name'),
      age: this.model.get('age'),
      height: this.model.get('height'),
      description: this.model.get('description')
    }

    var myTemplate = $('#MyView-Template').html();
    var templateWithValues = _.template(myTemplate , values);
  }
});

ビューによってロードされたテンプレート:

<script type="text/template" id="MyView-Template">  
  <li class="name"><%= name %></li>
  <li class="age"><%= age %></li>
  <li class="name"><%= height%></li>
  <li class="name"><%= description%></li>
</script>

これは不自然な例ですが、実際のコードにはモデル内にさらに多くの属性があります。私が経験している問題は、モデルの更新を処理する方法です。

各フィールドに適切な入力要素を持つ HTML フォームを作成します。フォームはモデル化され、テンプレートとして読み込まれます。

<script type="text/template" id="MyEditView-Template">  
  <input type"text" value="<%= name %>" /> <br />
  <input type"text" value="<%= age%>" /> <br />
  <input type"text" value="<%= height%>" /> <br />
  <input type"text" value="<%= description%>" /> 
</script>

そしてビューにロードされます:

MyEditView  = Backbone.View.extend({
      tagName: 'form',
      className: 'MyEditView',

      render() {
        var values = {
          name: this.model.get('name'),
          age: this.model.get('age'),
          height: this.model.get('height'),
          description: this.model.get('description')
        }

        var myTemplate = $('#MyEditView-Template').html();
        var templateWithValues = _.template(myTemplate , values);
      }
    });

ユーザーがフォームを保存すると、新しい値がモデル (MyModel) に設定されます。ただし、元のビュー全体を再レンダリングしたくありません。時間がかかりすぎて、ネストされた要素が多数あります。モデルで値が変更された HTML 要素のみを更新したい。

問題は、モデルの属性を HTML 要素にエレガントにリンクする方法です。これにより、既にレンダリングされたビューに対して次の操作を実行できます。

  1. モデルの属性を反復処理します。
  2. 変更された属性を特定します。
  3. 変更された属性の UI のみを更新します。
  4. もはや表示されるべきではない以前にレンダリングされた属性の UI を非表示にします。

属性名をHTML要素文字列にマップするJavaScriptルックアップテーブル(単なるオブジェクト)のかなり醜いソリューションを手に入れた瞬間:

var AttributesMap = {
    name: {
        htmlRef: 'li.name',
        attributeName: 'name'
    },
    age: {
        htmlRef: 'li.age',
        attributeName: 'age'
    }
    ...
}

これはハッキリしているように感じられ、かなり肥大化したコードになってしまいました。

4

3 に答える 3

6

あなたの投稿には、実際には 2 つの質問が隠されています。モデルの属性に問題があり、モデル変更イベントをサブスクライブする方法がわかりません。幸いなことに、これらはどちらも backbone.js で簡単に実現できます。ビューコードを以下に変更します

1

render: function () {
    var model = this.model;
    $(this.el).empty().html(_.template(this.template, this.model.toJSON()))
    return this;
}

whereelはコンテナを定義するビューのプロパティです。toJSON()は、ネットワーク経由で転送できる形式でシリアル化するためにモデルで呼び出すことができるメソッドです。

2

ビューは、初期化関数でモデル変更イベントをサブスクライブするか、委任されたイベントサポートをより適切に使用する必要があります。モデル属性が変更されるたびに、ここchangeと以下の例のようにサブスクライブできるそのモデルでイベントが呼び出されます。

window.ListView = Backbone.View.extend({
    initialize: function () {
        //pass model:your_model when creating instance
        this.model.on("change:name", this.updateName, this);
        this.model.on("change:age", this.changedAge, this);
    },
    render: function () {
        var model = this.model;
        $(this.el).empty().html(_.template(this.template, this.model.toJSON()))
        return this;
    },
    updateName: function () {
        alert("Models name has been changed");
    },
    changedAge: function () {
        alert("Models age has been changed");
    }
});

JsBin の例

http://jsbin.com/exuvum/2

于 2012-05-07T05:31:03.770 に答える
1

テンプレートにデータがあるフィールドのみを表示したいという同様の問題に直面しました。<%= undefinedKey %> が例外をスローするため、アンダースコア テンプレート側から問題にアプローチしました。私にとっての解決策は、モデル データを含むテンプレートにラッパー オブジェクトを渡すことでした。モデル データのラッピングは次のようになります。

this.$el.html(this.template({my_data: this.model.toJSON()}));

テンプレートは、目的のプロパティの存在を確認します。

<% if(my_data.phone) { %><p><%= my_data.phone %> </p><% } %>

モデルが変更されるたびに、ビュー全体を自動的にレンダリングできます。このメソッドを使用すると、新しい値が表示され、削除された値が UI から消えます。

要件に関連するその他の情報:

モデルの属性を反復処理します。変更された属性を特定します。

モデルの「変更」イベントが最後に発生してから変更された属性を知りたい場合は、バックボーン モデルの changedAttributesメソッドを使用できます。

変更された属性の UI のみを更新します。もはや表示されるべきではない以前にレンダリングされた属性の UI を非表示にします。

発生した属性の変更ごとにビュー全体をレンダリングする代わりに、各 UI フィールドを個別のバックボーン ビューにすることで、属性が変更された UI の部分のみを外科的に更新できます。すべてのビューは、特定のモデル属性の変更イベントの共有モデルをリッスンします。

    this.model.on("change:phone", this.render, this);
于 2012-11-16T22:34:33.257 に答える
0

バックボーンはイベント委任を使用し、this.el コンテンツを置き換えた後に要素イベントを再サブスクライブする必要はありません。ただし、テンプレートを同期するたびに DOM サブツリーを破棄するため、フォームの状態が失われます。入力/変更イベントのモデルをサブスクライブしてみてください。したがって、ユーザーは入力要素に入力し、フォームを検証します。コントロールの状態 (たとえば、input[type=file]) を復元するのは困難で、リソースを大量に消費します。

最善の方法は、必要なターゲット要素のみを更新する DOM ベースのテンプレート エンジンを使用することだと思います。たとえば、私のものはhttps://github.com/dsheiko/ng-templateです

次のようなテンプレートを作成できます。

<form id="heroForm" novalidate>
  <div class="form-group">
    <label for="name">Name</label>
    <input id="name" type="text" class="form-control" required >
    <div class="alert alert-danger" data-ng-if="!name.valid">
      Name is required
    </div>
  </div>
  <div class="form-group">
    <label for="power">Hero Power</label>
    <select id="power" class="form-control"  required>
      <option data-ng-for="let p of powers" data-ng-text="p" >Nothing here</option>
    </select>
    <div class="alert alert-danger" data-ng-if="!power.valid">
      Power is required
    </div>
  </div>
   <button type="submit" class="btn btn-default" data-ng-prop="'disabled', !form.valid">Submit</button>
</form>

ここで、モデルnamepowerおよびをバインドしformます。それらの状態が変化するたびに (たとえば、ユーザーが入力している間)、テンプレートは反応します。エラーメッセージを非表示/表示したり、送信ボタンを無効/有効にしたりできます。

興味深い場合 - Backbone にバンドルする方法については、ここに少し無料のオンライン ブックがあります https://dsheiko.gitbooks.io/ng-backbone/

于 2016-08-27T14:24:14.783 に答える