1

I'm struggling with all the Script# Dependency loading.

I have a Script# Project referencing the knockout library. Which I got to work after some time with RequireJS.

Now I'd like to use KnockoutJS mapping which complies to something like

var model = ko.mapping.fromJS(data, {}, new ViewModel());

However ko.mapping is undefined.

If I manually (for testing only) change the compiled .js file to include mapping like this:

define('MyApp',
    ['ss', 'jquery', 'knockout', knockout.mapping],
    function (ss, $, ko, mapping) { /*...*/ }
);

'mapping' is defined, but not as 'ko.mapping', which is how the compiler references it.

Any ideas?

This is my config:

requirejs.config({
    paths: {
        'jquery': 'jquery-1.9.1',
        'jqueryValidation': 'jquery.validate',
        'knockout': 'knockout-2.2.0',
        'knockout.mapping': 'knockout.mapping-latest.debug',
        'modernizr': 'modernizr-2.6.2'
    },
    shim: {
        'jqueryValidation': ['jquery'],
        'jquery.validate.unobtrusive': ['jquery', 'jqueryValidation'],
        'jquery.unobtrusive-ajax': ['jquery'],
        'knockout.mapping': ['knockout']
    }
});
Sam7
  • 3,382
  • 2
  • 34
  • 57

2 Answers2

4

It sounds like Script# is assuming that ko and ko.mapping are in the global namespace, not loaded as AMD. BUT, Knockout and Knockout.mapping are coded such that when they detect AMD/RequireJS, they do not use the global namespace.

A couple options to work around this:

1 - Inject it right after require.config is called (based on comments below) rather than waiting for something to actually request knockout or knockout.mapping

requirejs.config({
    // same as original
});

require(["knockout", "knockout.mapping"], function (ko, m) {       
    ko.mapping = m; 
})

2 - create your own wrapper module to inject it back into global. Something like this:

define('knockout.inject', ['knockout'], function(k)
{
  window.ko = k; // make a ko global
  return k; // but also return what a normal AMD require expects
});

define('knockout.mapping.inject', ['knockout.mapping'], function(m)
{
  window.ko.mapping = m; // make a ko.mapping global
  return m; // but also return what a normal AMD require expects
});

THEN, you can make a RequireJS map configuration so that whenever you request 'knockout' or 'knockout.mapping', they get transparently remapped to your above wrappers.

requirejs.config({
    paths: { // same as original },
    shim: { // same as original },
    map: {
      '*': {
        'knockout': 'knockout.inject',
        'knockout.mapping': 'knockout.mapping.inject'
      },
      // prevent cycles
      'knockout.inject': {'knockout': 'knockout'},
      'knockout.mapping.inject': {'knockout.mapping': 'knockout.mapping'}
    }
});
explunit
  • 18,967
  • 6
  • 69
  • 94
  • Thanks. looking promising... but [window.ko.mapping = m;] never gets called. And no exceptions are thrown. Any ideas? – Sam7 Jul 11 '13 at 07:27
  • @Sam7 looks like my syntax was wrong on the `map` section. Please try the above edited version. – explunit Jul 15 '13 at 18:52
  • thanks. I already tried that, but that wasn't it. I figured out that just adding this line of code will do the trick require(["knockout", "knockout.mapping"], function (ko, m) { ko.mapping = m; }); so if you want to adjust you're answer I'm happy to make it the solution. ;) – Sam7 Jul 16 '13 at 13:08
  • @Sam7 in which file did you add this? Was **any** of the `map` stuff required? – explunit Jul 16 '13 at 13:43
  • No, non of the map stuff was required, nore the 'define' stuff. Instead I appended it to the Require config file, right at the end. It's been fine in my dev environment. Not sure whether I might run into 'race-condition-issues' later though. – Sam7 Jul 16 '13 at 14:42
  • @Sam7 updated to add your approach. Should not be a race condition as long as you follow the rules about having [a single entry point for calling require.config and then kicking off the rest of the app](http://stackoverflow.com/a/17044595/151212) – explunit Jul 16 '13 at 14:53
0

This sample (https://github.com/nikhilk/scriptsharp/tree/cc/samples/KOWorld) shows using script# + knockout along with requirejs as the AMD loader.

Be sure to see the script template in AssemblyInfo.js to make all this work.

Hopefully this will help and work.

Nikhil Kothari
  • 5,215
  • 2
  • 22
  • 28
  • Please consider posting the key portions of the configuration here, in case the linked sample ever evolves into something else. Basically you're suggesting a direct script tag reference to Knockout rather than loading it through RequireJS? – explunit Jul 15 '13 at 19:01
  • Thanks Nik. I've already studied the KOWorld sample in detail, otherwise I would have never iven gotten here ;) But in your sample there no mapping is being used. Even though the .net wrapper shows the mapping classes / methods, it doesn't include a 'require' to the knockout.mapping library. – Sam7 Jul 16 '13 at 13:05