2

I have created custom functionality on item form on cart page like increment/decrement qty, remove product with a custom popup, custom move to wishlist by knockout js.

For knockout events, I created custom js and initialize it on form.phtml

<div data-bind="scope: 'remove-product'" id="cart-item-table">
    <form action="<?= /* @escapeNotVerified */ $block->getUrl('checkout/cart/updatePost') ?>"
              method="post"
              id="form-validate"
              data-mage-init='{"validation":{}}'
              class="form form-cart">

              ....
              ....
    </form>
</div>

<script type="text/x-magento-init">
    {
       "*": {
            "Magento_Ui/js/core/app": {
                "components": {
                    "remove-product": {
                        "component": "Vendor_Module/js/removeproduct"
                    }
                }
            }
        }
    }
</script>

enter image description here

Now I have a third-party coupon code module which adds free gift product by applying the coupon code on cart page. For that, I just re-render item form layout on ajax response.

Everything is working fine except knockout events on item form. All knockout events stop working after re-render item form.

After some R&D, I found that we need to rebind knockout events after ajax response.

ko.applyBindings(removeproduct, $('#cart-item-table'));

It's showing error like parameter should ViewModel and element. I already defined it.

Any help would be appreciated...!

2
  • Same issue i face before few days. This link is helpful for me. Hope, you can get solution from this answer :) Commented Jan 22, 2019 at 7:01
  • I also have similar implementation in my project. But when I use applyBindings, it create extra rows in my lists. For eg: if I have 5 rows it shows 20 after applyBindings. Any clue what can be the issue? Commented Sep 30, 2021 at 8:54

2 Answers 2

8

We need to re-bind the js component by knockout applyBindings function on the ajax response.

ko.applyBindings(new removeproduct(), $('#cart-item-table')[0]);

While removeproduct is viewModel(component) Vendor_Module/js/removeproduct and cart-item-table is the element which I need to re-bind knockout events.

EDIT

If you have same component with each element, you can use the below code. For example, you have multiple product div in response and each product div has knockout binding

if ($.fn.applyBindings != undefined) {
    setTimeout(function () {
        $('#recommend-products-cat').find('.product-item').trigger('contentUpdated');
        $('#recommend-products-cat').find('.product-item').applyBindings();
    }, 1000);
}

where #recommend-products-cat is main div. .product-item is child products divs.

4
  • 1
    yes you are right Commented Jan 22, 2019 at 9:02
  • yes bhai!! hahaahaa Commented Jan 22, 2019 at 9:09
  • <script> require([ 'jquery', 'ko', 'Vendor_Modul/js/removeproduct' ], function ($, ko, rp) { ko.applyBindings(new rp(), $("#cart-item-table")[0]); }); </script> is it right approach ?
    – 26vivek
    Commented Oct 1, 2020 at 10:33
  • I also have similar implementation in my project. But when I use applyBindings, it create extra rows in my lists. For eg: if I have 5 rows it shows 20 after applyBindings. Any clue what can be the issue? Commented Sep 30, 2021 at 8:54
2

It's better way to use data-mage-init into specific div instead of using <script type="text/x-magento-init">

x-magento-init will be call when module will be initialized. So, when module will be initialized at that time, x-magento-init script will be call. So, it will be not used for ajax response.

data-mage-init will be working same as like x-magento-init except it will call when that particular div will be load.

Example :

<div data-mage-init='{"Pulsestorm_JavascriptInitTutorial/example": {"another":"example"}}'>A single div</div>

Here, You can see on above code that when above div will be load at that time, js file will be execute. It's better way as per my view.


EDIT :

<div id="ko-example" data-bind="scope:'ko-example'">
    <!-- ko if: isVisible-->
    <div class="rh-module" data-mage-init='{"Rh_Module/js/kotemplate": {"another":"example"}}'>
        <div >A single div</div>
        <div id="rh-buttons" class="actions">
            <button type="submit" title="Select" class="action primary" id="rh-module-select-btn" data-bind="click:addCustomValue">
                <span>Select</span>
            </button>
            <button type="button" title="Cancel" class="action" id="rh-module-cancel-btn">
                <span>Cancel</span>
            </button>
        </div>
    </div>
    <!-- /ko -->
</div>

<script type="text/x-magento-init">
{
    "*": {
        "Magento_Ui/js/core/app": {
           "components": {
                "ko-example": {
                    "component": "Vendor_Module/js/kotemplate",
                    "template" : "Vendor_Module/kotemplate"
                    }
                }
            }
        }
}
</script>

Here, rh-module only visible when isVisible true and that will be get only in ajax respose. So, when <div class="rh-module"> visible kotemplate js will be call and you can bind data.

More reference

4
  • Thanks for the answer :). There are only difference between data-mage-init and text/x-magento-init is that data-mage-init adds js component on specific HTML element while text/x-magento-init is no relation to a certain HTML element. Both types, initialized js component on DOM ready so it's not working on ajax response. Commented Jan 22, 2019 at 8:41
  • 1
    I just found the solution to my issue. We need to re-bind the js component by knockout applyBindings function check my answer for more details. Commented Jan 22, 2019 at 9:02
  • Thanks for new solution @Prince +1 from mine. Can you please elaborate more in your answer? Commented Jan 22, 2019 at 9:27
  • 1
    Thanks @Rohan I will update the answer soon :) Commented Jan 22, 2019 at 9:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.