2016-06-23 19 views
0

Knockoutでノードをクリアした後でもバインディングを適用できません。私は、ユーザーの操作(ボタンをクリックするなど)に基づいてコンポーネントを動的に使用しようとしています。Knockout.jsのノードをクリアした後でバインディングを再適用する方法は?

私はそれらを作成し、削除し、コンポーネントを処理するためのUIComponentクラスを作成しました:

(function (window) { 

     define(['knockout'], function (ko) { 

     /** 
     * Custom component to the given DOM node 
     * @type {{render: UIComponent.render}} 
     */ 
     var UIComponent = (function() { 

      return { 

       /** 
       * Render a component 
       * @param {object} component 
       * @param {object} element 
       */ 
       render: function (component, element, childComponents) { 

        var tagName = element.tagName && element.tagName.toLowerCase(); 

        if (ko.components.isRegistered(tagName)) { 
         ko.components.unregister(tagName); 
        } 

        if (undefined !== childComponents) { 

         childComponents.forEach(function (child) { 

          if (ko.components.isRegistered(child.tagName)) { 
           ko.components.unregister(child.tagName); 
          } 

          ko.components.register(child.tagName, child.component); 
         }); 
        } 

        ko.components.register(tagName, component); 
        ko.applyBindings(); 
       }, 

       /** 
       * Removes a component 
       * @param {object} component 
       * @param {object} element 
       */ 
       remove: function (component, element) { 

        ko.components.unregister(component.name); 
        ko.cleanNode(element); 

        // Remove any child elements from node 
        while (element.firstChild) { 
         element.removeChild(element.firstChild); 
        } 
       } 
      }; 

     })(); 

     window.UIComponent = UIComponent; 

     return UIComponent; 
    }); 
})(window); 

私は動的にコンポーネントのファイルをロードするRequireJSを使用していますので、私はのためのJavaScriptファイルを含むオブジェクトを持つコンポーネントを宣言ViewModelにテンプレート:

var QuoteForm = { 
     viewModel: { 
      require: '/scripts/components/sample-form.js' 
     }, 
     template: { 
      require: 'text!/templates/forms/available-products-quote-form.html' 
     } 
    }; 

そして、私はこのように、UIComponentクラスにこのコンポーネントオブジェクトを使用します。

UIComponent.render(QuoteForm, $('quote-form')[0]); 

これでサンプルform.js内部のViewModelファイル:

UIComponent.render(QuoteForm, $('quote-form')[0]); 

setTimeout(function() { 
    UIComponent.remove(QuoteForm, $('quote-form')[0]); 
}, 2000); 

setTimeout(function() { 
    UIComponent.render(QuoteForm, $('quote-form')[0]); 
}, 4000); 

最初:

define(['knockout'], function (ko) { 

    var SampleFormModel = function (params) { 

    }; 

    SampleFormModel.prototype = { 

     /** 
     * Dispose any resources used on component 
     */ 
     dispose: function() { 
      console.log('SampleFormModel disposed'); 
     } 
    }; 

    return SampleFormModel; 
}); 

が、私は、ユーザーのアクションをシミュレートするために、単純なsetTimeout機能を使用していますテストするにはsetTimeoutが実行され、SampleFormModel disposedというメッセージがコンソールに記録されていますが、2番目のメッセージがsetTimeoutで実行されると、ノックアウトからエラーが表示されます。

Uncaught Error: You cannot apply bindings multiple times to the same element.

ノックアウトがコンポーネントを廃棄しても、同じノードにバインディングを再度適用することはできません。

+0

引数なしで 'ko.applyBindings();'を呼び出すので、DOMツリー全体に適用されます。特定のノードのみをクリーニングしています。 ko.componentsがあなたのために世話をしてくれることを、あなたが手でやっていると確信しています。 'cleanNode'をまったく使う必要はありません。 –

+0

WOW!それはトリックでした!どうもありがとうございました! –

答えて

2

Roy Jと同じように、私は全体のDOMへのバインディングを適用した:あなたはこれを試してみました。私はただ単に特定の要素にコンポーネントを登録するUIComponentクラスにいくつかの変更を加えた:

(function (window) { 

    define(['knockout'], function (ko) { 

     /** 
     * Custom component to the given DOM node 
     * @type {{render: UIComponent.render}} 
     */ 
     var UIComponent = (function() { 

      return { 

       /** 
       * Render a component 
       * @param {object} component 
       * @param {object} element 
       */ 
       render: function (component, element, childComponents) { 

        var tagName = element.tagName && element.tagName.toLowerCase(); 

        if (undefined !== childComponents) { 

         childComponents.forEach(function (child) { 
          ko.components.register(child.tagName, child.component); 
         }); 
        } 

        ko.components.register(tagName, component); 
        ko.applyBindings(component, element); 
       }, 

       /** 
       * Removes a component 
       * @param {object} component 
       * @param {object} element 
       */ 
       remove: function (component, element) { 

        ko.components.unregister(component.name); 
        ko.cleanNode(element); 

        // Remove any child elements from node 
        while (element.firstChild) { 
         element.removeChild(element.firstChild); 
        } 
       } 
      }; 

     })(); 

     window.UIComponent = UIComponent; 

     return UIComponent; 
    }); 

})(window); 

今それが魅力のように働いています!

1

あなたが、あなたはすべてのあなたが望むどのノードからバインディングを削除します

ko.cleanNode(element) 

この機能を使って、要素をきれいにすることができ、同じ要素にバインディングを適用する前に、あなたの要素をきれいにする必要があるので、バインディングを再適用します。

+2

ええ、私は知っています。私はComponentクラスの 'remove'メソッドでこの関数を使用していますが、まだエラーメッセージが表示されています。 –

+0

申し訳ありませんが、私は気づいていませんでした。 –

+0

問題ありません。とにかく、ありがとう。 –

0

ノックアウトコンポーネントはあまりよく知られていませんが、ドキュメントを見てきました。

ここで、この1ちらほらがあります:あなたは(通常は望ましくない)同じのviewmodelオブジェクトのインスタンスを共有するためにあなたのコンポーネントのすべてのインスタンスをしたい場合は

...それはViewModelに指定する必要があります:{インスタンス:オブジェクト}だけでなく、viewModel:オブジェクトあなたのコードを見てみると

http://knockoutjs.com/documentation/component-registration.html#registering-components-as-a-viewmodeltemplate-pair

、あなたは基本的に(2回レンダリングすることによって)HTMLコンポーネントの複数のインスタンスを持っているが、あなたはのviewmodelを再利用しています。言っ

var QuoteForm = { 
    viewModel: { 
     instance: { require: '/scripts/components/sample-form.js' } 
    }, 
    template: { 
     require: 'text!/templates/forms/available-products-quote-form.html' 
    } 
}; 
+0

ありがとうございます。それは動作しません。 RequireJSは** sample-form.js **ファイルをロードしませんでした。ノックアウトのドキュメントは、AMDの統合に関する情報がかなり不足しています。 –

+0

代わりに、sample-form.jsでreturn {instance:new SampleFormModel()}を実行してみてください。 – Jonathan

+0

これはうまくいきません。 :( –

関連する問題