Magento – Knockout.js What is the correct way to reference $parent on js file

knockoutjsmagento2viewmodel

I am new on Knockout.js, especially on how to use it in the Magento way.

I create a custom ViewModel with a js file called storepickup-select.js and an HTML template called storepickup-select.html.

I override Magento_Checkout/js/view/shipping on a custom shipping.js file on my custom module. In this file, I added two functions: selectedCarrierCode and saveStoreAddress.

When I call selectedCarrierCode on storepickup-select.html using the $parent variable, it works fine and without any problem:

<form class="form form-shipping-storepickup" id="co-storepickup-form" data-bind="visible: $parent.selectedCarrierCode()">
...
</form>

But when I tried to call saveStoreAddress from storepickup-select.js, it throws me an error:

selectStore: function(){
    ...                            
    $parent.saveStoreAddress(addresData);
    ...
}

ReferenceError: $parent is not defined

I tried some other options:

parent 
this.parent
this.$parent

But it doesn´t work. Why I can use $parent on HTML file but I can´t use it on js file? And, what is the better way to call saveStoreAddress from js file?

Thanks.

EDIT:

I added my js files:

shipping.js

define([
    'jquery',
    'underscore',
    'Magento_Ui/js/form/form',
    'ko',
    'Magento_Customer/js/model/customer',
    'Magento_Customer/js/model/address-list',
    'Magento_Checkout/js/model/address-converter',
    'Magento_Checkout/js/model/quote',
    'Magento_Checkout/js/action/create-shipping-address',
    'Magento_Checkout/js/action/select-shipping-address',
    'Magento_Checkout/js/model/shipping-rates-validator',
    'Magento_Checkout/js/model/shipping-address/form-popup-state',
    'Magento_Checkout/js/model/shipping-service',
    'Magento_Checkout/js/action/select-shipping-method',
    'Magento_Checkout/js/model/shipping-rate-registry',
    'Magento_Checkout/js/action/set-shipping-information',
    'Magento_Checkout/js/model/step-navigator',
    'Magento_Ui/js/modal/modal',
    'Magento_Checkout/js/model/checkout-data-resolver',
    'Magento_Checkout/js/checkout-data',
    'uiRegistry',
    'mage/translate',
    'Magento_Checkout/js/model/shipping-rate-service'
], function (
    $,
    _,
    Component,
    ko,
    customer,
    addressList,
    addressConverter,
    quote,
    createShippingAddress,
    selectShippingAddress,
    shippingRatesValidator,
    formPopUpState,
    shippingService,
    selectShippingMethodAction,
    rateRegistry,
    setShippingInformationAction,
    stepNavigator,
    modal,
    checkoutDataResolver,
    checkoutData,
    registry,
    $t
) {
    'use strict';

    var mixin = {
        selectShippingMethod: function (shippingMethod) {
            selectShippingMethodAction(shippingMethod);
            checkoutData.setSelectedShippingRate(shippingMethod.carrier_code + '_' + shippingMethod.method_code);

            return true;
        },

        selectedCarrierCode: ko.computed(function() {
          var method = quote.shippingMethod();
          var selectedCarrierCode = (method != null) ? method.carrier_code : null;
          return selectedCarrierCode;
        }),

        saveStoreAddress: function (addressData) {
          addressData['save_in_address_book'] = 0;
          console.log(addressData);
          newShippingAddress = createShippingAddress(addressData);
          selectShippingAddress(newShippingAddress);
          checkoutData.setSelectedShippingAddress(newShippingAddress.getKey());
          checkoutData.setNewCustomerShippingAddress($.extend(true, {}, addressData));
          //this.getPopUp().closeModal();
          this.isNewAddressAdded(true);
        }
    };

    return function (target) {
      return target.extend(mixin);
    };
});

storepickup-select.js

define([
        'jquery',
        'ko',
        'mage/url',
        'uiComponent',
        'Magento_Checkout/js/model/quote'
], function ($, ko, url, Component, quote) {
        'use strict';

        return Component.extend({
            defaults: {
                template: 'Morwi_Checkout/checkout/shipping-address/storepickup-select.html'
            },

            /** @inheritdoc */
            initialize: function (){
              this._super();
              this.bindAvailableStores();
              this.bindSelectedStore();
            },

            storeDetailUrl: ko.observable(),

            availableStores: ko.observableArray([]),

            storeAddress: [],

            selectedStore: ko.observable(),

            bindAvailableStores: function(){
              console.log('bindAvailableStores');
              var element = this;

              $.ajax({
                url: url.build('recogertienda/stores/get'),
                type: 'post',
                contentType: 'application/json',
                async: false,
                success: function(response){
                  element.setAvailableStores(response)

                  for(var index in response){
                    element.storeAddress[response[index].id] = response[index].address;
                  }

                  console.log(element.storeAddress);
                }
              });
            },

            bindSelectedStore: function(){
              console.log('bindSelectedStore');
              var element = this;

              $.ajax({
                url: url.build('recogertienda/stores/selected'),
                type: 'post',
                contentType: 'application/json',
                async: false,
                success: function(response){
                  if(response.id != 0){
                    element.setSelectedStore(response.id);
                  }
                  else{
                    element.setSelectedStore('');
                  }

                  element.selectStore();
                }
              });
            },

            setAvailableStores(data){
              this.availableStores(data);
            },

            setSelectedStore(data){
              this.selectedStore(data);
            },

            selectStore: function(){
              if((typeof this.selectedStore() !== 'undefined') && (this.selectedStore() != '')){
                this.storeDetailUrl(url.build('storelocator/index/view/id/' + this.selectedStore().toString()));

                $parent.saveStoreAddress(this.storeAddress[this.selectedStore()]);
              }
              else{
                this.storeDetailUrl(url.build('storelocator'));
              }
            }
        });
    }
);

Best Answer

You don't need to call $parent in js, just call your function directly using this.functionName().

EDIT (Some context) :

$parent: This is the view model object in the parent context, the one immeditely outside the current context.

You call $parent in your case to refer to js file associated with HTML template in /js/view/.. i.e the view model.

Related Topic