Question

I'm using this nice Ember.js mixin: https://github.com/Wildhoney/EmberDroplet to upload images using drag-and-drop and it's working brilliantly.

The only problem is, I don't know how to add the images into the view after they have been uploaded. The model is getting updated properly in my Rails API, but, short of actually refreshing the whole page, I don't know how to update the model in Ember.

Here's some snippets of my code:

router.js.coffee

App.Router.map ->
  @resource('site', { path: '/' }, ->
    @resource('media')
  )

routes.js.coffee

App.MediaRoute = Ember.Route.extend
  model: (params)->
    return App.Media.find()

media.handlebars

<div class="row">

    <div id="media-container" class="large-12">
        <div class="row">
        {{#each media in model}}
            <div class="large-4 columns">
                <a class="th" {{bind-attr href='media.url'}}>
                    <img {{bind-attr src='media.thumb'}}>
                </a>
            </div>
        {{else}}
            <div class="large-12">
                <p>Sorry, no images to display.</p>
            </div>
        {{/each}}
        </div>
    </div><!-- /#media-container -->

</div><!-- /.row -->

<div class="row">

    <div id="media-upload" class="large-12">
        <div class="buttons">
            <button class="btn" {{action "uploadAllFiles"}}>Upload All</button>
            <button class="btn" {{action "clearAllFiles"}}>Clear</button>
        </div>

        {{#if uploadStatus.uploading}}
            <h3 class="uploading-percentage">Uploaded Percentage: {{uploadStatus.percentComplete}}%</h3>
        {{/if}}

        {{#view view.DragDrop}}

            {{#if uploadStatus.error}}
                <div class="error">An error occurred during the upload process. Please try again later.</div>
            {{/if}}

            {{#each controller.validFiles}}

                <div {{bind-attr class="className:image"}}>
                    {{name}}
                    <a class="remove" {{action "deleteFile" this}}>Discard.</a>
                    {{view view.ImagePreview imageBinding="this"}}
                </div>

            {{/each}}

            {{view view.MultipleInput}}

        {{/view}}
    </div><!-- /#media-upload -->

</div><!-- /.row -->

media.js

App.MediaController = Ember.ArrayController.extend(DropletController, {
    dropletUrl: 'http://localhost:3000/images',

    uploadedSomeFiles: function uploadedSomeFiles() {
        var files = this.get('uploadedFiles');
        files.forEach(function(file) {
                // This gets fired every-time a file has uploaded successfully
                console.log(file + ' was uploaded!');
        });
    }.observes('files.length', 'files.@each.uploaded')

});

Oh yes, and the models, if that helps (I'm using ember-model):

App.Site = Ember.Model.extend({
  title: attr(String),
  about_text: attr(String),
  is_custom_domain: attr(Boolean),
  subdomain: attr(String),
  url: attr(String),
  copyright: attr(String),
  site_desc: attr(String),
  meta_keywords: attr(String),
  footer_code: attr(String),
  header_code: attr(String),
  hide_site: attr(Boolean),
  user: belongsTo('App.User', {key: 'user_id'}),
  page: hasMany('App.Page', {key: 'pages', embedded: true}),
  media: hasMany('App.Media', {key: 'images', embedded: true})
  });

App.Media = Ember.Model.extend({
 file_size: attr(Number),
 width: attr(Number),
 height: attr(Number),
 url: attr(String),
 identifier: attr(String),
 current_path: attr(String),
 content_type: attr(String),
 thumb: attr(String),
 medium: attr(String),
 small: attr(String),
 tall_banner: attr(String),
 short_banner: attr(String),
 site: belongsTo('App.Site', {key: 'site_id'})
});

With jQuery alone it would be dead simple to just append the new image to the page, but for some reason Ember makes it very complicated. (Ok, in all fairness other things are much easier with Ember.)

UPDATE:

Wouldn't you know it, I struggle with a problem for days (yes, days) and hours after I post it to stackoverflow, I get a little closer to the answer.

I found out that since emberdroplet is using a standard XMLHttpRequest, I can use request.onload and have access to the request.responseText sent back by my Rails API.

Ok, so here's my code now:

request.onload = function() {
  var media = App.Media.find();
  request.responseText.forEach(function(file, media) {
    media.pushObject(file);
    console.log(file.id);
  });
}

But, I'm getting back an error:

I get back this JSON:

{"id":93,"file_size":438162.0,"width":null,"height":null,"url":"/uploads/image/image/93/blevins_header_2013_v6.jpg", etc.

and this gets added to the view:

<a class="th" data-bindattr-276="276">
  <img data-bindattr-278="278">
</a>

So, I figure, Ember-model doesn't know what to do with the raw JSON, so I should do something to it first, right?

Any ideas?

Was it helpful?

Solution 2

Ok, so I finally figured it out myself. Maybe not the best way, but here goes.

One of the major hurdles was the discovery that the mixin was not providing an array-like name to my rails backend. In his words:

Node.js is clever enough to deduce an array of images, whereas Ruby/PHP require the specifying of an array-like name.

The developer was kind enough to add this. (See this commit: https://github.com/Wildhoney/EmberDroplet/commit/c769268084a006704f68f6993bb999e796762e4e)

So, here is my complete MediaController code for anyone else working with this kind of thing.

App.MediaController = Ember.Controller.extend(DropletController, {
    dropletUrl: 'http://localhost:3000/images',

    useArray: true,

    actions:{ 
        uploadAllFiles: function() {
            var self = this;
            $.when(self._super()).then(function(data){
                var media = App.Media.find();

                // console.log(data);

                $.each(data, function(idx, obj) {
                    media.pushObject(obj);
                });
                // media.save();
                media.reload();
            });
        }
    }
});

uploadAllFiles gets called after all of the files have been uploaded and returns my objects (i.e. newly created models) from the Rails API.

I have to call media.reload(); so that any actions (i.e. deleteImage) will work on the newly added objects.

OTHER TIPS

You need to fetch new Media model after it's been created on server-side. The way is up to you.

For example, you can call this.store.find('media') for fetching all models every time.

Or set MediaRoute.model to return this.store.filter('media') and add server-side route for creating a Media model. Then on client you need to manually create a new model for every uploaded file and save them (ember-data automagicly inserts them to filtered arrays).

Or you can handle all ajax work yourself. If you want a dead simple jQuery way, bind template to {{#each file in files}}, which iterates over uploaded files (use file.url or smth, I don't sure). To load already uploaded files use $.getJSON method and add every file from response to MediaController.files property (I'm not recommending this way, but it's possible to be simple).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top