using knockout es5 plugin in AMD module - javascript

I can use knockout with requirejs applications AMD module, here example. Actually using dojo toolkit.
// Main viewmodel class
define(['knockout-x.y.z'], function(ko) {
return function appViewModel() {
this.firstName = ko.observable('Bert');
};
});
But how can I use knockoutjs extension knockout-es5 this is does not work in AMD module.

You are right, knockout-es5 is not an AMD-compliant module. However, that doesn't stop you from using it properly with RequireJS. What you will have to do is use a shim (http://requirejs.org/docs/api.html#config-shim) in your require config. The reason you need this is because you need to tell require that the plugin depends on Knockout and therefore cannot be loaded first.
Here is what your require config might look like:
var require = {
paths: {
'ko': 'knockout.min',
'koES5': 'knockout-es5.min',
},
shim: {
'koES5': { deps: ['ko'] }
}
};
This creates 2 paths, one to Knockout and one to Knockout-es5. In the shim we tell require that knockout-es5 depends on knockout. This ensures loading happens in the right order.
Now your RequireJS module you should look like:
define(['ko', 'koES5'], function(ko) {
return function appViewModel() {
this.firstName = ko.observable('Bert');
};
});
NOTE: You technically don't need ko as a requirement because koES5 will already have asked it to load since it is marked as a dependency. Also, you don't need a variable for koES5 because it doesn't return anything that you would use, you're simply adding it as a requirement to ensure the file gets loaded.

Related

requirejs dependencies load order doesn't work

I have such a code:
requirejs.config({
urlArgs: "bust=" + (new Date()).getTime(),
paths: {
mods: 'default',
myFriend: 'myFriend',
myCoworker: 'myCoworker'
},
shim: {
mods: ['myFriend', 'myCoworker']
}
});
require(['mods'], function (mods) {
// something to do
});
and modules which are the dependencies:
myFriend.js
var mess = `<a really huge text, almost 200 Kbytes>`
console.log('This code is ran in the myFriend module...', {mess:mess});
myCoworker.js
console.log('This code is ran in the myCoworker module...');
var wrk = {
name: 'John'
};
So I hope, that accordingly to shim is should always load myFriend.js (that is checked by console.output) before myCoworker.js. But it doesn't. The console output shows:
This code is run in the myCoworker module...
and then
This code is run in the myFriend module...
Probably I have missed something, but what?
The entire code is here: http://embed.plnkr.co/zjQhBdOJCgg8QuPZ5Q8A/
Your dealing with a fundamental misconception in how RequireJS works. We use shim for files that do not call define. Using shim makes it so that RequireJS will, so to speak, add a kind of "virtual define" to those files. The shim you show is equivalent to:
define(['myFriend', 'myCoworker'], function (...) {...});
The dependency list passed to a define or require call does not, in and of itself, specifies a loading order among the modules listed in the dependency list. The only thing the dependency list does is specify that the modules in the list must be loaded before calling the callback. That's all.
If you want myFriend to load first, you need to make myCoworker dependent on it:
shim: {
mods: ['myFriend', 'myCoworker'],
myCoworker: ['myFriend'],
}
By the way, shim is really meant to be used for code you do not control. For your own code you should be using define in your code instead of setting a shim in the configuration.

requirejs dependencies grouping

I am trying to integrate requirejs framework to my app.
Is possible to create a virtual module (which doesn't exists as a physically file), where i could group all the jquery-validation plugins together?
For example, i need to load 4 dependencies everytime i want to use jquery-validate.
Instead of requesting them, each time, i create a jquery-val "virtual module", which should request all the dependencies automatically.
However, trying to load "jquery-val" actually tries to load the file from disk (which i don't have).
What should be the best practice in solving this issue?
// config
requirejs.config({
baseUrl: '/Content',
paths: {
'jquery': 'frameworks/jquery-3.1.1.min',
"jquery-validate": "frameworks/jquery.validate.min",
"jquery-validate-unobtrusive": "frameworks/jquery.validate.unobtrusive.min",
"jquery-unobtrusive-ajax": "frameworks/jquery.unobtrusive-ajax.min"
},
shim: {
"jquery-val": ["jquery", "jquery-validate", "jquery-validate-unobtrusive", "jquery-unobtrusive-ajax"]
}
});
// Solution 1: working, but ugly
define(["jquery", "jquery-validate-unobtrusive", "jquery-unobtrusive-ajax"], function ($) {
// My Module
});
// Solution 2: not working
define(["jquery-val"], function () {
// My Module
});
// Solution 3: create jquery-val.js file, which loads the dependencies automatically
// jquery-val.js
(function (global) {
define(["jquery", "jquery-validate-unobtrusive", "jquery-unobtrusive-ajax"], function ($) {
});
}(this));
take some time and read:
http://requirejs.org/docs/api.html#modulenotes
One module per file.: Only one module should be defined per JavaScript file, given the nature of the module name-to-file-path lookup algorithm. You shoud only use the optimization tool to group multiple modules into optimized files.
Optimization Tool
To answer your question:
It is good practice to define one module per file, so you don't need to define explicit a name for the module AND do the need for inserting it somewhere to be available before the other modules are loaded.
So you could require just the file: require("../services/myGroupModule") and this file would hold your module and requireJS would take care of the loading dependencies (and later the optimizations for concatenating into one file!). Here the module name is the file name.
You could nevertheless do the following and loading it as a file module or like you tried to define it beforehand and give the module a name:
//Explicitly defines the "foo/title" module:
define("myGroupModule",
["dependency1", "dependency2"],
function(dependency1, dependency2) {
return function myGroupModule {
return {
doSomething: function () { console.log("hey"); }
}
}
}
);
Maybe you should also give a look at some new module loaders:
WebPack 2: https://webpack.js.org/
SystemJS: https://github.com/systemjs/systemjs

How to inject a dependency on an external script

I'm using a cloud service called Parse in a JavaScript closure injected with require.js:
define([/*some dependencies*/],
function(/*some dependencies*/) {
...
// using Parse. For example:
var TestObject = Parse.Object.extend("TestObject");
...
}
The tutorial for using Parse in Javascript instructs to include their script in the HTML header:
<script src="//www.parsecdn.com/js/parse-1.4.2.min.js"></script>
I don't want the HTML page to depend on Parse and I don't want to clutter the HTML with external scripts. Instead, I would like to require parse-1.4.2.min.js directly from the script. What's the proper way of doing so? How do I define this dependency and make it work?
Similar to jQuery, Parse adds itself to the global scope on load. If no other scripts depend on it, it can simply be included as a dependency in a module or require call.
require([
'https://www.parsecdn.com/js/parse-1.4.2.min.js',
], function () {
console.log(Parse);
}
If you are using any other non-AMD scripts that depend on Parse (or any other library) you will need to use a config/shim. It tells requireJS what order it should load the scripts, based on their dependencies.
E.g. when using a jQuery plugin, you wouldn't want it to load and execute before jQuery itself.
A config/paths setup also helps organise your project by allowing script locations to be defined in a single location and then included by reference.
See the requireJS docs for more info.
The following config/require successfully loads Parse and a fictional plugin:
require.config({
// define paths to be loaded, allows locations to be maintained in one place
paths: {
parse: 'https://www.parsecdn.com/js/parse-1.4.2.min',
plugin: 'example-non-amd-parse-plugin.js'
},
// define any non-amd libraries and their dependancies
shim: {
parse: {
exports: 'Parse'
},
plugin: {
exports: 'plugin',
deps: ['parse']
}
}
});
require(['parse'], function(Parse){
console.log(Parse);
});
// note Parse is not required
require(['plugin'], function(plugin){
// Parse is available as it is depended on by plugin and on the global scope
console.log(Parse, plugin);
});
So after all I just wrote this and it seems to work. Don't know what the problem was in the first place...
define(["https://www.parsecdn.com/js/parse-1.4.2.min.js"],
function () {

Using knockout.simpleGrid.3.0.js with Require.js

I am using require.js with knockout on a website and would like to use the simpleGrid example from this link http://knockoutjs.com/examples/grid.html however I cannot include kncokout.simpleGrid.3.0.js with Require.
I have tried wrapping the plugin with
define(['jQuery', 'knockout'], // Require knockout
function($, ko) {
});
This does not work it seems the problem occurs with the templates.
Any help appreciated
In your require config, you should create a path to the simpleGrid library and use the shim to tell it that it depends on Knockout so that your libraries are loaded in the correct order. Here's an example:
var require = {
paths: {
'jquery': 'lib/vendor/jquery-2.0.3',
'ko': 'lib/vendor/knockout-3.0.0',
'koSimpleGrid': 'lib/vendor/knockout.simpleGrid.3.0'
},
shim: {
'koSimpleGrid': {
deps: ['ko']
},
}
};
And then you could copy paste the view model code from the example inside of a define like this:
define(['jquery', 'ko', 'koSimpleGrid'], function ($, ko) {
// VIEW MODEL GOES HERE
});
I agree the problem seems to be with the code that writes the grid template. Essentially, because requirejs loads modules asynchronously, document.write() can't be used - writing of the document has finished by the time a module executes. This StackOverflow answer explains it well I think.
I got round it by instead creating and appending the required script tag templates using dom methods:
templateEngine.addTemplate = function(templateName, templateMarkup) {
var scriptTag = document.createElement('script');
scriptTag.type = 'text/html';
scriptTag.id = templateName;
scriptTag.text = templateMarkup;
document.getElementsByTagName('body')[0].appendChild(scriptTag);
};
My amd version is in this gist.

Explicit vs. implicit dependency handling in require.js

I'll use what I'm actually doing as an example. I have a knockout custom binding that depends on a jquery plugin that itself depends on jQuery UI that of course depends on jQuery. There is another file that depends on another plugin, and another file that depends on jQuery UI, etc. In require.config.js I have:
shim: {
"jquery-ui": {exports: "$", deps: ["jquery"]},
"jquery-plugin1": {exports: "$", deps: ["jquery-ui"]},
"jquery-plugin2": {exports: "$", deps: ["jquery-ui"]}
}
This works, and then in corresponding files I may do:
define(["jquery-plugin1"], function ($) {
However, I could also do:
define(["jquery", "jquery-ui", "jquery-plugin1"], function ($) {
There is also the case where a file may depend on both plugins:
// which one?
define(["jquery-plugin1", "jquery-plugin2"], function ($) {
define(["jquery", "jquery-ui", "jquery-plugin1", "jquery-plugin2"], function ($){
There may also be other dependencies such as knockout custom bindings (which don't need to export anything) so I may end up with:
define(["jquery-plugin1", "model1", "model2",
"ko-custom1", "ko-custom2", "ko-custom3",
"jquery-plugin2"],
function ($, m1, m2) {
This file may also depend on jQuery UI (which depends on jQuery), but those are both loaded implicitly via the plugins.
My question is is it better to be explicit about all requirements (i.e. include jQuery and jQuery-UI in define) and possibly leave off the exports, or is the less-verbose nested dependency handling preferred?
This is a great question and becomes very relevant when using something like AngularJS dependency injection as those dependencies need to exist before the module is registered. So this won't work:
define(['angular'],function (angular) {
return angular.module('myModule', ['mySubmodule']);
});
// Error: [$injector:nomod] Module 'mySubmodule' is not available!
You need to define the AMD dependency as well:
define(['angular','./mySubmodule'],function (angular) {
return angular.module('myModule', ['mySubmodule']);
});
It might be subjective but I find it easier to reason about it by having each module define it's own dependencies explicitly and letting requireJS resolve them instead of leaving it up to faith that an out-of-scope module has already defined them which breaks modularity.
By doing this you also know that your AMD modules can be tested independently without rewiring the missing dependency.

Categories

Resources