I'm trying to override a widget variable using mixin , but I get the following error Uncaught TypeError: target.extend is not a function
the original file is
define([
 "underscore",
 "jquery",
 "knockout",
 'amShopbyTopFilters',
 "productListToolbarForm",
 "amShopbyFilterAbstract",
 "Magento_PageCache/js/page-cache"
], function (_, ,ドル ko, amShopbyTopFilters) {
 'use strict';
 $.widget('mage.amShopbyAjax', {
 prevCall: false,
 $shopbyOverlay: null,
 cached: [],
 memorizeData: [],
 blockToolbarProcessing: false,
 swatchesTooltip: '.swatch-option-tooltip',
 filterTooltip: '.amshopby-filter-tooltip',
 response: null,
 cacheKey: null,
 startAjax: false,
 isCategorySingleSelect: 0,
 nodes: {
 loader: $('<div id="amasty-shopby-overlay" class="amshopby-overlay-block">' +
 '<span class="amshopby-loader"></span></div>')
 },
 selectors: {
 products_wrapper: '#amasty-shopby-product-list, .search.results',
 top_nav: '.amasty-catalog-topnav',
 products: '.products.wrapper',
 overlay: '#amasty-shopby-overlay',
 top_navigation: '.catalog-topnav .block.filter',
 js_init: '[data-am-js="js-init"]',
 title_head: '#page-title-heading'
 },
 _create: function () {
 var self = this;
 $(function () {
 self.initWidget();
 if (typeof window.history.replaceState === "function") {
 window.history.replaceState({url: document.URL}, document.title);
 setTimeout(function () {
 /*
 Timeout is a workaround for iPhone
 Reproduce scenario is following:
 1. Open category
 2. Use pagination
 3. Click on product
 4. Press "Back"
 Result: Ajax loads the same content right after regular page load
 */
 window.onpopstate = function (e) {
 if (e.state) {
 if ($.mage.amShopbyApplyFilters) {
 $.mage.amShopbyApplyFilters.prototype.showButtonClick = true;
 }
 self.callAjax(e.state.url, []);
 self.$shopbyOverlay.show();
 }
 };
 }, 0)
 }
 });
 },
 initWidget: function () {
 var self = this,
 swatchesTooltip = $(self.swatchesTooltip),
 filterTooltip = $(self.filterTooltip);
 $(document).on('baseCategory', function (event, eventData) {
 self.currentCategoryId = eventData;
 });
 $(document).on('amshopby:submit_filters', function (event, eventData) {
 var data = eventData.data,
 clearUrl = self.options.clearUrl,
 isSorting = eventData.isSorting,
 pushState = !self.options.submitByClick;
 if (typeof data.clearUrl !== 'undefined') {
 clearUrl = data.clearUrl;
 delete data.clearUrl;
 }
 if (self.prevCall) {
 self.prevCall.abort();
 }
 var dataAndUrl = data.slice(0);
 dataAndUrl.push(clearUrl ? clearUrl : self.options.clearUrl);
 var cacheKey = JSON.stringify(dataAndUrl);
 $.mage.amShopbyAjax.prototype.cacheKey = cacheKey;
 if (self.cached[cacheKey]) {
 var response = self.cached[cacheKey];
 if (pushState || isSorting) {
 if (response.newClearUrl
 && response.newClearUrl.indexOf('?p=') == -1
 && response.newClearUrl.indexOf('&p=') == -1
 ) {
 self.options.clearUrl = response.newClearUrl;
 }
 window.history.pushState({url: response.url}, '', response.url);
 self.reloadHtml(response);
 self.initAjax();
 } else if ($.mage.amShopbyApplyFilters) {
 $.mage.amShopbyApplyFilters.prototype.showButtonCounter(
 response.productsCount
 );
 }
 return;
 }
 self.prevCall = self.callAjax(clearUrl, data, pushState, cacheKey, isSorting);
 });
 $(document).on('touchstart touchmove scroll', function () {
 if (swatchesTooltip) {
 swatchesTooltip.hide();
 filterTooltip.trigger('mouseleave');
 }
 });
 $(document).on('amshopby:reload_html', function (event, eventData) {
 self.reloadHtml(eventData.response);
 });
 filterTooltip.on('tap', function () {
 $(this).trigger('mouseenter');
 });
 self.initAjax();
 },
 callAjax: function (clearUrl, data, pushState, cacheKey, isSorting) {
 var self = this;
 if (pushState || isSorting) {
 this.$shopbyOverlay.show();
 }
 data.every(function (item, key) {
 if (item.name.indexOf('[cat]') != -1) {
 if (item.value == self.options.currentCategoryId) {
 data.splice(key, 1);
 } else {
 item.value.split(',').filter(function (element) {
 return element != self.options.currentCategoryId
 }).join(',');
 }
 return false;
 }
 return true;
 });
 data.push({name: 'shopbyAjax', value: 1});
 data = this.addMemorizeData(data);
 $.mage.amShopbyAjax.prototype.startAjax = true;
 if (!clearUrl) {
 clearUrl = self.options.clearUrl;
 }
 clearUrl = clearUrl.replace(/amp;/g, '');
 self.clearUrl = clearUrl;
 return $.ajax({
 url: clearUrl,
 data: data,
 cache: true,
 success: function (response) {
 try {
 $.mage.amShopbyAjax.prototype.startAjax = false;
 response = JSON.parse(response);
 if (response.isDisplayModePage) {
 throw new Error();
 }
 if (cacheKey) {
 self.cached[cacheKey] = response;
 }
 $.mage.amShopbyAjax.prototype.response = response;
 if (response.newClearUrl
 && (response.newClearUrl.indexOf('?p=') == -1 && response.newClearUrl.indexOf('&p=') == -1)) {
 self.options.clearUrl = response.newClearUrl;
 }
 if (pushState || ($.mage.amShopbyApplyFilters && $.mage.amShopbyApplyFilters.prototype.showButtonClick) || isSorting) {
 window.history.pushState({url: response.url}, '', response.url);
 }
 if (self.options.submitByClick !== 1 || isSorting) {
 self.reloadHtml(response);
 }
 if ($.mage.amShopbyApplyFilters && $.mage.amShopbyApplyFilters.prototype.showButtonClick) {
 $.mage.amShopbyApplyFilters.prototype.showButtonClick = false;
 $.mage.amShopbyAjax.prototype.response = false;
 self.reloadHtml(response);
 }
 if ($.mage.amShopbyApplyFilters) {
 $.mage.amShopbyApplyFilters.prototype.showButtonCounter(response.productsCount);
 }
 $(document).trigger('amshopby:ajax_filter_applied');
 } catch (e) {
 var url = self.clearUrl ? self.clearUrl : self.options.clearUrl;
 window.location = (this.url.indexOf('shopbyAjax') == -1) ? this.url : url;
 }
 },
 error: function (response) {
 try {
 if (response.getAllResponseHeaders() != '') {
 self.options.clearUrl ? window.location = self.options.clearUrl : location.reload();
 }
 } catch (e) {
 window.location = (this.url.indexOf('shopbyAjax') == -1) ? this.url : self.options.clearUrl;
 }
 },
 always: function () {
 if (self.$shopbyOverlay) {
 self.$shopbyOverlay.hide();
 }
 }
 });
 },
 addMemorizeData: function (data) {
 if (this.memorizeData) {
 $.each(this.memorizeData, function (key, param) {
 var current = this.filterDataByProp(data, param, 'name');
 if (_.isUndefined(current)) {
 data.push({ name: param.name, value: param.value });
 } else {
 current.value = param.value;
 }
 }.bind(this));
 }
 return data;
 },
 filterDataByProp: function (data, param, prop) {
 return data.find(function (obj) {
 return obj[prop] === param[prop];
 });
 },
 reloadHtml: function (data) {
 var selectSidebarNavigation = '.sidebar.sidebar-main .block.filter',
 selectTopNavigation = selectSidebarNavigation + '.amshopby-all-top-filters-append-left',
 selectMainNavigation = '',
 $productsWrapper = this.getProductBlock();
 this.options.currentCategoryId = data.currentCategoryId
 ? data.currentCategoryId
 : this.options.currentCategoryId;
 if ($(selectTopNavigation).first().length > 0) {
 selectMainNavigation = selectTopNavigation; //if all filters are top
 } else if ($(selectSidebarNavigation).first().length > 0) {
 selectMainNavigation = selectSidebarNavigation;
 }
 $('.am_shopby_apply_filters').remove();
 if (!selectMainNavigation) {
 if ($('.sidebar.sidebar-main').first().length) {
 $('.sidebar.sidebar-main').first().prepend("<div class='block filter'></div>");
 selectMainNavigation = selectSidebarNavigation;
 } else {
 selectMainNavigation = '.block.filter';
 }
 }
 $(selectMainNavigation).first().replaceWith(data.navigation);
 $(selectMainNavigation).first().trigger('contentUpdated');
 //top nav already exist into categoryProducts
 if (!data.categoryProducts || data.categoryProducts.indexOf('amasty-catalog-topnav') == -1) {
 $(this.selectors.top_navigation).first().replaceWith(data.navigationTop);
 $(this.selectors.top_navigation).first().trigger('contentUpdated');
 }
 var mainContent = data.categoryProducts || data.cmsPageData;
 if (mainContent) {
 $productsWrapper.replaceWith(mainContent);
 // we should reinitialize element - because it was replaced and removed
 $productsWrapper = this.getProductBlock();
 try {
 if (typeof $.fn.applyBindings !== 'undefined') {
 ko.cleanNode($productsWrapper)
 $productsWrapper.applyBindings();
 }
 $productsWrapper.trigger('contentUpdated');
 } catch (e) {
 //do nothing. error with third party extension
 }
 }
 $(this.selectors.title_head).closest('.page-title-wrapper').replaceWith(data.h1);
 $(this.selectors.title_head).trigger('contentUpdated');
 this.replaceBlock('.breadcrumbs', 'breadcrumbs', data);
 this.replaceBlock('.switcher-currency', 'currency', data);
 this.replaceBlock('.switcher-language', 'store', data);
 this.replaceBlock('.switcher-store', 'store_switcher', data);
 this.replaceCategoryView(data);
 if (data.behaviour) {
 this.updateMultipleWishlist(data.behaviour);
 }
 $(window).trigger('google-tag');
 mediaCheck({
 media: '(max-width: 768px)',
 entry: function () {
 amShopbyTopFilters.moveTopFiltersToSidebar();
 if (selectMainNavigation == selectTopNavigation
 && $(selectSidebarNavigation + ':not(.amshopby-all-top-filters-append-left)').length != 0) {
 $(selectSidebarNavigation).first().remove();
 }
 },
 exit: function () {
 amShopbyTopFilters.removeTopFiltersFromSidebar();
 }
 });
 var swatchesTooltip = $('.swatch-option-tooltip');
 if (swatchesTooltip.length) {
 swatchesTooltip.hide();
 }
 if (this.$shopbyOverlay) {
 this.$shopbyOverlay.hide();
 }
 var productList = $(this.selectors.products_wrapper).last();
 this.scrollUp();
 $('.amshopby-filters-bottom-cms').remove();
 productList.append(data.bottomCmsBlock);
 $(this.selectors.js_init).first().replaceWith(data.js_init);
 $(this.selectors.js_init).first().trigger('contentUpdated');
 this.afterChangeContentExternal(productList);
 this.initAjax();
 },
 /**
 * @public
 * @return {Object}
 */
 getProductBlock: function () {
 var $productsWrapper = $(this.selectors.products_wrapper).last();
 if ($productsWrapper.parent('.search.results').length) {
 $productsWrapper = $productsWrapper.parent('.search.results');
 }
 return $productsWrapper;
 },
 scrollUp: function () {
 var productList = $(this.selectors.products_wrapper).last(),
 topNavBlock = $(this.selectors.top_nav);
 if (this.options.scrollUp && productList.length) {
 $(document).scrollTop(this.options.scrollUp === 1
 ? (topNavBlock.length ? topNavBlock.offset().top : productList.offset().top)
 : 0);
 }
 },
 afterChangeContentExternal: function (productList) {
 //compatibility with Amasty Scroll extension
 $(document).trigger('amscroll_refresh');
 //fix issue with wrong form key
 productList.formKey();
 //porto theme compatibility
 var lazyImg = $("img.porto-lazyload:not(.porto-lazyload-loaded)");
 if (lazyImg.length && typeof $.fn.lazyload == 'function') {
 lazyImg.lazyload({effect:"fadeIn"});
 }
 if ($('head').html().indexOf('Infortis') > -1) {
 $(document).trigger('last-swatch-found');
 }
 },
 updateMultipleWishlist: function (data) {
 $('#popup-tmpl').remove();
 $('#split-btn-tmpl').remove();
 $('#form-tmpl-multiple').replaceWith(data);
 $('body').off('click', '[data-post-new-wishlist]');
 require('uiRegistry').remove('multipleWishlist');
 $('.page-wrapper').trigger('contentUpdated');
 },
 replaceBlock: function (blockClass, dataName, data) {
 $(blockClass).replaceWith(data[dataName]);
 $(blockClass).trigger('contentUpdated');
 },
 replaceCategoryView: function (data) {
 var imageElement = $(".category-image"),
 descrElement = $(".category-description");
 if (data.image) {
 if (imageElement.length !== 0) {
 imageElement.replaceWith(data.image);
 } else {
 imageElement.prependTo("column.main");
 }
 } else {
 imageElement.remove();
 }
 if (data.description) {
 if (descrElement.length !== 0) {
 descrElement.replaceWith(data.description);
 } else {
 if (imageElement.length !== 0) {
 $(data.description).insertAfter(imageElement.selector);
 } else {
 descrElement.prependTo("column.main");
 }
 }
 } else {
 descrElement.remove();
 }
 $('title').html(data.title);
 if (data.categoryData) {
 var categoryViewSelector = ".category-view";
 if ($(categoryViewSelector).length === 0) {
 $('<div class="category-view"></div>').insertAfter('.page.messages');
 }
 $(categoryViewSelector).replaceWith(data.categoryData);
 }
 },
 generateOverlayElement: function () {
 var selectors = this.selectors,
 productListContainer = $(selectors.products_wrapper
 .replace(',', ' ' + selectors.products + ',') + ' ' + selectors.products);
 if (!$(this.selectors.overlay).length) {
 productListContainer.append(this.nodes.loader);
 }
 this.$shopbyOverlay = $(this.selectors.overlay);
 },
 initAjax: function () {
 var self = this;
 this.generateOverlayElement();
 if ($.mage.productListToolbarForm) {
 //change page limit
 $.mage.productListToolbarForm.prototype.changeUrl = function (paramName, paramValue, defaultValue) {
 // Workaround to prevent double method call
 if (self.blockToolbarProcessing) {
 return;
 }
 self.blockToolbarProcessing = true;
 setTimeout(function () {
 self.blockToolbarProcessing = false;
 }, 300);
 var decode = window.decodeURIComponent,
 urlPaths = this.options.url.split('?'),
 urlParams = urlPaths[1] ? urlPaths[1].split('&') : [],
 paramData = {},
 currentPage = this.getCurrentPage ? this.getCurrentPage() : '1',
 newPage;
 for (var i = 0; i < urlParams.length; i++) {
 var parameters = urlParams[i].split('=');
 paramData[decode(parameters[0])] = parameters[1] !== undefined
 ? decode(parameters[1].replace(/\+/g, '%20'))
 : '';
 }
 if (currentPage > 1 && paramName === this.options.limit) {
 newPage = Math.floor(this.getCurrentLimit() * (currentPage - 1) / paramValue) + 1;
 if (newPage > 1) {
 paramData[this.options.page] = newPage;
 } else {
 delete paramData[this.options.page];
 }
 }
 paramData[paramName] = paramValue;
 if (paramValue == defaultValue) {
 delete paramData[paramName];
 }
 if (self.options.isMemorizerAllowed) {
 self.memorizeData.push({name: paramName, value: paramValue});
 }
 self.options.clearUrl = self.getNewClearUrl(
 paramName,
 paramData[paramName] ? paramData[paramName] : '',
 paramData[this.options.page]
 );
 //add ajax call
 $.mage.amShopbyFilterAbstract.prototype.prepareTriggerAjax(null, null, null, true);
 };
 }
 //change page number
 $(".toolbar .pages a").unbind('click').bind('click', function (e) {
 var newUrl = $(this).prop('href'),
 updatedUrl = null,
 urlPaths = newUrl.split('?'),
 urlParams = urlPaths[1] ? urlPaths[1].split('&') : [];
 for (var i = 0; i < urlParams.length; i++) {
 if (urlParams[i].indexOf("p=") === 0) {
 var pageParam = urlParams[i].split('=');
 updatedUrl = self.getNewClearUrl(pageParam[0], pageParam[1] > 1 ? pageParam[1] : '');
 break;
 }
 }
 if (!updatedUrl) {
 updatedUrl = this.href;
 }
 updatedUrl = updatedUrl.replace('amp;', '');
 $.mage.amShopbyFilterAbstract.prototype.prepareTriggerAjax(document, updatedUrl, false, true);
 $(document).scrollTop($(self.selectors.products_wrapper).offset().top);
 e.stopPropagation();
 e.preventDefault();
 });
 },
 //Update url after change page size or current page.
 getNewClearUrl: function (key, value, page) {
 var url = new URL(window.location.href),
 params = new window.URLSearchParams(url.search);
 if (value !== '') {
 params.set(key, value);
 } else {
 params.delete(key);
 }
 if (page) {
 params.set('p', page);
 } else if (key !== 'p') {
 params.delete('p');
 }
 url.search = params;
 return window.decodeURIComponent(url.toString());
 }
 });
 return $.mage.amShopbyAjax;
});
my mixin file is
define([
 "underscore",
 "jquery",
 "knockout",
 'amShopbyTopFilters',
 "productListToolbarForm",
 "amShopbyFilterAbstract",
 "Magento_PageCache/js/page-cache"
], function (_, ,ドル ko, amShopbyTopFilters) {
 'use strict';
 var loaderUrl = $("#loaderUrl").val();
 return function (target) {
 target.nodes.loader = $('<div id="amasty-shopby-overlay" class="amshopby-overlay-block">' +
 '<img src="'+loaderUrl+'" alt="Loading..."></div>');
 }
});
3 Answers 3
Replace your code with the following:
define(['jquery'], function ($) {
 'use strict';
 var amShopbyAjaxWidgetMixin = {
 nodes: {
 loader: $('<div id="amasty-shopby-overlay" class="amshopby-overlay-block">' +
 '<img src="'+loaderUrl+'" alt="Loading..."></div>')
 }
 };
 return function (targetWidget) {
 $.widget('mage.amShopbyAjax', targetWidget, amShopbyAjaxWidgetMixin); // the widget alias should be like for the target widget
 return $.mage.amShopbyAjax; // the widget by parent alias should be returned
 };
});
You can read more about how to use mixin from the official document: https://developer.adobe.com/commerce/frontend-core/javascript/mixins/
 answered Dec 21, 2022 at 19:50
 
 
 
 Tu Van 
 
 8,3812 gold badges15 silver badges26 bronze badges
 
 Try this
define([
 'jquery'
], function ($) {
 'use strict';
 var mixin = {
 nodes: {
 loader: $('<div id="amasty-shopby-overlay" class="amshopby-overlay-block">' +
 '<img src="'+$("#loaderUrl").val()+'" alt="Loading..."></div>');
 },
 };
 return function (target) {
 $.widget('mage.amShopbyAjax', target, mixin);
 return $.mage.amShopbyAjax;
 };
})
- 
 Thank you. I fixed itAnass Hl– Anass Hl2022年12月22日 14:27:27 +00:00Commented Dec 22, 2022 at 14:27
define([
 "underscore",
 "jquery",
 "knockout",
 'amShopbyTopFilters',
 "productListToolbarForm",
 "amShopbyFilterAbstract",
 "Magento_PageCache/js/page-cache"
], function (_, ,ドル ko, amShopbyTopFilters) {
 'use strict';
 var loaderUrl = window.TEMPLATE_URL;
 return function (widget) {
 $.widget('mage.amShopbyAjax', widget, {
 nodes: {
 loader: $('<div id="amasty-shopby-overlay" class="amshopby-overlay-block">' +
 '<img src="'+loaderUrl+'/images/loader-1.gif" alt="Loading..."></div>')
 }
 });
 return $.mage.amShopbyAjax;
 }
});
- 
 Your code have redundant dependencies and redundant code. Take a look at my answer for the optimized code.Tu Van– Tu Van2022年12月22日 16:26:02 +00:00Commented Dec 22, 2022 at 16:26
default