Using ScriptSharp with Knockout.Mapping through RequireJS
Asked Answered
L

2

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']
    }
});
Loathe answered 10/7, 2013 at 15:40 Comment(1)
please also post your require.config sectionGuaiacol
G
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'}
    }
});
Guaiacol answered 10/7, 2013 at 22:29 Comment(6)
Thanks. looking promising... but [window.ko.mapping = m;] never gets called. And no exceptions are thrown. Any ideas?Loathe
@Loathe looks like my syntax was wrong on the map section. Please try the above edited version.Guaiacol
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. ;)Loathe
@Loathe in which file did you add this? Was any of the map stuff required?Guaiacol
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.Loathe
@Loathe 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 appGuaiacol
F
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.

Felipa answered 14/7, 2013 at 0:5 Comment(2)
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?Guaiacol
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.Loathe

© 2022 - 2024 — McMap. All rights reserved.