2

I'd like to fetch some html from another domain with require.js. I know that CORS policies doesn't allow this easily. Note: I have configured the web server (with Access-Control-Allow-Origin "*" and other directives) and require.js so far that all JS and CSS files (css with require-css plugin) gets loaded from the other domain as expected - just fetching html makes problems. But in the browser network protocol I can see that the html content even gets loaded. However, this content does not get passed to the require function! The browser gets the content, but require.js doesn't provide it as an parameter...

My configuration:

requirejs.config({
    baseUrl: "http://some.other.domain/",
    paths: {
        jquery:       'ext/jquery/jquery.min',
        htmlTemplate: 'test.html?', 
        siteCss:      '../css/site'
    },
    shim: {
        htmlTemplate: [
            'css!siteCss'
        ]
    },

    config: {
        text: {                
            useXhr: function (url, protocol, hostname, port) {
                return true;
            }
        }
    },
    map: {
        '*': {
            text: 'ext/require/text',
            css:  'ext/require/css.min'
        }
    }
});


require(['text!htmlTemplate'], function (htmlTemplate) {
    console.log(htmlTemplate); // prints 'undefined' into the console
});

Two notes: The useXhr configuration is taken from require.js text plugin adds “.js” to the file name but it makes no difference if it is there or not. I appended a ? to htmlTemplate path. With this the .js does not get appended to the URL and the browser loads the html content - as said before, unfortunately, without that require.js is passing it to parameter htmlTemplate.

What can I do? I've read that if I use the require.js optimizer the generated file wouldn't have this problem anymore (however that works...). But I need to develop my JS without optimization on every edit.

Update: Found one solution but I'd be happy if anyone can provide the 'right' solution.

Community
  • 1
  • 1
robsch
  • 9,358
  • 9
  • 63
  • 104
  • @Louis I've update the question. It's now `text!htmlTemplate` in the require call. If I've got it right in my reply I could ignore the text plugin completely since I do not download text anymore, rather it's a JS module. So I'm wondering why the doc doesn't say that `text!` could be removed... Confusing.... – robsch Jul 13 '16 at 13:34
  • It is unclear to me why the documentation is written in this way. The only hypothesis I have is that it was written for maximum flexibility. If you have `text!foo` and you work with multiple configurations where `foo.html` could be local or `foo.html` could be on another server, you don't have to modify a call like `require(['text!foo.html'])`. The only thing that changes is the configuration you give to RequireJS and how you respond to the `GET` request (either with the HTML directly, or HTML wrapped in a module). – Louis Jul 13 '16 at 13:40
  • @Louis I think I've found my problem. Added another [answer](http://stackoverflow.com/a/38369034/57091). – robsch Jul 14 '16 at 08:46

3 Answers3

6

I've found the actual problem! This part:

config: {
    text: {                
        useXhr: function (url, protocol, hostname, port) {
            return true;
        }
    }
},

should really do it. However, I found out that it wasn't called at all. Instead, the default implementation was called. And this returned false.

To make it work it is necessary to have the right keys in the config section since the mapping doesn't seem to be evaluated for it.

So this is the right configuration that fetches HTML from the other domain:

requirejs.config({
    baseUrl: "http://some.other.domain/",
    paths: {
        jquery:       'ext/jquery/jquery.min',
        htmlTemplate: 'test.html', //                     // ---> removed the '?'
        siteCss:      '../css/site'
    },
    shim: {
        htmlTemplate: [
            'css!siteCss'
        ]
    },

    config: {
        'ext/require/text': {                              // ---> full path is required!!!               
            useXhr: function (url, protocol, hostname, port) {
                return true;
            }
        }
    },
    map: {
        '*': {
            text: 'ext/require/text',
            css:  'ext/require/css.min'
        }
    }
});


require(['text!htmlTemplate'], function (htmlTemplate) {
    console.log(htmlTemplate);   // now prints HTML into the console!!!
});

Hallelujah!

Found the right hint here. Another option might be to set the path for text. At least the configuration must be set somehow so that the function gets called...

robsch
  • 9,358
  • 9
  • 63
  • 104
  • 1
    Ah, yes indeed. The names of the `config` part must match the module names being loaded. Your map says "in all modules, when the module `text` is requested load the module `ext/require/text` instead". My recommendation is to drop `map` and use `paths` instead to map `text` and `css` to the paths of the two modules and change `config` back to use `text`. Map is really used to work around issues caused by 3rd party, which does not seem a need here. But yes, what you have here is a fix. – Louis Jul 14 '16 at 08:55
  • @Louis Thank you for this hint! – robsch Jul 14 '16 at 08:56
  • There is also a question about [map vs. paths](http://stackoverflow.com/q/19216580/57091). – robsch Jul 14 '16 at 09:18
0

I think I've found a solution. Doc of requirejs/text:

So if the text plugin determines that the request for the resource is on another domain, it will try to access a ".js" version of the resource by using a script tag. Script tag GET requests are allowed across domains. The .js version of the resource should just be a script with a define() call in it that returns a string for the module value.

Because of that I changed the configuration to this, so text is not used anymore:

requirejs.config({
    baseUrl: "http://some.other.domain/",
    paths:   {
        jquery:       'ext/jquery/jquery.min',
        htmlTemplate: 'test.html', // removed the '?'
        siteCss:      '../css/site'
    },
    shim:    {
        htmlTemplate: [
            'css!siteCss'
        ]
    },
    map:     {
        '*': {
            // removed the text plugin
            css:  'ext/require/css.min'
        }
    }
    // removed the useXhr configuration for the text plugin
});


require(['htmlTemplate'], function (htmlTemplate) {
    console.log(htmlTemplate); // prints '<div>Here I am!</div>' into the console
});

Now http://some.other.domain/test.html.js gets loaded. The content of test.html is:

define(function () {
    return '<div>Here I am!</div>';
});

So I surrounded the HTML with a little bit of JS - no problem to me. And now htmlTemplate is set to the expected string. It's not pure HTML anymore, but since it is a fixed template (i.e. not generated) it may be acceptable.

robsch
  • 9,358
  • 9
  • 63
  • 104
0

I am getting No 'Access-Control-Allow-Origin' header is present on the requested resource. after adding the code

config: {
        'ext/require/text': {                             // required!!!               
            useXhr: function (url, protocol, hostname, port) {
                return true;
            }
        }
    },
Matthew Schinckel
  • 35,041
  • 6
  • 86
  • 121
Ngen CMS
  • 146
  • 1
  • 6