I'm using RequireJS in my Web Application.
I found little step by step information on how modules should interact in practice.
Mostly theory. Or some too hard examples that I can't understand yet with my current experience in modules.
Directory structure:
| application
| -- app/
| -- customizer.js
| -- sorting.js
| -- config.js
| -- require.js
What is right way to decouple modules and pass parameters
between modules if event fired?
config.js:
requirejs.config({
baseUrl: "...",
paths: {
customizer: 'app/customizer',
sorting: 'app/sorting'
},
config: {
'_#r6': {
view: view,
selector: {
sorting: {
main: ".sort-bar",
items: ".sorting-item"
}
}
}
}
});
require(['module', 'customizer', 'sorting'],
function(module, customizer, sorting) {
// Variables
var config = module.config();
// Modules init data
var init_data = {
// Customizer module settings
customizer: {
config: config
},
// Sorting module settings
sorting: {
config: config
}
};
// Init modules
customizer.init(init_data.customizer);
sorting.init(init_data.sorting);
});
sorting.js:
define(['jQuery', 'customizer'], function($, customizer) {
var config;
var facade = {};
var selector = {};
var callbacks = {
// Events
sortItems: function() {
var selector = callbacks.getSelector();
$(selector.items).find("a").on("click", function(e) {
e.preventDefault();
// some actions with DOM
// then call function from "customizer.js" module
// or change some properties in the module
// How to decouple modules there?
customizer.itemsReload();
});
},
setConfig: function(value) {
config = value;
},
getConfig: function() {
return config;
},
setSelector: function(value) {
selector = value;
},
getSelector: function() {
return selector;
}
};
return {
init: function(atts) {
facade = this;
callbacks.setConfig(atts.config);
callbacks.setSelector(config.selector.sorting);
callbacks.sortItems();
}
}
});
customizer.js:
define(['lodash', 'templates'], function(_, templates) {
var config = {};
var isLoaded;
var callbacks = {
setConfig: function(value) {
config = value;
},
itemsLoad: function() {
// Some code
isLoaded = true;
}
};
return {
init: function(atts) {
facade = this;
// Pre Setup
callbacks.setConfig(atts.config);
items.itemsLoad();
},
itemsReload: function() {
callbacks.itemsLoad();
}
}
});
You can use some kind of EventBus. There are plenty of them available as modules.
Flow is quiet simple, customizer listens for an event which is triggered by sorting.
Probably this can be achieved with jQuery but I am not a jQuery expert :)
Related
Here i'm trying to extend Amasty js widget in my module using mixin.
I have created requirejs-config.js file like below
var config = {
config: {
mixins: {
'amShopbyAjax': {
'vendor_CustomCatalog/js/amasty/amShopbyAjax-override': true
}
}
}
};
I have created js file like below
define([
"jquery",
"jquery-ui-modules/widget"
], function ($) {
'use strict';
var amShopbyWidgetMixin = {
selectors: {
category_products_wrapper: '#amasty-shopby-category-product-list',
},
reloadHtml: function (data) {
console.log("Sdfsdfsdf");
return this._super();
},
callAjax: function (clearUrl, data, pushState, cacheKey, isSorting) {
console.log("sdnjkasndnsadj");
return this._super();
}
};
return function (targetWidget) {
$.widget('custom.amShopbyAjax', targetWidget, amShopbyWidgetMixin);
return $.custom.amShopbyAjax;
};
});
After that I ran sudo bin/magento setup:static-content:deploy -f and clear cache then I am getting "Uncaught TypeError: base is not a constructor" error
Think that you should change custom.amShopbyAjax to mage.amShopbyAjax
return function (targetWidget) {
$.widget('mage.amShopbyAjax', targetWidget, amShopbyWidgetMixin);
return $.mage.amShopbyAjax;
};
The widget alias should be like for the target widget and the widget by parent alias should be returned
i have the following scenario:
i have a global namespace called fort which has a few common functions that i need and it looks like this :
fort.js
define("fort", ["fortHistory"], function (FortHistory) {
function Fort(){}
Fort.prototype.history = FortHistory;
return Fort;
});
fortHistory is a small module i created defined as so:
fortHistory.js
"use strict";
define("fortHistory", function () {
function FortHistory() {
}
FortHistory.prototype.doSomething = function(){...}
return FortHistory;
});
i then do this in my config.js
require.config( {
enforceDefine: true,
paths: {
'fort': 'develop/js/fort',
'fortHistory' : 'develop/js/webapp/fortHistory'
},
shim: {
fort:{
exports: 'fort'
}
}
} );
define( function() {} );
finally in main.js i have:
define('fort', [], function(fort){
window.fort = fort;
});
the hope was that i could then make a call such as :
fort.fortHistory.doSomething();
instead fort is undefined so i am assuming i have misinterpreted how requirejs works
You've named it history, not fortHistory:
Fort.prototype.history = FortHistory;
Try calling it via fort.history.doSomething();.
I'm trying out requireJS in order to improve the loading of Javascript on an ASP.NET MVC app, using Knockout.
I have some files defining custom ko bindings like that:
(function (ko, bindings) {
bindings.stopBinding = {
init: function () {
return { controlsDescendantBindings: false };
}
};
bindings.anotherBinding = { ... };
})(ko, ko.bindingHandlers);
If I try to load it as a requireJS module this way:
define(['jquery', 'knockout', 'custom/knockout.bindings'], function ($, ko){
ko.applyBindings(...);
});
I get a ko is not defined error.
I know that I could enclose this file in a require callback for instance in order ot make it work:
require(['knockout'], function (ko) {
(function (ko, bindings) {
bindings.stopBinding = {
init: function () {
return { controlsDescendantBindings: false };
}
};
bindings.anotherBinding = { ... };
})(ko, ko.bindingHandlers);
});
Is there another way to allow this file to work without having to update each and every legacy JS file in the application ? I thought of using shim, but I didn't get anywhere, but I'm quite a noob with requireJS, so maybe I'm missing something obvious.
Thanks to this answer, I managed to inject Knockout back in the global namespace, making it available to legacy Javascript files that needed it.
First, create a module that injects ko in the global namespace:
define('knockout.inject', ['knockout'], function (k) {
window.ko = k;
return k;
});
Then, map the module to knockout to execute it for every knockout dependency.
var require = {
baseUrl: "/Scripts",
paths: {
//...
"knockout": "knockout-3.3.0.debug",
"knockoutbindings": "knockout.bindings",
},
shim: {
"knockoutbindings": {
deps: ["knockout"]
}
},
map: {
// inject ko back in the global namespace
'*': {
'knockout': 'knockout.inject'
},
// prevent cycles
'knockout.inject': { 'knockout': 'knockout' }
}
};
I'm building an application that can consists of multiple modules (and some modules will even have sub-modules). When the application is instantiated, I pass in an object the describes the modules that I'd like to be active for that application instance (myApp), and the class those modules should use. The Application then uses a factory object to create the necessary modules. This has worked great as a proof of concept, but I'm having troubles with creating submodules from within other modules initialize methods and getting those submodules to start up.
Using the code below, I see the following messages dumped to the console (added numbers for reference).
( Some.Module.Class ) moduleA initialize
( Some.Module.Class ) moduleB initialize
( Some.SubModule.Class ) moduleB initialize
( Some.SubModule.Class ) submoduleB initialize
( Some.Module.Class ) moduleA start
( Some.Module.Class ) moduleB start
What is confusing to me is
1. When the onStart method for moduleB is called, submodules is empty, and no submoduleB doesn't start.
2. Why does line #3 in the console messages appear? It's calling moduleB initialize twice, but with the submodule class?
//SUB MODULE
Some.SubModule.Class = Io.Modules.BaseModule.extend({
__className: "Some.SubModule.Class",
startWithParent: false,
onStart: function(options) {
console.log("(", this.getClassName(), ")", this.moduleName, "start");
},
initialize: function(options, moduleName, app) {
console.log("(", this.getClassName(), ")", this.moduleName, "initialize");
}
});
// MODULE
Some.Module.Class = Io.Modules.BaseModule.extend({
__className: "Some.Module.Class",
startWithParent: false,
onStart: function(options) {
console.log("(", this.getClassName(), ")", this.moduleName, "start");
_.each(this.submodules, function(module) { module.start(); });
},
initialize: function(moduleName, app, options) {
console.log("(", this.getClassName(), ")", this.moduleName, "initialize");
var moduleFactory = new Io.Factories.ModuleFactory({app:app});
var fieldConfigurator = moduleFactory.createModule("moduleB.submoduleB", Some.SubModule.Class, {});
}
});
// APPLICATION
var MyApp = Marionette.Application.extend({
...
initialize: function(opts) {
//Create all modules described in the options passed in
}
//Start up all modules
_.each(this.submodules, function(module) { module.start(); });
...
});
var myApp = new MyApp({
modules: {
moduleA: {
"class":Some.Module.Class,
options:{
someOption:someValue
}
},
moduleB: {
"class":Some.Module.Class,
options:{
someOption:someValue
}
},
...
}
});
myApp.start();
This is from knockout.dirtyFlag.js
;(function (ko) {
ko.DirtyFlag = function (objectToTrack, isInitiallyDirty, hashFunction) {
hashFunction = hashFunction || ko.toJSON;
var
_objectToTrack = objectToTrack,
_lastCleanState = ko.observable(hashFunction(_objectToTrack)),
_isInitiallyDirty = ko.observable(isInitiallyDirty),
result = function () {
var self = this;
self.isDirty = ko.computed(function () {
return _isInitiallyDirty() || hashFunction(_objectToTrack) !== _lastCleanState();
});
self.reset = function () {
_lastCleanState(hashFunction(_objectToTrack));
_isInitiallyDirty(false);
};
return self;
};
return result;
};
})(ko);
In my model I have a define setup like this:
define([
"lib/knockout",
"lib/knockout.dirtyFlag"
],
function(ko) {
...
self.dirtyFlag = new ko.DirtyFlag([
}
basically I get an error saying that DirtyFlag is undefined.
What do I need to do?
Well, looks like I got it working, so I'll post my findings:
In my requirejs config I added this:
shim: {
"lib/knockout/knockout.dirtyFlag": {
deps: [
"lib/knockout/knockout"
],
init: function (ko) {
var self = this;
ko.DirtyFlag = self.ko.DirtyFlag;
return ko;
}
}
I'm not very familiar with javascript or requirejs, but init seems to put the dep in "ko" and then I am able to create a DirtyFlag on ko. self.ko.DirtyFlag is the actual knockout.dirtyFlag javascript.
You are requiring lib/knockout and lib/knockout.dirtyFlag. Do you need both ?
If you need both, try:
define([
"lib/knockout",
"lib/knockout.dirtyFlag"
],
function(ko, kodf) {
...
self.dirtyFlag = new kodf.DirtyFlag([
}
You could also try:
define([
"lib/knockout",
"lib/knockout.dirtyFlag"
],
function(k) {
...
self.dirtyFlag = new ko.DirtyFlag([
}
As I think you are defining ko in the require as well as in knockout.dirtyFlag.
I have ended with wrapping kolite classes with define methods. Below example with command, but it will be same for others.
requirejs.config({
paths: {
knockout: 'Scripts/libs/knockout/knockout-2.2.1.debug',
command: 'Scripts/libs/knockout/knockout.command'
},
shim: {
knockout: {
exports: 'ko'
},
command: {
deps: ['knockout'],
exports: 'ko'
}
});
And wrapped command
define(['knockout'], function(ko) {
... // command original code
return ko;
)
in my module
define(['command'], function (ko) {
var doYourStaff = 0;
return doYourStaff;
});
We have added support for RequireJS for all three libs now, you can use the latest version from master.