9

Cordova 2.4.0 and up supports AMD for loading into javascript. I am specifically looking to use Cordova 2.5.0 with the latest version of RequireJS, backbone, jquery, jquery mobile.

I have yet to see any tutorial or posting about how to use the new version of Cordova properly with with requirejs. I do see tons of posts about how to incorporate Cordova and requirejs back when Cordova did not support AMD (2.3.0 and prior).

If anyone can post or point me to a simple example of this, it would be greatly appreciated.

Edit: Specifically I am targeting development for iOS.

Edit: I want to be more specific and include the exact problem I am running into.

main.js

require.config({
    paths: {
        cordova: 'libs/cordova/cordova.ios',//cordova-2.5.0',//2.5.0*/
        jquery: 'libs/jquery/jquery.min',//1.9.1
        text: 'libs/require/text',
        domReady: 'libs/require/domReady',
        underscore: 'libs/underscore/underscore-min',
        backbone: 'libs/backbone/backbone-min',
        'jquery.mobile-config': 'libs/jquery-mobile/jqm-config',
        'jquery.mobile': 'libs/jquery-mobile/jquery.mobile.min'
    },
    shim: {
        backbone: {
            deps: ['jquery', 'underscore'],
            exports: 'Backbone'
        },
        underscore: {
            exports: '_'
        },
        'jquery.mobile': {
            deps: ['jquery', 'jquery.mobile-config']
        }
    }
});


require(['app'], function(App){
    App.initialize();
});

app.js

define([
    'cordova',
    'jquery',
    'underscore',
    'backbone',
    'router'
], function(cordova, $, _, Backbone, Router){
    var initialize = function(){
        Router.initialize();
    }
    return {
        initialize: initialize
    };
});

To be clear, everything works fine before I optimize the require.js project. When I try to optimize the project with r.js (part of require.js), the optimizer throws an error which can be seen below.

Error channel.js

I am not sure what channel.js is, and why it is asking for it, but I was able to find it on github here https://github.com/apache/cordova-js/tree/master/lib/common

Once I create the subdirectory it is looking for, I place the channel.js file into it. I no longer get that error, but now a different one, seen below.

enter image description here

I was also able to find that file on the cordova-js github site. Once I place that file into the subdirectory, I don't get any error messages and the project builds successfully.

Now when I try to run the application using the single optimized js file, I get this error in the javascript console, and just a blank white screen on the device.

"ReferenceError: Can't find variable: exports"

Here is my build.js file I am using to run the optimization

({
    baseUrl: "../js",
    mainConfigFile: "../js/main.js",
    name: "../js/main",
    out: "../js/big.js",
    preserveLicenseComments: false,
    paths: {
        requireLib: "../js/libs/require/require"
    },
    include: "requireLib"
})

Edit 4/11/13: Answer Thanks to the help of SA user asgeo1, I got my problem solved. It turns out you cannot name the variable in main.js 'cordova' because it conflicts with an internal variable named 'cordova'.

The solution is below.

main.js

require.config({
    paths: {
        'cordova.ios': 'libs/cordova/cordova.ios',//cordova-2.5.0' THIS IS CHANGED
        jquery: 'libs/jquery/jquery.min',//1.9.1
        text: 'libs/require/text',
        domReady: 'libs/require/domReady',
        underscore: 'libs/underscore/underscore-min',
        backbone: 'libs/backbone/backbone-min',
        'jquery.mobile-config': 'libs/jquery-mobile/jqm-config',
        'jquery.mobile': 'libs/jquery-mobile/jquery.mobile.min'
    },
    shim: {
        backbone: {
            deps: ['jquery', 'underscore'],
            exports: 'Backbone'
        },
        underscore: {
            exports: '_'
        },
        'jquery.mobile': {
            deps: ['jquery', 'jquery.mobile-config']
        }
    }
});


require(['app'], function(App){
    App.initialize();
});

app.js

define([
    'cordova.ios',//THIS IS CHANGED
    'jquery',
    'underscore',
    'backbone',
    'router'
], function(cordova, $, _, Backbone, Router){
    var initialize = function(){
        Router.initialize();
    }
    return {
        initialize: initialize
    };
});

This solution works and was tested with Cordova 2.5.0 and 2.6.0

njtman
  • 2,160
  • 1
  • 19
  • 32
  • I've used cordova 2.2.0 with requirejs once. Don't know if it still applies to the newer versions, but it might help you: http://stackoverflow.com/a/13955963/1916258 – asgoth Mar 19 '13 at 09:14
  • Like I mentioned in my OP, cordova 2.4.0 and up now have the ability to lazy-load with requirejs. This is specifically what I am looking to do. That being said, I tried your method and it didn't work. I am mainly having problems when I optimize the code with r.js. It throws a couple errors saying 2 .js files that cordova depend on are missing. I found those 2 files on github, and when I place them in the structure it optimizes, but the code throws errors and the app doesn't run on the device/sim. I don't experience any errors at all before optimizing the code. – njtman Mar 19 '13 at 12:34
  • Woha jtman, I guess you saved me hours of searching. Got the same issue :-) – Christopher Klewes Nov 10 '13 at 00:12
  • Your welcome! Please up vote my question to make it easier for others to find this solution! – njtman Nov 10 '13 at 14:12

2 Answers2

5

I have had this same problem too. It appears to be an issue in how they are building the cordova.ios.js file they distribute with Cordova 2.4 / 2.5

As you found out, the cordova/channel and cordova/utils sections are not in the correct order within the cordova.ios.js file.

That why RequireJS tries to load the individual files since those modules hadn't occurred yet in the cordova.ios.js file yet.

Unfortunately when you put the channel.js and utils.js files in your project, you weren't actually solving the root cause of the issue.

Sure, that allowed it to build, but the ReferenceError: Can't find variable: exports error then occurs because RequireJS will put the contents of utils.js/channel.js above the rest of the cordova.ios.js contents - which is not good because they depend on the exports stuff being setup inside of cordova.ios.js.

Not to mention you now have two copies of the channel.js/utils.js code in your built file as well...

The solution is you must modify cordova.ios.js yourself. Move the cordova/channel and cordova/utils to the top of cordova.ios.js (but after the exports/requirejs stuff is defined)

You can get one that I prepared for myself here: https://gist.github.com/asgeo1/5361227

Also, another tip - in your main.js, don't refer to cordova.ios.js as 'cordova'. That will clash, because cordova.ios.js already uses this module name internally.

Use 'cordova.ios' instead:

require.config({
    paths: {
        'cordova.ios': 'libs/cordova/cordova.ios',
        ...
asgeo1
  • 9,028
  • 6
  • 63
  • 85
  • Thank you so much for your response. I will try this in the next couple of hours. – njtman Apr 11 '13 at 12:26
  • Thank you very much, I got everything working now. I tested the fix in cordova 2.5.0 and the newest 2.6.0. All I needed to do was what you suggested at the very end of your answer. I changed 'cordova' in main.js and app.js to 'cordova.ios', and everything worked great. Because I no longer was having issues, I didn't try re-arranging the content of the cordova file like you suggested. Again, thank you so much. – njtman Apr 11 '13 at 13:12
  • 3
    No worries, the issue with parts being out of order was only in 2.4 and 2.5. Thankfully it has been fixed in 2.6 ;) – asgeo1 Apr 12 '13 at 00:26
1

To use the lazy loading, one option is to use the domReady plugin from requireJS (cf. http://requirejs.org/docs/api.html#pageload).

Once requireJS has confirmed that the DOM was ready, you can then wait for the device to be ready using the regular deviceready listener.

require(['require/domReady','cordova/cordova-ios-2.5.0'], function (domReady) {
    domReady(function () {
    console.log("The DOM is ready - waiting for the device");
    document.addEventListener("deviceready", startWhenReady, false);
    }
 });

function startWhenReady()
{
  console.log("dom and device ready : we can start...");
  // your app
}

Worked for me!

Delta
  • 198
  • 7
  • Thank you very much for your response. I will try this tomorrow. Quick question though. Were you able to optimize (and minify) your project using your method? I was only experiencing issues during and after optimizing the all the require js files. Before optimizing the code, everything worked fine. (However the way I tried doing it was slightly different than your way.) Thanks again for your response. – njtman Mar 26 '13 at 17:38
  • You're welcome. I did not experience any issue when using minified (w/ YUICompressor) versions jquery-require.js and cordova-2.5.0.js. – Delta Mar 26 '13 at 19:08
  • I think you misunderstood my comment. I am having no problems using the pre-built minified versions of the libraries either. I am having issues when I optimize (combine and uglify) my entire project using r.js which is a part of require js. Before I run the optimization, everything works fine. After I run the optimization is when I start to have problems. – njtman Mar 27 '13 at 12:26
  • I tried your solution this morning and I am still running into the same problems I had before. I updated my OP describing the issue I am having more thoroughly. – njtman Mar 27 '13 at 14:52