Unloading and loading modules on the fly #1

Closed
rikschennink opened this Issue Jun 16, 2013 · 6 comments

Comments

Projects
None yet
2 participants

I think for true webapp / website responsiveness we need to be able to load and unload modules on the fly. People might be rotating their iPad in between sessions, their network connection might fail or they might logout of your service. All environment changes that could impact the availability of certain UI modules.

Owner

webpro commented Jun 16, 2013

This demo and the article are only an approach, a starting point (i.e. I wasn't aiming for a full solution/framework).

This sounds like responsive application modules, i.e. such "UI modules" should probably be responsive by themselves. This is also why we need element queries (e.g. use min-width to decide whether the module fits), and the ability to hook into (some central module taking care of) contextual events as you mention (device orientation change, on/offline, etc.).

It's a tricky thing. E.g. having to load a module (through XHR) after some event might take too long. Then it probably makes sense to just (pre)load it like normal, and show/hide it afterwards (i.e. switch based on data-condition).

You mention "load and unload modules". Have you already looked into patterns like Dependency Injection and IOC containers? Such patterns might give you the control you need in application module lifecycle management. It was the extra step I needed to move from rather simple dependency injection to more sophisticated solution (which I explain in the article). If you're interested, the docs of wire.js might be a great starting point (and contains links to other resources).

In any case, you have a valid point and I'll probably take some time to demonstrate how I would implement it somehow (probably in this demo).

There's plenty of pitfalls indeed, that's why I haven't yet used my framework in any production environment, but at the same time I haven't found anything else that solves this problem elegantly. I was quit surprised to read the article as your approach to the problem is so similar to mine.

Conditions, they can be about a lot of things I think, for instance, I've build an example using cookie consent as a condition. As the user blocks the use of cookies modules requiring or using cookies are unloaded instantly. Some modules however should check for certain functionality themselves, like a google map build on geolocation support, you can't expect the developer to add conditions for this as it is a core functionality of the map module. I think, conditions should only live outside the module if the module could be active without them.

I solved the XHR loading delay by checking if the conditions are still suitable when the module is done loading, this way I can still 'rollback'.

As for dependency injection and IOC, I wrote a small dependency injection module for the first version of conditioner.js but soon found that I was in over my head. :-) I've now moved to RequireJS which suits the needs of the framework nicely at the moment, it's pretty straightforward and does not force exotic code styles. I'll definitely take a look at wire.js as that was one of the topics that I was quit charmed about in the article you wrote.

Do you know of any other frameworks out there that offer the same sort of solution?

Owner

webpro commented Jun 17, 2013

The basic approach of declaratively attaching modules to DOM elements is not that new (see e.g. SpringbokJS, Aura, Pamela Fox's blogpost, and there's probably a ton more). However it's clear we both were looking for more, and didn't really find.

There's (at least) two issues with most implementations:

  1. Tight coupling: some application controller needs to know how to initialize modules. You'll end up with requiring a specific way of doing that, e.g.:
  • The DOM element is passed as the only argument to some constructor.
  • Backbone views have an options object and always calls initialize() on itself.
  • The init or load method or similar is called (if present).

This might be fine in smaller projects, but it doesn't scale. With an IOC container like wire.js provides, this can be defined at the module layer. Using the same bootstrapping mechanism, projects can choose how to build modules (vanilla JS, Backbone, Flight, the-next-cool-thing, etc).

And this is actually just for starters, using the wire specs you can also start thinking about:

  • apply AOP (plugin included)
  • unified way for module lifecycle management
  • "grouping" modules in a single spec
  1. Conditions, as you point out. The (extra) XHR is killing performance, just to find out whether the module should be loaded at all. So this information should be present at the client, which can't be there for all existing modules. That's why I think it makes sense to put this at the element level as well (i.e. the data-condition attribute). It's a trade-off between performance and proper separation of concern.

I solved the XHR loading delay by checking if the conditions are still suitable when the module is done loading, this way I can still 'rollback'.

Could you please explain this a bit more? How do you know the conditions if they're not outside of the module?

As for RequireJS, if it would be possible to make that "AMD", then you could delegate loading to the global require method that's available. Users wouldn't be forced to use RequireJS (not that I have anything against it).

Do you know of any other frameworks out there that offer the same sort of solution?

No, I don't know of other frameworks that somehow enable "responsive apps" the way we are discussing. In the comments at SM, Benoit Marchant pointed to MontageJS, I still need to look into that.

  1. I recognize this, I want to offer the freedom to pick the framework of your choice but it's kind of difficult to do this atm, if wire.js solves this problem that would be nice. Currently conditioner.js expects a constructor or a load method to be defined on the module exports. It will then pass an element and options object to this method, from there the developer can choose on how to proceed.

  2. You should have a set of modules minimized and available in your default package, the more exotic components could be loaded async when applicable. I've thought a lot about where to put the actual condition and although it doesn't look very nice the best location is still the element. This is one of the reasons I've written the expression parser thingy so you can write more expressive conditions and it fits nicely in the attribute format.

I think I might have misunderstood your earlier statement, I was talking about the delay between requesting a module and the moment it comes available.

If the conditions are located in the module than indeed you can't know them before loading the module. In that case I think it should function the other way around, you load a basic module (which does not require GPS) and then test if a more advanced module (which requires GPS) could be loaded by just trying to load it and see if it returns a support error (i've currenlty not build this though). This would fit with a progressive enhancement strategy.

After some more thinking, I guess, it's only logical to have some module registry where you define these 'global' module conditions. The above situation would cause too much overhead and unnecessary bytes sent over the line. With predefined module registry you would be able to write a GPS module without testing for GPS support in the module itself. Next you can add the necessary tests to the registry and on loading a module test against the required conditions automatically. This would then result in page and node level conditions, where page level conditions are about the module itself (static requirements, possibly to do with required level of browser support and the availability of a fallback module) and node level conditions are about the specific instance of the module loaded at that location in the DOM.

  1. I've thought about doing this but there's also some configuration stuff handled by requirejs, like aliases for modules and page level module options, it would give more freedom though, but I think I'll have a look at wire.js first.

  2. MontageJS, looks interesting but can't find anything about conditions or environment testing in the docs. It does have a draw cycle which I think would be interesting to look at, it's one of the performance concerns I have atm.

Owner

webpro commented Jun 19, 2013

After some more thinking, I guess, it's only logical to have some module registry where you define these 'global' module conditions.

In certain applications this might work really well. However, it doesn't really scale, you might not know which modules might be in the page (e.g. when multiple applications/projects are using the same system).

With predefined module registry you would be able to write a GPS module without testing for GPS support in the module itself.

That's indeed exactly the goal: not needing to test from within the module. That's also accomplished with data-condition, although it's not very elegant (trade-off as I mentioned earlier). The important thing is that it's defined in a specific location/layer, and not having feature tests etc. scattered across modules.

Owner

webpro commented Sep 23, 2013

Just closing this for now, feel free to re-open or open a new ticket.

@webpro webpro closed this Sep 23, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment