0

The First Pattern: "Module Pattern" AFAIK

We have a pretty common pattern for using plupload. We don't use their built in UI because it is overkill for our simple single uploads. So we declare our own HTML

<div class="control-group">
    <label class="control-label" for="OriginalFilename">Attachment (optional):</label>
    <div class="controls">
        <div id="uploadControl">
            <a id="pickfiles" class="btn btn-default" href="@Url.Void()">Select</a>
            <div id="filelist">Your browser doesn't have Flash, Silverlight or HTML5 support.</div>
        </div>
        @Html.Hidden("TemporaryFilename")
        @Html.Hidden("OriginalFilename")
        <span class="help-inline error">
            @Html.ValidationMessage("OriginalFilename")
        </span>
    </div>
</div>

and then initialize plupload wiring up events to the button and populating the hidden fields with upload results.

There is alot of repetition each time we implement this on the javascript side, so I implemented a module pattern to put the common code in. Leveraging the module looks like this, so you just put this at the bottom of your page, telling the module what url to upload to, and it exposes an upload started and upload succeeded events.

pluploadBuilder.init({
    chunkUrl: '/SubmissionFileChunker")',
    maxFileSize: '25mb',
    uploadStarted: function () { $('#submitBtn').attr('disabled', 'disabled'); },
    success: function () { $('#submitBtn').removeAttr('disabled'); },
    filenameResultSelector: '#TemporaryFilename',
    browseButtonId: 'pickfiles'
});

My module pattern looks like this, but you can see how I'm taking the config values passed in and using them to initialize plupload:

var pluploadBuilder = (function (){ //Module pattern

    return {

        init : function(config) {
            var self = this;

            var uploader = new plupload.Uploader({
                runtimes: 'html5,flash,silverlight,html4',
                browse_button: config.browseButtonId, 
                url: config.chunkUrl,
                FileUploaded: function (up, file, info) {
                        $(filenameResultSelector).val(info.response);
                        if (config.hasOwnProperty('success'))
                            config.success();
                    },
                //...many more plupload events, omitted for brevity... 
            });//end plupload   
            uploader.init();

        },//module init
    }
}());

The problem with this is as far as I understand this construct, is it is a singleton, and thus this will not work if I need to do this more than once on a page. I.e. if I have two seperately declared html blocks, and want to initialize each, the main challenge being that each upload should populate its respective hidden fields. Imagine a dynamically generated form that presents several items with a form for each.

I can't use ID selectors anymore unless I want to hack it with some crazy ID generator Id='TemporaryFilename-123' since there will be multiple '#TemporaryFilename' fields.

The Second Pattern: Name unknown

How I've handled this in the past is to use a pattern like this to scope all selectors to a parent container and select on classes instead of Ids:

var uploadBuilderNS = function ($this) { //$this expected to be a container element(already jquery selected)

    var initialize = function (config) {
        //similar plupload initialization except we scope selectors to $this, and caller should be using class selectors like: filenameResultSelector: '.tempFilename':

        var uploader = new plupload.Uploader({
              ...
              FileUploaded: function (up, file, info) {
                  $this.find(config.filenameResultSelector).val(info.response);
                  if (config.hasOwnProperty('success'))
                      config.success();
              },            
        });
        uploader.init();//calling pluploads init(not our init)

    };

    return {
        initialize: initialize,

    };
};

Then you would leverage like this assuming each control group was in a .uploadContainer:

$(".uploadContainer").each(function () {        
    uploadBuilderNS($(this)).initialize({ filenameResultSelector: '.temporaryFilename',... });
});

Basically I went through a few module pattern articles and came up with the first solution thinking I was refining my pattern. However, now that I understand exactly what is happening, it doesn't seem very modular at all. It is pretty much a singleton and I don't see how it would scale well for anything other than static utility/helper functions. I'm not sure what the second pattern, the old one I had been using previously is called, but it certainly seems more modular than the first. I guess that is the pitfall of people promoting a pattern without explaining its purpose. No pattern is universal and has appropriate uses, but I had seen it promoted quite a bit so I thought it would be an improvement upon what I had been doing previously. Please don't flame me. Maybe I'm using it wrong, obviously I realize this or else I wouldn't be here! I'm just explaining my understand so far so others can point out where I might be confused.

What is the second pattern called? I've seen it referred to in a couple places as a module pattern, but it is significantly different from the first, in that each uploadBuilderNS($(this)) generates a new instance. My main goal originally was to look at what others were suggesting, since it seems these patterns have been refined over time. I feel like I went down a really wrong path by switching to the first module pattern now that I realize it is a singleton.

Is there a more appropriate pattern for handling initializing multiple controls?

Update:

Problem with calling the first multiple times is element selectors being ID based and not being scoped to a container. If there are multiple controls repeated with the same IDs this won't work. I can switch to class selectors, but I still need a container element to scope to. The second pattern deals with this by passing in a container element to the closure when constructing each instance(see the last code block). The first pattern doesn't allow for this because it is constructed as soon as the script is loaded, so a parameter like var pluploadBuilder = (function (container){ wouldn't work since a parameter there is only for imports(i.e. it runs/creates the singleton when the script is loaded, and I have no opportunity to create multiple pluploadBuiler's passing in different containers): http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html

The only way I can invision making the first pattern work with calling initialize multiple times is to generate sequential ID suffixes when rendering the HTML, such as "TemporaryFilename-123". But this will be much more messy. If you look at the last codeblock, initializing multiple instances is much cleaner with the second pattern. No need to use IDs as it is purely class based selectors, and the foreach/init allows me to initialize multiple identical control groups with one code block.

5
  • How are you using this within init? This question cannot be properly answered without that knowledge. Commented Jul 8, 2014 at 18:22
  • In the first pattern? Im not. The self declaration was just from an article, but never found a use for it since AFAIK refers to the singleton being created. In the second pattern the caller passes in $this so it is useful for scoping selectors to the current container. If i jave multiple methods in the second param i save $this to a member so it can be referenced in other methods. I will update the second example to exemplify $this usage Commented Jul 8, 2014 at 18:50
  • Well if you aren't using this what prevents you from calling pluploadBuilder.init multiple times with a different configuration? Commented Jul 8, 2014 at 18:56
  • See update appended. The issue is scoping selectors to a specific container. The second pattern being instance based allows me to provide each instance a container to scope all selectors to. The first does not. Commented Jul 8, 2014 at 19:18
  • 1
    Why can't you just pass the container as a configuration option to init? I mean, pluploadBuilder.init is just a factory function. Nothing prevents you from doing pluploadBuilder.init({ container: someEl, ... });. You actually do not need the pluploadBuilder object at all (only serving as a namespace here). I would rather just have a function like function createUploader(config). If you would however, store data on this, then you could do Object.create(pluploadBuilder).init(config). Commented Jul 8, 2014 at 20:36

1 Answer 1

1

You're under the misconception that module pattern only returns plain old JavaScript objects that resemble an object literal. The module pattern runs its code only once (unless you run it in a loop) so you're right that it is effectively a singleton in normal usage. While that singleton is usually an instance of an object, it can also be a namespace or a constructor function or simply a JavaScript primitive. The essence of the module pattern is to place private functions or members in the closure scope. It's wrong to focus on the enclosing IIFE, for while the module pattern is usually enclosed in an IIFE to minimize pollution of global scope, it's not necessary.

Your second example can be rewritten simply as a module pattern that returns a singleton function

var uploadBuilderNS = (function() {
  return function($this) {
    var initialize = function (config) {
      var uploader = new plupload.Uploader({
          ...
          FileUploaded: function (up, file, info) {
              $this.find(config.filenameResultSelector).val(info.response);
              if (config.hasOwnProperty('success'))
                  config.success();
          },            
      });
      uploader.init();//calling pluploads init(not our init)
    };
    return {
      initialize: initialize,
    };
  };
})();

There isn't any point in doing it for your example, though, because you don't have anything to hide in the closure.

So the answer to Is there a more appropriate pattern for handling initializing multiple controls? is that the module pattern will work just fine -- you just have to see that the module pattern can return functions as well as plain old JavaScript objects.

Sign up to request clarification or add additional context in comments.

2 Comments

So usage of your example would be uploadBuilderNS( $('someSelector')).initialize(configObject) ?
I just took your example, which is supposed to accept a container element. I just wrapped it in an IIFE and module pattern but returned the exact same function as in your example, so it should be invoked in the same way your original was invoked. So ... yes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.