2016-04-11 19 views
2

いくつかのコンポーネントで構成されたフォームを含むノックアウトビューがある場合、フォームを送信するときにコンポーネントオブザーバブルの値にアクセスする方法を教えてください。親ビューに親ビューモデルのフォームからコンポーネントに属するobservableにアクセスするにはどうすればよいですか?

function parent_vm() { 
    this.item = ko.observable(); 
} 

コンポーネントのfoo::親のviewmodelで

function component_vm(params) { 
    this.item = params.item || ko.observable(); 
} 

私はこのような何かをやってきた

<form> 
    <foo params="item: item"></foo> 
</form> 

これは場しのぎようです私に。より良い方法がありますか?

答えて

1

最初のノート...あなたが使用している

メカニズム(paramsを通じて何かを渡す)は、私の知る限りこれを行うには、メイン組み込みのメカニズムです。

最初にする必要があるのは、自分自身に質問することです。なぜこれが必要ですか。 実際ののケースではなく、抽象的なreproを表示しています。実際のシナリオでは、解決すべき設計上の問題がある可能性があります。デモの抽象的なシナリオの具体的なインスタンスを想像することはできません(つまり、コンポーネントビューモデルは親からのオブザーバブルを正確に再利用します)。カプセル化を破っていないことは確かですか?

典型的なものI doコンポーネントとその親との間のリンクは、親の(計算された)観測値がその子に基づいて異なると考えています。あなたはこのためko-postboxの使用を検討、または手動でそのようなパブ・サブを行うことができます:

function component_vm(params) { 
    this.item = ko.observable(); 
    if (!!params.itemChangedHandler) { 
    this.item.subscribe(params.itemChangedHandler); 
    } 
} 

function parent_vm() { 
    this.someHandler = function() { 
    alert('Parent knows something is up!'); 
    } 
} 
<form> 
    <foo params="itemChangedHandler: someHandler "></foo> 
</form> 

二ノートを...

I通常はコンポーネントのような自己すべき(私の以前の提案から、コンポーネント間通信のためのko-postboxのようなものを使用するために集めたかもしれない)。私のコンポーネントは、通常、ユーザーとそれに関連付けられた自分の作業単位のための独自の「タスク」を持っています。

それは私に聞こえるので、実際には、親が正しく動作するためにを必要としている子供は、「コンポーネント」という(少なくとも、あなたのコメントから、この答えに)、KnockoutJSもtemplate bindingをサポートしていることを忘れてはいけませんその場合は、代わりにテンプレートを使用してアプリをモジュール化し、コードをより再利用可能にすることをお勧めします。

// Mock ajax calls: 
 
var $ = { post: function(url, dto) { console.log(dto); } }; 
 

 
function Address() { 
 
    this.addressLine1 = ko.observable(); 
 
    this.addressLine2 = ko.observable(); 
 
    this.addressLine3 = ko.observable(); 
 
} 
 

 
function Person() { 
 
    this.firstName = ko.observable(); 
 
    this.lastName = ko.observable(); 
 
    this.mainAddress = new Address(); 
 
    this.secondaryAddress = new Address(); 
 
} 
 

 
function RootVm() { 
 
    var self = this; 
 
    self.person = new Person(); 
 
    self.submit = function() { 
 
    $.post("my_url", ko.toJSON(self.person)); 
 
    } 
 
} 
 

 
ko.applyBindings(new RootVm());
div { margin: 10px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script> 
 

 
<script type="text/html" id="addressTmpl"> 
 
    <input data-bind="textInput: addressLine1" placeholder="address line 1"><br> 
 
    <input data-bind="textInput: addressLine2" placeholder="address line 2"><br> 
 
    <input data-bind="textInput: addressLine3" placeholder="address line 3"> 
 
</script> 
 

 
<script type="text/html" id="personTmpl"> 
 
    Name: 
 
    <div> 
 
    <input data-bind="textInput: firstName" placeholder="firstname"> 
 
    <input data-bind="textInput: lastName" placeholder="surname"> 
 
    </div> 
 
    Main address: <div data-bind="template: { name: 'addressTmpl', data: mainAddress }"></div> 
 
    Secondary address: <div data-bind="template: { name: 'addressTmpl', data: secondaryAddress }"></div> 
 
</script> 
 

 
<div data-bind="template: { name: 'personTmpl', data: person }"></div> 
 
<button data-bind="click: submit">submit</button>


最後の注意...

あなた本当には、2つのビューモデルの間のハードリンクを持つようにしたい、と上記の2つのオプションが嫌いな場合は/それから、私は二@Andrew's answerでの提案とcreateViewModel factory functionを使用し、component Sを使用して主張。

詳細は実際の状況やアプリに固有のものですが、その機能には自分のアプリの手段で親にアクセスするか、もう1つの回答にはcontextForの提案を付けることができます。

+0

ページをインスタンス化し、すべてのコンポーネントが含まれているパターンがあります。ルートはページ全体、次にコンポーネントのリストです。通常は、複数のパネルコンポーネントがあり、各パネルには子コンポーネント(独自のコンポーネントが含まれている可能性があります)が含まれています。フォームを送信するには、より良い方法を考えることができない限り、各コンポーネントからすべてのフォーム値を集める必要があります。 –

+0

@MusicalShore私は理解しています。その場合、コンポーネントとテンプレートを混在させることを検討してください。私の更新された答えを見てください。いずれにしても、パラメタ以外のものを介して別個のコンポーネントのビューモデル間の緊密な結合を得ることは、(おそらく意図的に)困難です。なぜなら、コンポーネントはデカ - カップルであるためです。 – Jeroen

+0

理想的には、親が正しく動作するためにコンポーネントは必要ありません。ルートビューモデルは、テンプレートに記述されているすべての子コンポーネントをレンダリングするルート「ダッシュボード」コンポーネントをインスタンス化するだけです。私の場合は、コンポーネントに観測値をバインドするルートコンポーネントを囲むような隙間があります。 @アンドリューのソリューションはあまりにも緊密に結合されています。私はどのように私はテンプレートソリューションを実装するだろうか分からない、コンポーネントの複雑さを与えます。私はpostboxメソッドが気に入っていますが、私はそれぞれの観測対象ごとに別々のトピックを公開する必要がありますか?それは私の元のソリューションとほとんど同じように見えます。 –

2

あなたは直接ので、これに代えて、createViewModel工場機能を使って、親コンテキストにアクセスすることができます。

ko.components.register('childComponent', { 
viewModel: component_vm, 
template: ... 
}) 

は、ファクトリ関数を指定します。

ko.components.register('childComponent', { 
viewModel: { 
    createViewModel: function(params, componentInfo) {    
     return new component_vm(params, componentInfo); 
    }, 
template: ... 
}) 

そして、これは私たちにcomponentInfoへのアクセスを提供し、そこからコンポーネントが関連付けられている要素のバインディングコンテキストを取得できます。

function component_vm(params, componentInfo) { 

var parentContext = ko.contextFor(componentInfo.element).$parent; 

//anything available in the parent context is now available here 
} 

このアプローチを使用してチェーン内の任意のコンテキストにアクセスできます。これは、各子に渡されるパラメータに手動で何も追加することなく、ルートコンテキストに戻ることができます。

+0

これは興味深いアプローチですが、私は、親があまり結合していないコンポーネントフォームにパラメータを渡すと思います。あなたの方法は、テストすることをより困難にします。 –

関連する問題