10

I need to create an Ember component to select a file. My page will include multiple "upload component"

I have read a post trying to implement that: (https://stackoverflow.com/questions/9200000/file-upload-with-ember-data) BUT the UploadFileView is directly linked to the controller. I would like to have something more generic...

I would like to remove the App.StoreCardController.set('logoFile'..) dependency from the view or pass the field (logoFile) from the template...

Any idea to improve this code ?

   App.UploadFileView = Ember.TextField.extend({
    type: 'file',
    attributeBindings: ['name'],
    change: function(evt) {
      var self = this;
      var input = evt.target;
      if (input.files && input.files[0]) {
        App.StoreCardController.set('logoFile', input.files[0]);
      }
    }
});

and the template:

{{view App.UploadFileView name="icon_image"}}
{{view App.UploadFileView name="logo_image"}}
Community
  • 1
  • 1
fvisticot
  • 7,936
  • 14
  • 49
  • 79
  • I did reply to your comment btw -and just to clarify the UploadFileView is not a controller but instead a full blown custom view object. I honestly think you could modify the view I showed to make it more dynamic and allow your frontend to have a number of file upload inputs – Toran Billups Dec 18 '12 at 01:28
  • Yep, the UploadFileView is a full view object. As I'm not a ember.js expert, could you please help me to get the answer with a sample code ? – fvisticot Dec 18 '12 at 08:22

4 Answers4

14

I completed a full blown example to show this in action

https://github.com/toranb/ember-file-upload

Here is the basic handlebars template

<script type="text/x-handlebars" data-template-name="person">
{{view PersonApp.UploadFileView name="logo" contentBinding="content"}}
{{view PersonApp.UploadFileView name="other" contentBinding="content"}}
<a {{action submitFileUpload content target="parentView"}}>Save</a>
</script>

Here is the custom file view object

PersonApp.UploadFileView = Ember.TextField.extend({
    type: 'file',
    attributeBindings: ['name'],
    change: function(evt) {
      var self = this;
      var input = evt.target;
      if (input.files && input.files[0]) {
        var reader = new FileReader();
        var that = this;
        reader.onload = function(e) {
          var fileToUpload = reader.result;
          self.get('controller').set(self.get('name'), fileToUpload);
        }
        reader.readAsDataURL(input.files[0]);
      }
    }
});

Here is the controller

PersonApp.PersonController = Ember.ObjectController.extend({
  content: null,
  logo: null,
  other: null
});

And finally here is the view w/ submit event

PersonApp.PersonView = Ember.View.extend({
  templateName: 'person',
  submitFileUpload: function(event) {
    event.preventDefault();
    var person = PersonApp.Person.createRecord({ username: 'heyo', attachment: this.get('controller').get('logo'), other: this.get('controller').get('other') });
    this.get('controller.target').get('store').commit();
  }
});

This will drop 2 files on the file system if you spin up the django app

scribu
  • 2,958
  • 4
  • 34
  • 44
Toran Billups
  • 27,111
  • 40
  • 155
  • 268
  • if I don't use Person view and submitFileUpload have inside controller, may it cause problems? For me inside controller this.get('logo') is null @Toran Billups – Nininea Jul 22 '14 at 08:13
  • 1
    `self.get('controller').set(self.get('name'), fileToUpload);` needs to be changed to `self.get('targetObject').set(self.get('name'), fileToUpload);` – Roel Oct 07 '14 at 13:22
10

EDIT (2015.06): Just created a new solution based on a component. This solution provides an upload button with a preview and remove icon. P.S. The fa classes are Font Awesome

Component handlebars

<script type="text/x-handlebars" data-template-name='components/avatar-picker'>
        {{#if content}}
            <img src={{content}}/> <a {{action 'remove'}}><i class="fa fa-close"></i></a>
        {{else}}
            <i class="fa fa-picture-o"></i>
        {{/if}}

        {{input-image fdata=content}}
</script>

Component JavaScript

App.AvatarPickerComponent = Ember.Component.extend({
    actions: {
        remove: function() {
            this.set("content", null);
        }
    }
});

App.InputImageComponent = Ember.TextField.extend({
    type: 'file',

    change: function (evt) {
        var input = evt.target;
        if (input.files && input.files[0]) {
            var that = this;

            var reader = new FileReader();
            reader.onload = function (e) {
                var data = e.target.result;
                that.set('fdata', data);
            };
            reader.readAsDataURL(input.files[0]);
        }
    }
});

Usage example

{{avatar-picker content=model.avatar}}

Old Answer

I took Chris Meyers example, and I made it small.

Template

{{#view Ember.View contentBinding="foto"}}
    {{view App.FotoUp}}
    {{view App.FotoPreview width="200" srcBinding="foto"}}
{{/view}}

JavaScript

App.FotoPreview= Ember.View.extend({
    attributeBindings: ['src'],
    tagName: 'img',
});


App.FotoUp= Ember.TextField.extend({
    type: 'file',

    change: function(evt) {
        var input = evt.target;
        if (input.files && input.files[0]) {
            var that = this;

            var reader = new FileReader();
            reader.onload = function(e) {
                var data = e.target.result;
                that.set('parentView.content', data);
            }
            reader.readAsDataURL(input.files[0]);
        }
    },
});
lpinto.eu
  • 2,077
  • 4
  • 21
  • 45
  • This way I was only able to send binarydata for image to the server. Had to do a lot of fixing for server-side. IS there anyway I can send whole file object(temp file) for rails server(Carrierwave) ?? – Abhaya Jan 25 '14 at 14:14
  • If you want to use multipart-formdata, you can use jQuery's .serialize() method on form. – Marek Fajkus Nov 22 '14 at 19:37
1

Marek Fajkus you cannot use JQuery's .serialize, it makes no mention of file uploads in the documentation at JQuery UI docs

However, you could use JQuery Upload Plugin

Actually it does mention it, it says: ". Data from file select elements is not serialized."

Epirocks
  • 480
  • 4
  • 11
1

In case of uploading multiple files, you may want to use

{{input type='file' multiple='true' valueBinding='file'}}
                          ^^^^

This is a solution that you would use in normal HTML upload.

Additionally, you can use 'valueBinding' which will allow you to set up an observer against that value in your component.

Adrian Grzywaczewski
  • 868
  • 1
  • 12
  • 25