batilc
batilc

Reputation: 741

KnockoutJS - binding child component's click event in parent's markup

I am trying to write a reusable component library, and have come up with this issue which is bugging me for a time now.

I have overriden the template engine using the example that is provided. I think my problem is mentioned in his 'A quick note about rewriting templates' paragraph (I am not sure though, my problem might be different). But I don't have any idea how to implement it. Any help is appreciated. Here's the problem:

My template engine basically registers templates given as 'templateName' and 'markUp'. Firstly, I have a product model, which goes as:

 var Product = function(img, brand, description){
      this.img = img;
      this.brand = brand;
      this.description = description;
 };

Then, I have my parent view as:

{
    name: 'productlist-view',
    template:  '<productlist-view class="product-list">\
            <ul data-bind="foreach: {data: productlist, as: \'product\'}">\
                <product-box params="parent: $parent, productInfo: product" data-bind="click: getProduct"></product-box>\
            </ul>\
        </productlist-view>'
}

Now in the viewModel of this productlist-view view, productlist property is defined as an array of Product instances. The product-box component is supposed to create a viewModel and an associated template for each of these Products. The product-box component is registered to knockout using:

ko.components.register('product-box', {
    'viewModel': function(params){
          this.product = params.product;

          this.getProduct = function(){
               //Will perform an ajax call here
          };
     },
    'template': '<div class="product-box" data-bind="click: getProduct">\
                     <img class="product-img fl" data-bind="attr: {src: product.img}"/>\
                     <p data-bind="text: product.brand"></p>\
                     <p data-bind="text: product.description"></p>\
                 </div>'
})

I know, in the code above there are two bindings for the getProduct method. I will come to that. For now, just imagine the one in the productlist-view was not there.

The code above generates an html that goes like:

<productlist-view ....
    <ul .... 
        <product-box ....
             <div class="product-box" ....
                  <img .../>
                  <p ...  />
                  <p ...  />
             </div>
        </product-box>
        <product-box ..... goes on
    </ul>
</productlist-view>

In the above-code, the wrapper div in the product-box element is totally unnecessary since it just wraps the element. Moreover, the element is already wrapped within product-box element. So I want to remove the wrapper div. The problem here is that, I need the whole product visual to be clickable, but I cannot bind the click event to the getProduct method from the productlist-view template. When the foreach loop iterates in productlist-view template, the $data points to model of the product, not the viewModel (aka product-box).

How can I set this getProduct method, from the parent view?

Is there anyway to remove that unnecessary wrapper div in product-box?

In other words how can I have a clickable product-box component which goes like:

<productlist-view ....
    <ul .... 
        <product-box data-bind="click: getProduct"....
             <img .../>
             <p ...  />
             <p ...  />
        </product-box>
        ...
    </ul>
</productlist-view>

Upvotes: 3

Views: 1721

Answers (1)

Roy J
Roy J

Reputation: 43881

You can make a custom binding handler that would attach a click binding to the parent, but that strikes me as too clever by half (and violates encapsulation). If your click binding is associated with your component, it makes sense that the div be part of the component. Instead of using custom product-box tags, you can use a virtual tag and the component binding, so you don't have an extraneous wrapper.

Upvotes: 1

Related Questions