On demand creation

34 views
Skip to first unread message

Duong Nguyen

unread,
Jul 24, 2014, 9:21:06 AM7/24/14
to cuj...@googlegroups.com
Hi all,
I am learning wire in my weekend project. It's a great tool, I would say. However, I am facing a problem that I don't know how to solve. I hope you can give me some advises. Thanks a lot in advance.
In my application, I have a ViewContainer object which is responsible for loading views according to view name on the hash. The ViewContainer instance is a singleton created in the root wire context. To be able to load views, the ViewContainer need to create view instances. I want to create the view with its dependencies (property injection) using wire. I am trying 3 approaches:

1) Define all the views' specification in the root context and inject them into the ViewContainer
This is really inefficient because everything is loaded, instantiated as soon as the root context is resolved.

2) Define each view's specification in its own spec and inject to the ViewContainer in the root context using wire factory.
This is better since the view is not loaded until the view container needs it. Problem here is that I need so many spec files. Says, I have 300 views, then I need 300 spec which is not nice.

3) Programmatically load the view context into the root context
I keep all the views' spec in a single specification file, identified by view name. Once the view container needs to create a view, I only take that view's spec and feed to rootContext.wire(). My problem here is that I have to pass the rootContext all the way from the root application object to the view container which is not good. I also think that using the root context in the wired component is not recommended in IoC world.

Any help would be really appreciated.
Thanks

Brian Cavalier

unread,
Jul 25, 2014, 11:54:34 AM7/25/14
to cuj...@googlegroups.com
Hey Duong,

Great question.  You're right in that passing around contexts is something to try to avoid, because it means your application code becomes tied to something wire-specific.  There are situations, though, where it's convenient to use wire programmatically.  There are a couple of features that can help you abstract it.

First, there is the ability to inject wire as a function, rather than passing around a context.  Since you inject a function, your application code isn't tied to any wire-specific API names (of course, there is a small tie to the functions' parameters).  For example, in testing, you could inject a mock function instead.

Here are the docs for injecting wire as a function: https://github.com/cujojs/wire/blob/master/docs/wire.md#injecting-wire

I could imagine using the hash to build the module name of the view your ViewContainer wanted to wire, and then passing it to an injected wire function.

Second, there is the ability to use wire itself as a factory from within wire specs.  Especially interesting is the `defer` option which lets you pre-configure (somewhat like currying) a wire function with a spec.  You can call the function later and it will wire the spec into a context on demand.  You can call it multiple times, and even pass in a mixin spec in each call.

Here are the docs for the wire factory and its defer option: https://github.com/cujojs/wire/blob/master/docs/components.md#wire

This second option doesn't quite seem to fit as well, but I wanted to point it out as an option, since you know more about your use case than I do!

Anyway, it seems like one of those two options can help you abstract out the rootContext.  Let me know how it goes, and feel free to keep asking questions!

Duong Nguyen

unread,
Jul 25, 2014, 6:29:21 PM7/25/14
to cuj...@googlegroups.com
Thank you very much Brian.
I was thinking about using wire factory (my 2nd approach) but the problem was that I did'nt want to wire all the views at once. Therefore, I needed to define each view spec in a separate file to be able to defer wiring them separately. That ended of having so many spec files and made my view more complex. I had already view template, view widget (dijit's), view model so I didn't want a view spec file. It would be great if wire factory can work with inline spec instead of an external spec file (correct me if that is already supported). That would help me to reduce number of files by putting all view spec in a single file.

And thanks for suggesting injecting view function. I think that would be good to make my ViewContainer testable if I wanted to programmatically use wire there. However, my main problem here is not injecting wire but injecting my root context. I don't know if I used the correct term. When saying "root context" I meant my main wire spec which is loaded with the application and available throughout the application life cycle. I need any child context to be loaded on top of this root context because of the references the items in child context use. For example: my TodoListView spec look like:

todoListView: {
    create: {
        module: "myapp/views/TodoListView",
        args: {
            model: {
                create: {
                    module: "myapp/views/viewmodels/TodoListViewModel",
                    args: {
                        store: { $ref: "todoStore" }
                    }
                }
            }
        }
    }
}

"todoStore" is a singleton object which was created in my root spec so I need to load this view spec from that root context. I was thinking about making my main module return the root context so every time I want to use it, I can just require that main module. Of course, my ViewContainer should still be testable by using the Poor Man's DI pattern where the root context is either taken from constructor parameter (in test) or from the singleton module (in production)

I hope that explained my problem better. Any further advices would be greatly appreciated. And again, thank you for the great tool.

Best regards.

Brian Cavalier

unread,
Jul 27, 2014, 5:17:48 PM7/27/14
to cuj...@googlegroups.com
Duong,

> I need any child context to be loaded on top of this root context because of the references the items in child context use

Let me explain a little more about injecting wire as a function.  The function that is returned by { $ref: 'wire!' } is *contextual*.  That is, it is specific to the enclosing context.  So, for example if, inside your "root context", you inject { $ref: 'wire!' }, that injected function will behave as if you are calling rootContext.wire(...), thus wiring child contexts "on top of" your root context.  Those child contexts will have visibility into the root context, etc.

In other words: injecting { $ref: 'wire!' } is effectively the same as injecting a context and calling its .wire() method.  Using { $ref: 'wire!' } just has the advantage of freeing your application code from a direct dependency on the wire-specific context object.


So, it still sounds like that is the right fit for your situation.  I hope that helps.  I may be missing something, though, so if that still doesn't sound like it will work for you, maybe you can post the code for an example that shows more of the problem you are trying to solve.  That way we could work through it together.

Duong Nguyen

unread,
Jul 27, 2014, 5:49:47 PM7/27/14
to cuj...@googlegroups.com
Cool! Thank you for pointing that out. That would completely solve the problem I got with the programmatical approach!

Just one more question with $ref. It's really strange that it worked when I made a reference to a property of a component ($ref: "foo.bar") but when I made a "deeper" reference ($ref: "foo.bar.qux"). Do you have any idea? I am using wire 0.9.1

Best regards

On Thursday, July 24, 2014 3:21:06 PM UTC+2, Duong Nguyen wrote:

Brian Cavalier

unread,
Jul 27, 2014, 9:14:16 PM7/27/14
to cuj...@googlegroups.com

On Sunday, July 27, 2014 5:49:47 PM UTC-4, Duong Nguyen wrote:
Cool! Thank you for pointing that out. That would completely solve the problem I got with the programmatical approach!
  
No problem.  I hope that works for you.
 
Just one more question with $ref. It's really strange that it worked when I made a reference to a property of a component ($ref: "foo.bar") but when I made a "deeper" reference ($ref: "foo.bar.qux"). Do you have any idea? I am using wire 0.9.1

There is a somewhat arbitrary restriction[1] only allowing 1 dot.  Originally, we felt it was a bad practice to "reach into" other objects and pull out properties.  I'm not really as opposed to it now, so I could probably be convinced to remove the restriction :)  If you have a good use case for it, please do post it!
 

Duong Nguyen

unread,
Jul 30, 2014, 4:31:56 AM7/30/14
to cuj...@googlegroups.com
I was just thinking that being able to organize components into namespaces would make my spec cleaner.

Brian Cavalier

unread,
Aug 1, 2014, 8:58:26 AM8/1/14
to cuj...@googlegroups.com
Hey Duong,

Personally, I'd try to avoid going too deep with the namespacing, as you may end up with something that's not unlike global (ie window) namespacing ... window.myApp.theViews.aView etc etc.  One of the benefits of IOC/DI is that it can free you from having to deeply namespace things.  For example, you can often use your module directory structure as namespacing rather than nesting in JS with dots.

All that said, I don't see a compelling reason to keep the current artificial limit, so I created a github issue to discuss:


Please feel free to add your thoughts there :)
Reply all
Reply to author
Forward
0 new messages