Martin Devillers
Martin Devillers

Reputation: 18002

Inter-component communication with KnockoutJS

Say you have a product overview page with the following view models:

The widgets are loaded by using custom HTML elements (e.g. <product-list></product-list> and <product-registration></product-registration>). This is great because I don't have to put any knowledge of these widgets in my root-model. However, I would also like to refresh the product list after the user has registered a new product. In short:

How do I send a signal from ProductRegistrationViewModel to ProductListViewModel?

I've already looked into Knockout Postbox but this does not seem to solve the problem. What if I have multiple product lists and I only want to refresh one of them? Ideally, I would want to implement a series of public methods on my component's view model. Then tie the two together from my page's root view model, like this:

var ProductIndexViewModel = function() 
{
    var productRegistrationComponent = ??;
    var productListComponent = ??;

    productRegistrationComponent.onRegistrationComplete(function() {
      productListComponent.refresh();
    };
}

However, I don't have access to these view models from here. Or do I?

How can I access the child view-models from my root view model?

Finally, if anyone sees a better solution to my problem: I'm all ears!

Upvotes: 1

Views: 2082

Answers (2)

Chris Knoll
Chris Knoll

Reputation: 411

I've just recently done this sort of thing. In my approach, my component creates an API object that is assigned to an observable that is passed in by the parent component. For example, I have a datatable wrapped by a component which I would like to be able to find the selected rows:

pageModel = function() {
  this.dtAPI = ko.observable();
}

ko.applyBindigns(new pageModel());
<datatable params="api: dtAPI, data: getTableData()"></datatable>

Inside the component, you'd set the observable to the reference to the API for your component so that others can invoke calls to it. In my case, I'm not exactly doing this case, I expose an API to a databinding so that I can manipulate the databound element later (in this case it's a datatable binding:

if (binding.api != null)
{
    // expose datatable API to context's api binding.
    binding.api({
        getSelectedData: function() { return _getSelectedData(element);}
    });
}

Then in the main (parent) component, you can do things like:

dtAPI.getSelectedData();

So far, I've found this pattern as a way to expose behavior from an inner component useful.

Upvotes: 2

sroes
sroes

Reputation: 15053

It sounds like they should be sharing an observable array (the product list). I think it would be best to define this list in the root-model and pass it to the widgets that depend on it. If both components share the same observable, you don't have to worry about communicating between the two components. Your HTML could look something like this: <product-list data-bind="list: $root.allProducts"></product-list>.

How can I access the child view-models from my root view model?

If you want you could use ko.contextFor to get the context for a HTML element.

Upvotes: 3

Related Questions