play 2.2 webjars requirejs jquery integration - javascript

After upgrade to play 2.2, I cannot make requirejs work as before.
It seems to me that the requirejs is not initialized or configured correctly. I tried to follow https://github.com/mariussoutier/play-angular-require-seed, but I cannot even make the simplest case to work.
jquery can be located correctly in main.js, but require(['jquery'].....) does not work in tag.
If someone could help me with this, it would be appreciated a lot.
webjars definition
libraryDependencies ++= Seq(
javaJdbc,
javaEbean,
cache,
"org.webjars" % "jquery" % "1.10.2",
"org.webjars" % "requirejs" % "2.1.1",
"org.webjars" % "webjars-play_2.10" % "2.2.0"
)
resolvers += "typesafe" at "http://repo.typesafe.com/typesafe/repo"
requireJs += "main.js"
requireJsShim += "main.js"
main.js
(function(requirejs) {
"use strict";
// -- PROD RequireJS config --
requirejs.config({
shim: {
"jquery": { exports: "$" }
},
paths: {
"jquery": ["/webjars/jquery/1.10.2/jquery.min"]
}
});
// It works fine here
require(["jquery"], function($) {
console.log($);
});
})(requirejs);
index.scala.html
#(title: String)
<html>
<body>
<script src='/lib/require.js' type='text/javascript' data-main="/assets/javascripts/main"></script>
<script type="text/javascript">
require(["jquery"], function($) {
console.log($);
});
</script>
</body>
</html>
main.js is loaded, and the error message in the console is
Uncaught TypeError: Property 'require' of object [object Object] is not a function
Edit
The key here is to understand the AMD concept. Although two snippets in tag are placed one after the other, they are not guaranteed execute in order. Therefore, when the second snippet runs, the requirejs may not be configured yet.
<script src='webjars/requirejs/2.1.8/require.js' type='text/javascript' data-main="javascripts/main"></script>
<script type='text/javascript'>
// Second snippet
require(['jquery'], function($) {return $;});
</script>
To fix this, we have two different approaches:
Include the javascript code of the second snippet in main.js (specified in data-main) and Use require(['dependencies'], function() {// do second.js})
Do not specify data-main field when including requirejs, and do all the configuration in the second part.
IMHO, the first approach is preferred and is the exact one used in the sample project https://github.com/mariussoutier/play-angular-require-seed

Qualifying my reply, definitely not an expert...
In your index.scala.html page, you're trying to use a script that's loaded asynchronously. That will give you random results, because it might very well not be loaded in time.
From the RequireJS site:
Note: the script tag
require.js generates for your data-main module includes the async
attribute. This means that you cannot assume that the load and
execution of your data-main script will finish prior to other scripts
referenced later in the same page.
For example, this arrangement will fail randomly when the
require.config path for the 'foo' module has not been set prior to it
being require()'d later:
<script data-main="scripts/main" src="scripts/require.js"></script>
<script src="scripts/other.js"></script>
// contents of main.js:
require.config({
paths: {
foo: 'libs/foo-1.1.3'
}
});
// contents of other.js:
// This code might be called before the require.config() in main.js
// has executed. When that happens, require.js will attempt to
// load 'scripts/foo.js' instead of 'scripts/libs/foo-1.1.3.js'
require( ['foo'], function( foo ) {
});
Also of note... in the example you linked to, this problem is avoided, because all the usage of "require" happens in main*.js
So what I believe you need to do, is to wrap JavaScript code contained within your second script in an event handler which is listening for an event such as window.onload or DOMContentLoaded event, to delay the execution until those events have fired (and all will be present and correct).
References:
http://requirejs.org/docs/api.html#data-main
Javascript async loading and order of execution
https://github.com/mariussoutier/play-angular-require-seed/blob/master/app/views/index.scala.html

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.

Why is my global requirejs var undefined?

I have two seperate applications. Both use jQuery an RequireJS. I want to embed application A into application B, so I transfer the required HTML to application B. Application B is an instance of edx-platform (https://github.com/edx/edx-platform).
So far so good, but the second app won't load. First I had both script tags with data-main, but it doesn't work. So I searched and I found this: https://stackoverflow.com/a/10839885
I looks good so far, but my global requirejs variable is undefined. In the HTML edx-platform calls RequireJS with this code:
<script>
window.baseUrl = "/static/";
(function (require) {
require.config({
baseUrl: window.baseUrl
});
}).call(this, require || RequireJS.require);
</script>
<script type="text/javascript" src="/static/lms/js/require-config.js"></script>
<script type="text/javascript">
(function (require) {
require.config({
paths: {
'js/courseware/courseware_factory': 'js/courseware/courseware_factory',
'draggabilly': 'js/vendor/draggabilly',
'js/courseware/toggle_element_visibility': 'js/courseware/toggle_element_visibility',
'js/courseware/course_home_events': 'js/courseware/course_home_events',
'js/courseware/link_clicked_events': 'js/courseware/link_clicked_events',
'moment': 'common/js/vendor/moment-with-locales',
'moment-timezone': 'common/js/vendor/moment-timezone-with-data',
'js/student_account/logistration_factory': 'js/student_account/logistration_factory',
'js/groups/views/cohorts_dashboard_factory': 'js/groups/views/cohorts_dashboard_factory',
'js/dateutil_factory': 'js/dateutil_factory',
'js/courseware/accordion_events': 'js/courseware/accordion_events',
'js/bookmarks/views/bookmark_button': 'js/bookmarks/views/bookmark_button',
'js/views/message_banner': 'js/views/message_banner',
'js/student_profile/views/learner_profile_factory': 'js/student_profile/views/learner_profile_factory'
}
});
}).call(this, require || RequireJS.require);
</script>
My code is included later. I tried to run a simple console.log(requirejs) but it didn't work. requirejs is also undefined when calling it from the developer console.
OK I found out why it is undefined. edX also loads the file which undefined all of those variables.
Now I need to find a way to get my code running.

require.js, am I doing it right?

I'm new to require.js and found the documentation quite hard to understand. After a while I got my project up and running with the following setup.
project
|
|--js
|--vendor
|--require.js
|--modernizr.js
|--jquery.js
|--modules
|--module1.js
|--module2.js
|--main.js
|--index.html
index.html:
<!DOCTYPE html>
<html>
<head></head>
<body>
<script data-main="/js/main.js" src="/js/vendor/require.js"></script>
</body>
</html>
main.js
define([
"vendor/modernizr",
"modules/module1",
"modules/module2"
]);
modules/module1.js
define(['vendor/jquery'], function () {
// Some module code like
$('#button').on('click', function(){});
});
Is this a good setup if I like to have one import file (main.js) like in a less setup for CSS?
This setup is generally in the right direction, just some slight modifications need to be made.
For the modules, you need to pass in a matching number of arguments for each dependency of the module. In this case, you would want to assign $ to what is returned by the jQuery module, so that you can actually use $ within the module:
define(['vendor/jquery'], function ($) {
// Some module code like
$('#button').on('click', function(){});
});
For main.js, the define() call should be a require() call so that you will be executing whatever's in the module instead of simply registering it a module for some other module to execute:
require([
"vendor/modernizr",
"modules/module1",
"modules/module2"
], function(Modernizr, module1, module2) {
// do something with Modernizr, module1, module2
});

Adding RequireJS module that uses jquery on a page that already has jquery as a global

I have an add-on to an application (call it appX) that allows users to create their own customizations using javascript, css and appX's webservices api.
Usually customizations are small and do not involve a lot of external libraries/plugins but when they do have external libs the typical users' library of choice is jQuery.
In the next version of appX they are using jQuery natively which I know is going to break some of the customizations.
So I have a need to modularize this situation. I have some other problems that are coming up and RequireJS seems like a good solution to these issues. I just need to figure out how to apply RequireJS properly for this situation
In my POC I'm loading require.js as follows:
<!--A bunch of other script including jQuery (but not require) are loaded already -->
<script type="text/javascript" src="/custom/js/require.js"></script>
<script type="text/javascript" src="/custom/js/dostuff.js"></script>
We'll call the jQuery loaded with appX jqueryx and the one I want to load jqueryp (p for private)
jQuery utilizes AMD and by default uses this definition internally:
define( "jquery", [], function () { return jQuery; } );
But in this case RequireJS is loaded AFTER jQuery (jqueryx) so there will be no default 'jquery' definition correct?
Some more background before I show you my problem... the file structure is like this:
appx
/js:
jqueryx.js
other.js
appx
/custom/js:
jqueryp.js
dostuff.js
Looking at the RequireJS api it seems that I should be doing something like this:
require.config({
baseUrl : 'custom/js',
paths : { 'jquery' : 'jqueryp'},
map: {
'*': { 'jquery': 'jquery-private' },
'jquery-private': { 'jquery': 'jquery' }
}
});
define(['jquery'], function (jq) {
return jq.noConflict( true );
});
require(['jquery'], function(jq){
console.log(jq.fn.jquery);
});
But when I do this I get an error:
Mismatched anonymous define() module: function (jq)...
I've played around with switching references to jquery, jquery-private as it's kind of confusing but with no progress.
How should I be applying RequireJS to this situation?
Almost a year late but better than no answer at all...
The following part should be moved into a "jquery-private.js" file:
define(['jquery'], function (jq) {
return jq.noConflict( true );
});
You can't define a module in your main entry point. Even if you could, the module has no name so how would you reference it?

Any way to do conditional including in javascript?

We're developing a portal with lots of portlets (independent application within the page/portal). Each portlets have to be independent : They have to be able to run on stand-alone page from within the portal.
We've been ask not to add tons of javascript files to the portal base-page (the one that calls everything). It also comes with dojo (but no one uses it).
Are there any way to load javascript files (including jQuery aka, it can't be the solution) if they are not loaded yet? The answer can use dojo
Right now we though of
if (!window.jQuery) {
document.write('<script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"><' + '/script>');
}
if (!window.jQuery.ui) {
document.write('<script src="/Scripts/jquery-ui-1.8.11.min.js" type="text/javascript"></scr' + 'ipt>');
}
[...] other includes
The problem with this is that jquery isn't loaded when the jQuery.ui test is done, so an error is thrown and the 2nd file is not loaded.
Edit
Re-writing the issue : The problem is that we could have 4 portlets, each requiring jQuery + jQuery-ui + differents others plugins/files. So they need to all include code to load all those files independantly. But we don't want to load jQuery and jQuery-ui 4 times either.
The solution to this seems to be to use separate script blocks. Apparently the document.write will not effect the loading of the scripts, until the script block closes.
That is, try this:
<script>
if (!window.jQuery) {
document.write('<script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"><' + '/script>');
}
</script>
<script>
if (!window.jQuery.ui) {
document.write('<script src="/Scripts/jquery-ui-1.8.11.min.js" type="text/javascript"></scr' + 'ipt>');
}
</script>
Works for me. Tested in IE and Firefox.
Misread the question slightly (can and can't look very similar).
If you're willing to use another library to handle it, there are some good answers here.
loading js files and other dependent js files asynchronously
I've always injected js files via js DOM manipulation
if (typeof jQuery == 'undefined') {
var DOMHead = document.getElementsByTagName("head")[0];
var DOMScript = document.createElement("script");
DOMScript.type = "text/javascript";
DOMScript.src = "http://code.jquery.com/jquery.min.js";
DOMHead.appendChild(DOMScript);
}
but it's a bit picky and may not work in all situations
Just write your own modules (in Dojo format, which since version 1.6 has now switched to the standard AMD async-load format) and dojo.require (or require) them whenver a portlet is loaded.
The good thing about this is that a module will always only load once (even when a portlet type is loaded multiple times), and only at the first instance it is needed -- dojo.require (or require) always first checks if a module is already loaded and will do nothing if it is. In additional, Dojo makes sure that all dependencies are also automatically loaded and executed before the module. You can have a very complex dependency tree and let Dojo do everything for you without you lifting a finger.
This is very standard Dojo infrastructure. Then entire Dojo toolkit is built on top of it, and you can use it to build your own modules as well. In fact, Dojo encourages you to break your app down into manageable chunks (my opinion is the smaller the better) and dynamically load them when necessary. Also, leverage class hierachies and mixins support. There are a lot of Dojo intrastructure provided to enable you to do just that.
You should also organize your classes/modules by namespaces for maximal manageability. In my opinion, this type of huge enterprise-level web apps is where Dojo truely shines with respect to other libraries like jQuery. You don't usually need such infrastructure for a few quick-and-dirty web pages with some animations, but you really appreciate it when you're building complicated and huge apps.
For example, pre-1.6 style:
portletA.js:
dojo.provide("myNameSpace.portletA.class1");
dojo.declare("myNameSpace.portletA.class1", myNameSpace.portletBase.baseClass, function() { ...
});
main.js:
dojo.require("myNameSpace.portletA.class1");
var myClass1 = new myNameSpace.portletA.class1(/* Arguments */);
Post-1.6 style:
portletA.js:
define("myNameSpace/portletA/class1", "myNameSpace/portletBase/baseClass", function(baseClass) { ...
return dojo.declare(baseClass, function() {
});
});
main.js:
var class1 = require("myNameSpace/portletA/class1");
var myClass1 = new class1(/* Arguments */);
Pyramid is a dependency library that can handle this situation well. Basically, you can define you dependencies(in this case, javascript libraries) in a dependencyLoader.js file and then use Pyramid to load the appropriate dependencies. Note that it only loads the dependencies once (so you don't have to worry about duplicates). You can maintain your dependencies in a single file and then load them dynamically as required. Here is some example code.
File: dependencyLoader.js
//Set up file dependencies
Pyramid.newDependency({
name: 'standard',
files: [
'standardResources/jquery.1.6.1.min.js'
//other standard libraries
]
});
Pyramid.newDependency({
name:'core',
files: [
'styles.css',
'customStyles.css',
'applyStyles.js',
'core.js'
],
dependencies: ['standard']
});
Pyramid.newDependency({
name:'portal1',
files: [
'portal1.js',
'portal1.css'
],
dependencies: ['core']
});
Pyramid.newDependency({
name:'portal2',
files: [
'portal2.js',
'portal2.css'
],
dependencies: ['core']
});
Html Files
<head>
<script src="standardResources/pyramid-1.0.1.js"></script>
<script src="dependencyLoader.js"></script>
</head>
...
<script type="text/javascript">
Pyramid.load('portal1');
</script>
...
<script type="text/javascript">
Pyramid.load('portal2');
</script>
So shared files only get loaded once. And you can choose how you load your dependencies. You can also just define a further dependency group such as
Pyramid.newDependency({
name:'loadAll',
dependencies: ['portal1','portal2']
});
And in your html, just load the dependencies all at once.
<head>
<script src="standardResources/pyramid-1.0.1.js"></script>
<script src="dependencyLoader.js"></script>
<script type="text/javascript">
Pyramid.load('loadAll');
</script>
</head>
Some other features that might also help is that it can handle other file types (like css) and also can combine your separate development files into a single file when ready for a release. Check out the details here - Pyramid Docs
note: I am biased since I worked on Pyramid.

Categories

Resources