In this post, I’ll continue to work on the code started in the first article on Feature Detection and File Upload Forms, so you’d better read it before going further.
Last time, we created a Javascript component used to upload a file asynchronously on the server.
It uses FormData
and FileList
APIs in modern browsers but also degrades gracefully with browsers that don’t support those APIs.
This time, I’ll explain how to handle the thumbnail associated to the file input field.
It’s a good example to introduce loosely coupled modules, and to show some other uses of the feature detection technique.
As mentioned in the previous post, you can find the source code on Github: uploader-thumbnail.
Loosely Coupled Modules
If you never hear about loosely coupled module, I highly recommended you to read/watch the following:
- Nicholas Zakas’ talk on Scalable JavaScript Application Architecture [YouTube]
- Addy Osmani’s Patterns For Large-Scale JavaScript Application Architecture
Those links will give you a pretty good picture but to summarize briefly, the main concepts to keep in mind when you want to rely on a loosely coupled modules architecture is that you need to:
- Split your code into separate modules, obviously.
- Don’t allow a module to know about each-other.
How could you do that? Let’s try to illustrate the idea with our file upload form.
Creating modules
We will create three simple modules to complete our file upload form, each one related to a very simple functionality:
- 1st Module
uploader
: uploads a file - 2nd Module
remover
: ask for deleting a previously uploaded file - 3rd Module
thumbnail
: display a thumbnail
In the first part we have already build the first one. As the two other ones are quite easy to implement, I won’t detail every piece of code in this post. Feel free to look at the complete source if necessary. For now let’s see those two as black boxes that fits with our requirements.
Communication between modules: introducing the observer
The Observer Pattern
As mentioned before, our modules can’t reference each-other. In other words, in the uploader
‘s code, calling a method from the thumbnail
object or referring to it in any other way is forbidden.
1 | // uploader's inside code |
That’s why we need to introduce the observer component. Its role is to allow communication between modules. In other words, instead of talking directly to each-others, modules in your application will talk and listen to the observer.
There many different implementations of the observer pattern but it’s basically a component with 3 methods:
1 | var observer = { |
observer.publish('some-topic', message)
: send a message on the topic ‘some-topic’observer.subscribe('some-topic', callbackFunc)
: starts listening messages on the topic ‘some-topic’.callbackFunc
will be called every times a message is published on the topic, themessage
will be passed as a parameter.observer.publish('some-topic', callbackFunc)
: stops listening messages on the topics ‘some-topic’.
Using the observer
Back the the upload form, the implementation doesn’t change that much. You only need to pass the observer as parameter, and publish some events to notify the application
1 | this.obs = options.observer || {publish:function(){}, subscribe:function(){}}; |
1 | beforeUpload: function(file) { |
On the other side, the thumbnail module need to listen to these events and to react to these in an appropriate way. In our case, when receiving a submit
event, the thumbnail module should hide an existing image and display a load indicator. When receiving a uploaded
event, it have to show the newly uploaded picture.
1 | Thumbnail.prototype = { |
As you can see in the above code, both uploader
and thumbnail
module are not aware from each others, they only talk and listen to the observer.
Conclusion
- You mat decide to completely change some module implementation, as long as it sends the same messages to the observer, everything is fine.
- Every other module in your application can listen to observer. The thumbnail may not be the only piece of application interested in
uploader
events. That’s fine, the new module just need to subscribe the appropriate event. - Last but not least, if some module appears to be broken, or if it doesn’t starts as expected, other modules won’t be much affected and your application is still working (even if some features are down).