Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Passing in literals at wire time #42

Closed
Worsh opened this issue Jun 26, 2012 · 18 comments
Closed

Passing in literals at wire time #42

Worsh opened this issue Jun 26, 2012 · 18 comments

Comments

@Worsh
Copy link

Worsh commented Jun 26, 2012

I'm trying to put together a project using wire.js at the moment, and I'm at the situation where I need to be able to pass some literals (mainly DOM IDs) into the loaded spec.

The scenario is that the JS app being constructed by Wire.js is an application that can be embedded on other people's sites. As such, the name of DOM elements be referenced will be different each time.

I'd like to keep my application code clean and not include any global variable references, I'd like to pass everything in via the proxy wrapper that sits between the site and the application code (and which sets up Curl, calls wire.js, etc)

The code for retrieving the IDs of the DOM elements already exists in the wrapper code, I just need some way of getting that into the spec.

Any way of constructing a child context and passing it in as a string rather than having it loaded by the AMD loader?

@Worsh
Copy link
Author

Worsh commented Jun 26, 2012

What I really want to be able to do in my wrapper code is...

var config = "define({myLiteral:'abcd', realSpec:{wire: 'myapp'}});";

curl(['wire!'+config]);

And then reference myLiteral in myapp...

@briancavalier
Copy link
Member

Hey @Worsh,

The usual way to handle this kind of thing is to select the actual nodes in a parent context using the any of the various DOM resolvers (e.g. dom!, dom.first!, etc.), and then they will be visible to any child contexts. You can even do this in the root context, which is the ultimate ancestor to all other contexts, so the nodes will be visible basically everywhere.

Here's a quick example that shows using the root context.

If using the root context isn't quite the right fit, the general approach of selecting the nodes in a parent so they are visible in a child may still be the right pattern: The child can be your main spec, and a parent can have the responsibility of supplying some dependencies, in this case nodes, to the main spec.

There are other ways that may work, too, like using wire programmatically, so if the parent/child approach doesn't work for your architecture, we can try to work through another approach.

@Worsh
Copy link
Author

Worsh commented Jun 26, 2012

Hi Brian,

That's looks pretty close to what I need! Let me take a stab at it and come back.

@Worsh
Copy link
Author

Worsh commented Jun 28, 2012

Ok so the dom referencing in the root context doesn't seem to work for me (and stops everything from loading after wire is loaded).

Using this code to load everything in...

curl = {
    baseUrl: myBaseURL,
    pluginPath:myBaseURL,
    paths: {
        wire:      'wire',
        config:    'js/config',
        foo:         'js/foo'
    }
};

window.wire = {
    dispatcher: {$ref: 'dom!FooContainer_'+this.fooID},
    initialHeight: this.iVHeight,
    initialWidth: this.iVWidth,
    plugins: [{module: 'wire/dom.js'}]
};

var curlLoaded = function() {
    curl(['wire/wire.js!config/fooConfig.js'], function(context){console.log(context);});
};

myHelperScript.getScript(['curl.curl.js'], curlLoaded);

Where the dispatcher is what I'm trying to inject from the DOM (and has the id being tagged on which is the part that differs everytime.

The config looks like

define({
    itsMyFoo: {
        create: {
            module: foo/MyFoo.js',
            args: [
                {$ref: 'initialHeight'},
                {$ref: 'initialWidth'},
                {$ref: 'dispatcher'}
            ]
        },
        init: {foobar: {}}
    }
});

Which is just injecting those three variables into the MyFoo object, which looks like...

define([], function() { 
    function MyFoo(height, width, dispatcher) { // constructor
        // private variables
        var _height = height;
        var _width = width;
        var _dispatcher = dispatcher;

        alert("Height: " + _height + " Width: " + _width + " Dispatcher: " + _dispatcher);
    };

    MyFoo.prototype = {
        // variables

        //functions
        foobar: function() {
            alert("init");
        }
    };

    return MyFoo;
});

If I remove the dispatcher part is the code path, everything loads, and I see the alerts, so something is breaking by trying to parse that DOM element.

@briancavalier
Copy link
Member

I'm swamped today, but I do see a few things in those code snippets that seem odd. I'll try to write more of a response later today.

@Worsh
Copy link
Author

Worsh commented Jun 28, 2012

Cheers @brian!

@briancavalier
Copy link
Member

Ok, done being swamped :) I have a few questions and suggestions based on the code you pasted, so maybe we can talk through those so I can understand the situation a bit better, and we can figure out what's going on with the DOM selector.

First, I've come to feel that using an AMD package config is usually a better than paths for integrating libs like wire.js, which themselves are composed of multiple modules, into a project. For example, here's an example AMD config from an as-yet-unreleased cujo quickstart/boilerplate we're working on. Configuring wire.js that way allows you to simply reference it as "wire" rather than "wire/wire".

Also, when using AMD modules, you don't need to add .js to module ids. For example, when calling curl in curlLoaded, dropping the .js and using a package config, you can do:

var curlLoaded = function() {
    curl(['wire!config/fooConfig'], function(context){console.log(context);});
};

Similarly, in a wire spec, you can can drop the .js since wire uses the AMD loader to load modules. For example, you can include the wire/dom plugin this way:

    plugins: [{module: 'wire/dom'}]

Ok, window.wire. A few questions:

  1. What is this at the point where the window.wire assignment happens? There's nothing special about that assignment, it'll execute just like any other Javascript. The wire magic happens later in the call to curl([wire!...]). So, maybe verify that the string concatenation 'dom!FooContainer_'+this.fooID is forming the string you think it should be. Also, check to see that this.iVHeight and this.iVWidth are what you think they should be.
  2. Assuming they are ... let's say this.fooID === 123. In that case, when curl([wire!...]) executes, wire will attempt to resolve {$ref: 'dom!FooContainer_'+this.fooID} to a DOM node whose id is "FooContainer_123" ... e.g. <div id="FooContainer_123">. That DOM Node must exist at wiring time, or else the DOM resolver won't be able to find it.

The other thing you can do initially is to add the wire/debug plugin. It will spit out lots of information about what wire is doing, and will be much louder about errors. Simply include the wire/debug module in any wire spec you want to debug. For example, you can add it to the root spec:

window.wire = {
    dispatcher: {$ref: 'dom!FooContainer_'+this.fooID},
    initialHeight: this.iVHeight,
    initialWidth: this.iVWidth,
    plugins: [
        {module: 'wire/dom'},
        {module: 'wire/debug'}

        // Or for full verbosity fire hose:
        // {module: 'wire/debug', verbose: true}

        // It can even trace method calls on your wired components in real time:
        // {module: 'wire/debug', trace: true}

    ]
};

You could also add it to your config/fooConfig wire spec if you want to debug that, too. See the wiki for even more info on using wire/debug

Hopefully that will provide some useful information that helps us see what is going on. Feel free to paste any errors or other interesting output here or put it in a gist if you think it might be helpful in figuring out what is going on, and I'll be happy to take a look.

@Worsh
Copy link
Author

Worsh commented Jun 29, 2012

Configuring wire.js that way allows you to simply reference it as "wire" rather than "wire/wire".

I'll look into that approach as I was having to make a few changes to support the paths.

Also, when using AMD modules, you don't need to add .js to module ids.

It might be an artifact of how I was doing this, but the calls were definitely missing .js when I left them out (I think it's partly because I'm not using absolute paths in the URLs)

What is this at the point where the window.wire assignment happens? There's nothing special about that assignment, it'll execute just like any other Javascript.

Just a random JS object, it's part of the wrapper that injects the app onto the page.

So, maybe verify that the string concatenation 'dom!FooContainer_'+this.fooID is forming the string you think it should be. Also, check to see that this.iVHeight and this.iVWidth are what you think they should be.

It is (I use the same string as part of a JQuery expression directly above this to inject something else into a div), and they are (when I take out the dispatcher part, the alert from the constructor contains the right values)

That DOM Node must exist at wiring time, or else the DOM resolver won't be able to find it.

Yup, its created much earlier in this process, way before I even get to this injection point.

The other thing you can do initially is to add the wire/debug plugin

I tried and it stopped everything from working. It loaded up the debug.js and the aop.js files and then never loaded the fooConfig... Without putting a single line onto the console. It isn't relying on a function called require being available is it? I didn't follow that part of the hello world, and just used curl.

I'll try clear up the curl part so that everything looks like you think it should from that perspective, but everything (bar debug) loads, its just that dom expression (which I know exists as I use it in JQuery directly above this).

@briancavalier
Copy link
Member

Hmmm, since selecting dom nodes and using wire/debug are basic things that are known to work without any problems, my guess is that this is some sort of configuration problem. Have you tried using packages yet?

The best thing might be for you could put together a minimal example that reproduces the problem, and email me a zip file, or post it somewhere so I can take a look. I'd be happy to take a look at it over the weekend.

@briancavalier
Copy link
Member

Hey @Worsh, just checking to see how things are going. Any luck? Have you managed to narrow it down to a smaller test case?

@ethern8
Copy link

ethern8 commented Aug 3, 2012

@Worsh Just to clarify on:

It might be an artifact of how I was doing this, but the calls were definitely missing .js when I left them out (I think it's partly because I'm not using absolute paths in the URLs)"

I noticed that there is a 'checkToAddJsExt' function on line 504 in the curl.js source. The comment within the function says:

"don't add extension if a ? is found in the url (query params)"

So since you are using a service to serve static files (i.e. http://foo.com/staticService?file=/curl/src/curl.js) you probably have to add the .js.

However, I don't think that helps you because dependencies get loaded for you and will omit the .js, and give you a 404. For example a simple package declaration in a config like this:

packages: [
            {name:'curl',           location:'cujo/curl/src',   main:'curl.js'},
            {name:'wire/domReady',  location:'cujo/wire',       main:'domReady.js'},
            {name:'wire',           location:'cujo/wire',       main:'wire.js'},
            {name:'when',           location:'cujo/when',       main:'when.js'},
            {name:'aop',            location:'cujo/aop',        main:'aop.js'},
]

... will give you a 404 when /cujo/wire/base is loaded.

@briancavalier Any suggestions for overriding or getting around the 'checkToAddJsExt' function?

@briancavalier
Copy link
Member

@ethern8 ah, yes, interesting. @ethern8 or @Worsh: Can you provide more of your curl config (especially baseUrl)? That'd be helpful in seeing how modules are being loaded.

Loading from service like that isn't something I've dealt with, but @unscriptable may have dealt with it--he's the resident cujojs AMD loading expert, so may have some ideas.

@ethern8
Copy link

ethern8 commented Aug 5, 2012

Here is pretty much what the curl config and wire spec look like:

// curl config:
config:{
    baseUrl: 'http://localhost:8080/ProjectName/staticService?clientId=12345678&file=',
    pluginPath: 'http://localhost:8080/ProjectName/staticService?clientId=12345678&file=',
    packages: [
        {name:'curl',           location:'cujo/curl/src',   main:'curl'},
        {name:'wire/domReady',  location:'cujo/wire',       main:'domReady'},
        {name:'wire',           location:'cujo/wire',       main:'wire'},
        {name:'when',           location:'cujo/when',       main:'when'},
        {name:'aop',            location:'cujo/aop',        main:'aop'},
    ]
}

As you can see from above the static service serves files with a clientId then a file location. Then later in the file the call to curl is made:

// configure the wire rootSpec
window.wire = {
    dispatcher: {$ref: 'dom!FooContainer_'+this.fooID},
    initialHeight: this.iVHeight,
    initialWidth: this.iVWidth,

        plugins:[
            { module: 'cujo/wire/dom' },
            { module: 'cujo/wire/debug' }
         ]
}

//  
curl(this.config, ['wire!feature/js/config/SuperRadSpec']).then(
        function(){
            console.log("mods: ", arguments);
        },
        function(e){
            console.log("error:", error);
        }
);

I get a successful load of all the modules in the config, including the debug plugin because i get the following outout:

Resource interpreted as Script but transferred with MIME type text/plain: "http://localhost:8080/ProjectName/staticService?clientId=12345678&file=cujo/curl/src/curl.js". staticService:551
Resource interpreted as Script but transferred with MIME type text/plain: "http://localhost:8080/ProjectName/staticService?clientId=12345678&file=/cujo/wire/wire.js". staticService:551
Resource interpreted as Script but transferred with MIME type text/plain: "http://localhost:8080/ProjectName/staticService?clientId=12345678&file=/cujo/when/when.js". staticService:551
Resource interpreted as Script but transferred with MIME type text/plain: "http://localhost:8080/ProjectName/staticService?clientId=12345678&file=/cujo/wire/base.js". staticService:551
Resource interpreted as Script but transferred with MIME type text/plain: "http://localhost:8080/ProjectName/staticService?clientId=12345678&file=/cujo/wire/dom.js". staticService:551
Resource interpreted as Script but transferred with MIME type text/plain: "http://localhost:8080/ProjectName/staticService?clientId=12345678&file=/cujo/wire/debug.js". staticService:551
Resource interpreted as Script but transferred with MIME type text/plain: "http://localhost:8080/ProjectName/staticService?clientId=12345678&file=/cujo/wire/plugin-base/dom.js". staticService:551
Resource interpreted as Script but transferred with MIME type text/plain: "http://localhost:8080/ProjectName/staticService?clientId=12345678&file=/cujo/aop/aop.js". staticService:551
Resource interpreted as Script but transferred with MIME type text/plain: "http://localhost:8080/ProjectName/staticService?clientId=12345678&file=/cujo/wire/domReady.js". staticService:551

DEBUG (total: 0ms, context: 0ms / 0ms): Context init staticService:363
DEBUG (total: 23ms, context: 23ms / 23ms): ready plugins[] 
DEBUG (total: 34ms, context: 11ms / 34ms): ready plugins[] 

GET 
http://localhost:8080/ProjectName/staticService?clientId=12345678&file=/domReady.js 500 (Internal Server Error) staticService:551

--------------------------------------------------- staticService:340
WIRING: No progress in 5000ms, status: staticService:435
--------------------------------------------------- staticService:340
Components that finished wiring staticService:457
plugins[]: ready 
Object
 staticService:460
--------------------------------------------------- 

This extra request to domReady.js keeps sneaking in?

Oh, and i should mention, that i modified curl.js a bit to ALWAYS add the file extention. My checkToAddJsExt looks like this:

checkToAddJsExt: function (url) {
    // don't add extension if a ? is found in the url (query params)
    // i'd like to move this feature to a moduleLoader
    return url + /*(dontAddExtRx.test(url) ? '' :*/ '.js';//);
},

For reference, this is what my directory structure looks like where http://localhost:8080/ProjectName/staticService?clientId=12345678&file= points to:

= /cujo
=== /aop
=== /curl
=== /when
=== /wire

= /feature
=== /js
====== /config
========= /SuperRadSpec.js
====== /viewer
========= /AssetState.js

@briancavalier
Copy link
Member

Great, thanks @ethern8, the extra info is a huge help. I think I see one possible cause of the problem.

First, though, this package config is a bit strange:

    {name:'wire/domReady',  location:'cujo/wire',       main:'domReady'}

What this is essentially doing is mapping wire/domReady to itself. I don't honestly know if it's part of the problem here, but it shouldn't be necessary. So, it may be a red herring that's contributing to the confusion.

Ok, on to what I think is the real problem.

First, and this may also be a point of confusion, "wire/domReady" is an adapter that allows wire.js to use an existing domReady module provided by an AMD loader, such as curl.js or RequireJS. This allows wire.js to integrate more closely with AMD loaders and their existing DOMReady handling, without having to duplicate DOMReady boilerplate code.

Next, curl.js provides its own domReady module, and it's located in curl's plugin directory. When wire.js needs to use the AMD loader's domReady, it loads it as a plugin, e.g. "domReady!". When curl.js sees a top-level plugin module id, it prepends the pluginPath value to try to load the plugin. Thus, when wire/domReady tries to load "domReady!", that ends up resolving to:

http://localhost:8080/ProjectName/staticService?clientId=12345678&file=/domReady.js

Those which seems to explain the error you're seeing.

So, I believe there are 2 possible solutions. For the first, it's probably a good idea still to remove the "wire/domReady" package config. For the second, I think you'll need to remove it:

  1. Define a path config that explicitly maps the module id "domReady" (not "wire/domReady") to the correct url for curl's domReady. I'm guessing here but such a path config in your setup might look like:

    paths: {
        domReady: 'http://localhost:8080/ProjectName/staticService?clientId=12345678&file=cujo/curl/src/curl/plugin/domReady.js'
    }
  2. Define a path config that maps "wire/domReady" directly to curl's domReady module. This bypasses wire.js's domReady adapter, however, this is ok because wire.js can work directly with curl's domReady. So, this might look like:

    paths: {
        'wire/domReady': 'http://localhost:8080/ProjectName/staticService?clientId=12345678&file=cujo/curl/src/curl/plugin/domReady.js'
    }

Please give one of those a try and see if that helps get past the domReady loading errors. Good luck!

@ethern8
Copy link

ethern8 commented Aug 6, 2012

SUCCESS!

Removing the redundant wire/domReady from the config probably helped, but appeared to have little to no effect. The first suggestion of explicitly setting the domReady path helped get me a little further, but adding BOTH the domReady and the wire/domReady paths solved the problem!

paths: {
    domReady:'http://localhost:8080/ProjectName/staticService?clientId=12345678&file=cujo/curl/src/curl/plugin/domReady',
    'curl/domReady':'http://localhost:8080/ProjectName/staticService?clientId=12345678&file=cujo/curl/src/curl/domReady'
},

Do you have any other suggestions for configuring the library to handle URLs with ? query strings? Currently my hack to checkToAddJsExt is working well, but I was thinking i'd fork the lib and work on a way to set a flag in the config to force adding 'js' extension unless you had something in the works? Any thoughts?

Thank you for your help. Thrilled to have this working again!

@ethern8
Copy link

ethern8 commented Aug 6, 2012

Related to the checkToAddJsExt :
cujojs/curl#107

@briancavalier
Copy link
Member

Awesome, glad you got it working. I'm not really sure why the "curl/domReady" path mapping is needed there. That's curl's domReady module (as opposed to the plugin--curl separates the two). Is it possible you have code that uses "curl/domReady" as a dependency?

As far as checkToAddJsExt goes, you've def done the right thing by posting to that curl issue. It's possible @unscriptable may want to add some sort of "alwaysAddJsExt" option, but he and I haven't really talked about it yet. Let's see where the discussion goes over there

For now, I'll close this issue, since it seems like the remaining issue is related to AMD loading, rather than specifically to wire.js. Feel free to reopen or create a new issue if something wire.js-specific pops up, though!

@unscriptable
Copy link
Member

Hey guys,

There's a lot going on in this thread, but I think all that is needed to solve the domReady issue is to fix the pluginPath in curl's config:

pluginPath: 'http://localhost:8080/ProjectName/staticService?clientId=12345678&file=/cujo/curl/src/curl/plugin'

Then remove all of the domReady-related paths and packages. Does that work?

-- J

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants