Knockout Components "Uses require, but no AMD loader is present" - javascript

I am currently working in a Durandal project and researching the use of Knockout Components in my application. I'm building using Gulp and the gulp-durandal plugin and have it configured to use almond.
I'm running into an issue where I receive the following error when navigating to one of my pages which uses the newly registered components:
component: function () { return componentBindingValue; }" Message:
Component 'myComponent': Uses require, but no AMD loader is present
In the hopes of providing as much information as possible, here is the gulpfile I am currently using as well.
var gulp = require('gulp');
var durandal = require('gulp-durandal');
gulp.task('durandal', function() {
durandal({
baseDir: 'app',
main: 'main.js',
output: 'main-built.js',
almond: true,
minify: true,
rjsConfigAdapter: function (rjsConfig) {
rjsConfig.paths = {
'text': '../Scripts/text',
'durandal': '../Scripts/durandal',
'plugins': '../Scripts/durandal/plugins',
'transitions': '../Scripts/durandal/transitions',
'dataservice': 'domain/dataservice'
};
return rjsConfig;
}
}).pipe(gulp.dest('build'));
});

The Durandal Gulp task is calling r.js with the wrap parameter configured to encapsulate your application code in an IFFE with the Almond source. Unfortunately, Almond's require, requirejs, and define implementations are getting bundled inside and not being added to the global window scope the way Knockout is expecting.
You can manipulate the wrap parameter in the rjsConfigAdapter to remove the IFFE wrappers, or just add require/define to the window object first thing in your application code to get around this.
Ex.
requirejs.config(config);
window.require = require;
window.requirejs = requirejs;
window.define = define;

I ran into this as well, but I had a much simpler front-end stack, and I was only seeing it on one page, even though I was using the component several places through my site.
Turns out it can also be a race condition. I had to put my ko.applyBindings inside of document.ready callback, and everything worked.

Related

How to properly configure requireJS

Hi I'm trying to make starting template for SPA project mainly using:
RequireJS, KnockoutJS, TypeScript, etc.
I'm having hard time figuring out how to configure paths and folder structure for RequireJS to work properly...
here is my folder structure:
Scripts
app
components
main.js
lib
knockout.js
jquery.js
here is my RequireJS config file:
var config = {
waitSeconds: 15,
paths: {
app: '../app',
'knockout': '/lib/knockout-3.4.2.',
sammy: '/lib/sammy-0.7.5.',
jquery: '../scripts/lib/jquery-1.10.2.'
}
};
This is my attempt for main.js:
define(['jquery', 'PageOne', 'PageTwo'], function ($, pageOne, pageTwo) {
$(document).ready(function () {
var app = Sammy('#main', function () {
this.get('#/pageOne', function () {
pageOne.activate();
});
this.get('#/pageTwo', function () {
pageTwo.activate();
});
});
app.run();
});
});
Here is my Index.cshtml script tag:
<script src="~/Scripts/lib/require.js" data-main="scripts/app/components/main"></script>
I saw in different project that config is called in header so this is in html header:
<script src="~/Scripts/app/config/require.config.js"></script>
My problem is that in main.js it looks for jquery under path defined in data-main (scripts/app/components/), but my jquery is in scripts/lib folder.
I'm trying to figure out by reading online the whole day but it's too much time for me I need someone to give me some hints how is this supposed to work?
Seriously having hard time figuring this out and RequireJS website just isn't helping me atm.
Note: I am beginner in JavaScript based projects, first SPA attempt,
never used RequireJS...
Your configuration file does not do anything. I'm assuming from your description that the script element that loads it is located before the script element that loads RequireJS. That's one valid way to configure RequireJS, but if you want RequireJS to pick up the configuration, you need to set the global variable require before you load RequireJS, and RequireJS will use the value of require as its configuration. Right now you are setting config, which is ignored by RequireJS. So:
var require = {
waitSeconds: 15,
// etc...
And once the configuration is in effect, you should be able to reduce your data-main to data-main="components/main".
I see some of your paths in the paths configuration end with a dot. That's most likely a mistake on your part, or you have some very strange file names.

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

JavaScript Faux Pas - Let me put global libraries on the window?

I'm using RequireJS. I absolutely hate the double variable syntax of defining a dependency and passing it in as a variable in a callback function. I am therefore attempting the implement the 'Sugar' syntax available in RequireJS.
However, I only want to 'import' global libraries like Backbone, jQuery, Underscore and Marionette once. jQuery and Backbone obviously assign themselves to the Window object but Underscore and Marionette do not.
This is my main.js file:
require.config({
paths: {
"jquery" : "vendor/jquery.min",
"underscore": "vendor/underscore-min",
"backbone" : "vendor/backbone-min",
"marionette" : "vendor/marionette",
"app" : "app/app"
}
});
define(function(require, exports, module) {
// Make these libraries available globally
var jquery = require('jquery');
window.underscore = require('underscore');
var Backbone = require('backbone');
window.marionette = require('marionette');
// Require and start our own app
var app = require('app');
app.start();
});
This obviously stops me from having to import/require each of these core libraries into every subsequent module/component for my application. Taking my code from potentially this (app.js file):
define(function (require, exports, module) {
var jquery = require('jquery'),
underscore = require('underscore'),
Backbone = require('backbone'),
Marionette = require('marionette'),
// module specific libs
mymodule = require('../js/app/module'),
logger = require('../js/app/logger');
return {
start: function () {
var testview = new mymodule();
logger.logme();
}
}
});
To this (better app.js):
define(function (require, exports, module) {
var mymodule = require('../js/app/module'),
logger = require('../js/app/logger');
return {
start: function () {
var testview = new mymodule();
logger.logme();
}
}
});
Much cleaner IMO.
So thoughts? Criticisms? Are these going to play well together? (two already do it themselves - why not for the other two if they are core to the app).
In my head I don't think it will be a problem as long as I don't start hammering every module/component/library onto the global scope but I'm interested for someone more experienced to weigh in.
If I'm doing it wrong or there is a better way let me know!
Thanks
EDIT: After reading your comments, I realized your question has two components. One component is purely syntactical - and my original answer addresses a possible solution to that component. However, to the extent that your solution also incorporates an application design component, whereby dependencies of individual modules are defined at the application level, my answer is that is "bad practice." Dependencies should be defined at the module level (or lower) so that every element of your application is decouplable. This will be an enormous benefit in writing code that is (1) reusable, (2) testable, (3) refactorable. By defining your dependencies at the application level, you force yourself into loading the entire application simply to access a single module, for example, to run a test - and you inhibit rearranging modules or reuse of the same modules in other projects. If you do this, in the long run... you're gonna have a bad time.
Now, to the extent that your question is syntactical...
ORIGINAL ANSWER: I agree that require.js syntax is ugly as hell, annoying to use, and a potential source of difficult to debug errors. My own approach was to implement the following wrapping function (pardon the coffeescript).
# Takes dependencies in the form of a hash of arguments with the format
# { path: "name"}
# Assigns each dependency to a wrapper variable (by default "deps"),
# without the need to list each dependency as an argument of the function.
PrettyRequire = (argHash, callback) ->
deps = new Array()
args = new Array()
#loops through the passed argument, sorts into two arrays.
for propt of argHash
deps.push(propt)
args.push(argHash[propt])
#calls define using the 'dependency' array
define(deps, ()->
deps = new Array()
# assigns the resulting dependencies to properties of the deps object
for arg, i in args
deps[arg] = arguments[i]
# runs callback, passing in 'deps' object
return callback(deps)
)
This code is a simple rewrite which (1) preserves scope and (2) prettifies the syntax. I simply include the function as a part of a internal library that I maintain, and include that library at the outset of any project.
Define can then be called with the following (imho) prettier syntax:
PrettyRequire({
'backbone': 'Backbone'
'vendor/marionette': 'Marionette'
'../js/app/module': 'myModule'
}, (deps)->
Backbone.Model.extend(...) # for dependencies that assign themselves to global
deps.myModule(...) # for all dependencies (including globals)
)
That's my approach, to the extent that it's responsive to your question. It's worth noting also that your apps will take a (small) performance hit as a result of the increased overhead, but as long as you're not calling too many sub modules, it shouldn't be too much of an issue, and to me, it's worth it not to have to deal with the double syntax you describe.

Using webpack with an existing requirejs application

I am working with an existing application (canvas-lms) that uses RequireJS in its build system. I'm working on a pseudo-standalone application that plugs into Canvas (a "client_app" in Canvas parlance). This is a fontend-only app that makes API calls back to the host Canvas app. The details aren't terribly important for my question - all a client_app needs to do is have a build script that spits out a JS file in a defined place within the Canvas app tree.
I'm trying to use Webpack to build my app instead of RequireJS. Everything works great if I keep all my dependencies self-contained (e.g. npm-install everything I need); however, Canvas already provides many of these dependencies (e.g. React, jQuery), and in jQuery's case, it provides a patched version that I'd like to use instead. This is where I start to have problems.
Getting React to work was easy; Canvas installs it with bower, so I was able to add an alias in my webpack config to point at it:
alias: {
'react': __dirname + '/vendor/canvas/public/javascripts/bower/react/react-with-addons',
}
(__dirname + /vendor/canvas is a symlink in my application tree to the host Canvas application's tree)
Where I'm having trouble is trying to load the provided copy of jQuery.
Canvas has the following jQuery structure:
/public/javascripts/jquery.js:
define(['jquery.instructure_jquery_patches'], function($) {
return $;
});
/public/javascripts/jquery.instructure_jquery_patches.js:
define(['vendor/jquery-1.7.2', 'vendor/jquery.cookie'], function($) {
// does a few things to patch jquery ...
// ...
return $;
});
/public/javascripts/vendor/jquery.cookie.js -- looks like the standard jquery.cookie plugin, wrapped in an AMD define:
define(['vendor/jquery-1.7.2'], function(jQuery) {
jQuery.cookie = function(name, value, options) {
//......
};
});
and finally, /public/javascripts/vendor/jquery-1.7.2.js. Not going to paste it in, since it's bog-standard jQuery1.7.2, except that the AMD define has been made anonymous -- reverting it to the stock behaviour doesn't make a difference.
I want to be able to do something like var $ = require('jquery') or import $ from 'jquery' and have webpack do whatever magic, poorly-documented voodoo it needs to do to use jquery.instructure-jquery-patches.
I've tried adding the path to resolve.root in my webpack.config.js file:
resolve: {
extensions: ['', '.js', '.jsx'],
root: [
__dirname + '/src/js',
__dirname + '/vendor/canvas/public/javascripts'
],
alias: {
'react': 'react/addons',
'react/addons/lib': 'react/../lib'
}
},
This should mean that when I do a require('jquery'), it first finds /public/javascripts/jquery.js, which defines a module with instructure_jquery_patches as a dependency. That falls into instructure_jquery_patches, which defines a module with two dependencies ('vendor/jquery-1.7.2', 'vendor/jquery.cookie').
In my main entry point (index.js), I am importing jQuery (also tried a commonjs require, no difference), and trying to use it:
import React from 'react';
import $ from 'jquery';
$('h1').addClass('foo');
if (__DEV__) {
require('../scss/main.scss');
window.React = window.React || React;
console.log('React: ', React.version);
console.log('jQuery:', $.fn.jquery);
}
Building the bundle with webpack seems to work; there are no errors. When I try to load the page in the browser, though, I'm getting an error from within jquery.instructure-jquery-patches.js:
It would seem that jQuery is not availble within jquery.instructure-jquery-patches.
It is, however, available in the global scope after the page loads, so jQuery is being evaluated and executed.
My guess is that I'm running into some sort of requirejs/amd asynchronicity problem, but that's a shot in the dark. I don't know enough about requirejs or amd to know for sure.
TobiasK's comment pointed me at needing to add amd: { jQuery: true } to my webpack config. Everything is working now.

How can I use a local file during Require.js optimisation, but a CDN-hosted version at runtime?

My page includes several components that exist as separate AMD modules. Each of these components is turned into a single file by the Require.js optimiser. Because several of these components share dependencies (e.g. jQuery and d3), the optimiser paths config uses CDN URLs for those dependencies, rather than bundling them into the optimised file.
Here's where it gets tricky. I've written a module loader plugin for Ractive.js called rvc.js, which allows me to include Ractive components that are defined in HTML files. (Yes, I'm asking for help on how to use my own library.)
This works fine - code like this gets optimised as you'd expect:
define( function ( require ) {
var ChartView = require( 'rvc!views/Chart' );
var view = new ChartView({ el: 'chart' });
});
Because Ractive is used by several of the components, it should be served from a CDN like jQuery and d3. But it's used by rvc.js during the optimisation process, which means that the Ractive entry for the optimiser's paths config can't point to a CDN - it has to point to a local file.
Is there a way to tell Require.js 'use the local file during optimisation, but load from CDN at runtime'?
So here's the solution I eventually settled on. It feels somewhat kludgy, but it works:
Stub out the loaders and the library you don't want bundled
Add an onBuildWrite function that rewrites modules depending on the library, so that they think they're requiring something else entirely - in this case Ractive_RUNTIME
Add an entry to your runtime AMD config's paths object, so that Ractive_RUNTIME points to the CDN
My optimiser config now looks like this:
{
baseUrl: 'path/to/js/',
out: 'build/js/app.js',
name: 'app',
optimize: 'none',
paths: {
'amd-loader': 'loaders/amd-loader',
'rvc': 'loaders/rvc',
'Ractive': 'lib/Ractive'
},
stubModules: [ 'amd-loader', 'rvc', 'Ractive' ],
onBuildWrite: function ( name, path, contents ) {
if ( contents === "define('Ractive',{});" ) {
// this is the stub module, we can kill it
return '';
}
// otherwise all references to `Ractive` need replacing
return contents.replace( /['"]Ractive['"]/g, '"Ractive_RUNTIME"' );
}
}
Meanwhile, the script that loads the app.js file created by the optimiser has a config entry that points to the CDN:
require.config({
context: uniqueContext,
baseUrl: baseUrl,
paths: {
'amd-loader': 'loaders/amd-loader',
'rvc': 'loaders/rvc',
'Ractive': 'lib/Ractive',
'Ractive_RUNTIME': 'http://cdn.ractivejs.org/releases/0.3.9/Ractive.min'
}
});

Categories

Resources