3

I'm trying to use a jQuery plugin (Plupload) with AngularJS. I have created a directive that will be my file upload "widget". The directive looks like this (The code in the link function is a very simplified version of the example on the Plupload site):

.directive('myFileUpload', function () {
    return {
        restrict: 'E',
        replace: true,
        link: function(scope, element, attrs) {
            scope.uploaderProperties = {
                    runtimes : 'html5,flash,html4',
                    url : 'media/upload',
                    max_file_size : '10mb',
                    container: 'fileUploadContainer',
                    drop_element: 'fileUploadDropArea'
            };

            scope.uploader = new plupload.Uploader(scope.uploaderProperties);

            scope.uploader.init();

            scope.uploader.bind('FilesAdded', function(up, files) {
                scope.$apply();
            });
        },
        templateUrl: 'upload.html'
    };
});

My upload.html file looks like this:

<div id="{{uploaderProperties.container}}">
    <div id="{{uploaderProperties.drop_element}}" style="border: 1px black dashed;">Drop files here<br><br><br></div>
    Files to upload:<br>
    <div ng-repeat="currFile in uploader.files">{{currFile.name}} ({{currFile.size}}) </div>
    <br><br>
    <!-- for debugging -->
    {{uploader.files}}
    <br><br>
</div>

When I include the directive on my page with a <my-file-upload> element, all the data bindings happen correctly. The problem is, when scope.uploader.init(); runs, the ids haven't been inserted into the DOM yet, and so Plupload complains and breaks since it can't select those elements. If I just hard-code the fileUploadContainer and fileUploadDropArea ids in the template, it works just fine. However, I'd really like to define those ids in only one place.

So, is there any way I can run the init() on the uploader after the template is linked in? I thought about using $timeout to delay when it runs, but that seems like a pretty big hack to me. Is there a more correct way of accomplishing this?

UPDATE

I wrapped the init() in a $timeout like this

$timeout(function() {
    scope.uploader.init();
}, 2000);

just to make sure it would behave the way I was thinking, and sure enough, with this delay the plugin gets set up correctly and works. However, I do not want to have to rely on the timing of the $timeout. If there was just some way I could call scope.uploader.init(); after the template is linked in, everything should work fine. Anyone know of a good way of doing this?

1 Answer 1

1

Your problem is actually the other way around - The link function happens after the template is put in. So in this case, scope.uploaderProperties isn't set when the template happens.

It looks like your template is too fancy for the templateUrl option, really. You could try manually setting your ids with jQuery, or setting everything up in the compile function.

http://docs.angularjs.org/guide/directive

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

8 Comments

I've been reading through that doc the last few days, and I'm still trying to fully understand some of the concepts. You say scope.uploaderProperties isn't set when the template happens, but when I inspect the DOM, those 2 divs have the correct ids that came from uploaderProperties. Am I misunderstanding what you're referring to? Also, with the compile function, would I just set up all the properties in the pre function and then just do the init in the post function? I still don't fully understand what I should and shouldn't do in the parts of the compile function. Thanks for your help!
Well the ids will be set on the next iteration after hte link function happens. When the template actually put in, there is nothing in scope.uploaderProperties. Then you set it, and the link function finds it later. And yeah, the pre and post idea sounds like your best bet.
I tried the compile method, and still have the exact same problem. Is not the problem what I described in my original post? Plupload uses the values in the container and drop_element properties to select those elements and set them up for the plugin. When the init() method runs in the link function, the {{}} haven't been replaced yet for the ids, so it can't select them. I used $timeout to delay the call to the init(), and then it all works fine. I just don't want to have to rely on the timing of the $timeout.
If you set the ids in pre-link or compile (manually, creating the elements with jquery or whatever), then call init in post-link, it should work.
Yeah, I can see that working. It's just not as clean as I would like. I understand that using jQuery plugins with Angular isn't always going to result in perfect, clean solution. It just sure would be nice if there was a post-template link hook to do this kind of stuff... Thanks for helping me understand this stuff better.
|

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.