Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BreezeJS and RequireJS not working as expected

I am integrating breezeJS into an existing requireJS project which already uses knockoutJS. I ran into a couple of problems.

The first was that breeze is unable to load the Q library unless i include it on my html wrapper as a <script> tag, not as a loaded AMD dependency. In my project i am trying to keep my code down to a single script tag, so this isnt ideal.

The second issue is that breezeJS is unable to load knockout. In my main.js I have defined a path for knockout to be:

knockout: '../libs/knockout/knockout-2.2.0',

(I do this because I like knowing for sure that I am not accessing a global ko)

However when i added breeze to my project, breeze wasn't able to load my knockout library. Looking into the breeze code i can see that it has been hardcoded to load the knockout library as ko.

Not wanting to change all of my code i found that i could add my AMD loaded knockout library to the global window object as window['ko']. But this feels like quite a bodge. Also weirdly adding Q this way and removing the <script> tag didn't work, as i think Q is required too early in the application's lifecycle, even before i get to pollute the global - i did nest my require() calls in main.js but that hid the majority of my application files from the build process so i abandoned that approach.

How can i include Q and knockout and breeze in my project and still use a single line <script> tag, at the moment I am having to include Q as a separate <script> tag and pollute the global to get breeze and knockout to play nicely.

I am using quite a few other libraries in my project and none of them have been this difficult to integrate in.

Any help is much appreciated

Cheers

Gav

EDIT: Here is my full require config:

require.config({
    /**
    * shims are for 3rd party libraries that have not been written in AMD format.
    * shims define AMD modules definitions that get created at runtime.
    */
    shim: {
        'jqueryUI': { deps: ['jquery'] },
        'jqueryAnimateEnhanced': { deps: ['jqueryUI'] },
        'jqueryScrollTo': { deps: ['jquery'] },
        'touchPunch': { deps: ['jquery'] },
        //'Q': { exports: 'Q' },
        //'breeze': { deps: ['Q', 'knockout'], exports: 'breeze' },
        'path': { exports: 'Path' },
        //'signalR': { deps: ['jquery'] },
    },

    paths: {
        //jquery
        jquery: '../libs/jquery/jquery-1.7.2.min',
        'jquery.adapter': '../libs/jquery/jquery.adapter',

        //jquery plugins
        horizontalNav: '../libs/jquery/plugins/horizontalNav/jquery.horizontalNav',
        jqueryUI: '../libs/jquery/plugins/jquery-ui/jquery-ui-1.9.2.custom',
        jqueryAnimateEnhanced: '../libs/jquery/plugins/animate-enhanced/jquery.animate-enhanced',
        touchPunch: '../libs/jquery/plugins/touch-punch/jquery.ui.touch-punch.min',
        //jqueryScrollTo: '../libs/jquery/plugins/jquery-scrollTo/jquery.scrollTo.min',
        //reveal: '../libs/jquery/plugins/reveal/jquery.reveal',
        //opentip: '../libs/jquery/plugins/opentip/opentip-jquery',

        //RequireJS
        domReady: '../libs/require/plugins/domReady',
        text: '../libs/require/plugins/text',
        async: '../libs/require/plugins/async',
        depend: '../libs/require/plugins/depend',
        json: '../libs/require/plugins/json',
        noext: '../libs/require/plugins/noext',

        //coffee-script
        'coffee-script': '../libs/coffee/coffee-script',
        cs: '../libs/require/plugins/cs',

        //Path
        path: '../libs/path/path.min',

        //Knockout
        knockout: '../libs/knockout/knockout-2.2.0',
        knockoutTemplateSource: '../libs/knockout/ko.templateSource',
        knockoutValidation: '../libs/knockout/ko.validation',

        //breeze
        Q: '../libs/breeze/q',
        breeze: '../libs/breeze/breeze.debug',

        //Signals (Observer pattern)
        signals: '../libs/signals/signals',

        //SignalR - Push notifications
        signalR: '../libs/signalR/jquery.signalR-0.5.2.min',

        //utils
        logger: 'common/logging/logger',
        tinycolor: '../libs/tinycolor/tinycolor',
        composing: 'common/composition/composing',

        //app specific
        BaseWidgetViewModel: 'app/view/core/BaseWidgetViewModel',

    }
});
like image 322
Gav Avatar asked Dec 18 '12 16:12

Gav


2 Answers

Sorry for the (holiday) delay in addressing your question.

I understand and appreciate your goal of eliminating every script tag except the one for RequireJS. This goal is not easily obtained in my experience.

You did uncover a Breeze defect. Breeze internally is referencing a different 'require' function than your application's 'require' function. It doesn't know about the application 'require' function or its configuration. Therefore, when you omit the Q script tag, Breeze cannot find Q ... no matter how well you configure the application 'require'.

We'll have to fix that. I'll add a comment here when we do.

Meanwhile you'll must use a script tag for 'Q' and for 'KO' and you must put those tags above the script tag for RequireJs. Please continue to use require for your application scripts.

Unfortunately, you have other problems that are unrelated to the dual-require-function problem.

First, I think you will always have trouble keeping KO out of the global namespace ... and this has nothing to do with Breeze.

In KO's AMD implementation (at least when I last looked), KO is either in the global namespace or in the require container; never both. Unfortunately, many useful plug-ins (bindingHandlers, debug.helpers) assume it is in the global namespace; you won't be able to use them if you load KO with require.

You can load Knockout in script tags before Require and then shim KO into the Require container during configuration. Your configuration might look like this:

define('knockout', [], function () { return window.ko; });

The jQuery developers realized this would be a problem. Too many good plugins assume jQuery is in the global namespace. Rather than be purists, the jQuery maintainers (correctly in my view) put jQuery in both the Require container and in the global namespace.

Second, many other useful libraries do not support Require. Your code can be written as if these scripts were designed for require. I still think the easiest, clearest way to handle this is specify them in script tags. Then you can add them into the container by defining them with define calls as shown above. With this shimming, your own modules can be consistent in relying on Require for service location and dependency injection of these libraries.

Third, I think you have a bug in your configuration. For reasons that escape me, you decided to refer to the Knockout module as "knockout" in your Require universe.

Fine ... but you shouldn't expect Breeze to anticipate that module name when we fix it to use the application's require container. Breeze will be looking for KnockoutJs under its standard name, "ko". Breeze will not find it under your preferred name, "knockout".

This is not a big deal. If you're wedded to the "knockout" name, you can make the same instance available under both names. I'm pretty sure the following shim line will work when added to your configuration code:

define('ko', ['knockout'], function (ko) { return ko; });

Again ... this will be effective only after we fix our Breeze require bug.

John Papa had to address many similar issues in Code Camper; you might look at what he did there, especially at main.js.

like image 127
Ward Avatar answered Sep 28 '22 22:09

Ward


This should be fixed as of Breeze v 1.2.4. We no longer use an internal implementation of "require".

like image 42
Jay Traband Avatar answered Sep 28 '22 21:09

Jay Traband