Dave
Dave

Reputation: 2765

Ember.js File Upload to change model on a route

I have Ember displaying a list of things.

I want the user to upload a file and change the list of things to the stuff in that file.

HTML

  <script type="text/x-handlebars" id="index">
    <h2>List of things</h2>
    <ul>
      {{#each item in model}}
        <li>{{item}}</li>
      {{/each}}
    </ul>
    Change List of Things:
    {{#view Ember.View}}
      {{view App.FileUp}}
    {{/view}}
  </script>

Javascript

App = Ember.Application.create();

App.Router.map(function() {
  // put your routes here
});

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return ['red', 'yellow', 'blue'];
  }
});

App.ApplicationController = Ember.Controller.extend({
    actions: {
        file_upload: function(data) {
            alert("I want to change the model to: " + data);
        }
    }
});

App.FileUp= 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;
                var c = App.ApplicationController.create();
                c.send('file_upload', data);
            }
            reader.readAsText(input.files[0]);
        }
    },
});

At the line with the alert, I have access to the text from the uploaded file, and I'm in the application controller. How can I change the model so that the list of the page reflects what's in the text file?

JS Fiddle: http://jsfiddle.net/LJx9t/

If this code isn't idiomatic Ember, please feel free to correct.

Upvotes: 2

Views: 626

Answers (2)

Kingpin2k
Kingpin2k

Reputation: 47367

Being a component you should use sendAction to pass the action out of the component. This allows the component to be used multiple times and doesn't tie it to the context.

  {{view App.FileUp uploadComplete='file_upload'}}

  {{view App.FileUp uploadComplete='file_upload2'}}

Then inside your component

 change: function(evt) {
    var input = evt.target,
        self = this;

    if (input.files && input.files[0]) {
        var that = this;

        var reader = new FileReader();
        reader.onload = function(e) {
            var data = e.target.result;
        self.sendAction('uploadComplete', data);
        }
        reader.readAsText(input.files[0]);
    }
},

then your action gets sent to the controller, then route, then up the route tree until it's handled.

Create an index controller to handle it

App.IndexController = Ember.Controller.extend({
    actions: {
        file_upload: function(data) {
            this.set('model', data.split('\n'));
        },
        file_upload2: function(data) {
            alert(data);
        }
    }
});

http://jsfiddle.net/rmCYk/1/

Upvotes: 2

melc
melc

Reputation: 11671

The model you want to change has been created by the IndexRoute, so you need to set it to the IndexController. Furthermore the App.FileUp is a component, so in order to send the action to the currently active controller, which is IndexControler it is required to use targetObject or get('controller') from parentView .

(related info, https://github.com/emberjs/ember.js/blob/master/packages/ember-handlebars/lib/controls.js#L81-84 and a discussion https://github.com/emberjs/ember.js/issues/3393 )

So assuming a file with csv is uploaded the following code can achieve what you require,

http://jsfiddle.net/9u3Cd/

js

App = Ember.Application.create();

App.Router.map(function() {
  // put your routes here
});

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return ['red', 'yellow', 'blue'];
  }
});

App.IndexController = Ember.ObjectController.extend({
    actions: {
        file_upload: function(data) {
            alert("I want to change the model to: " + data);
            this.set('model',data.split(","));
        }
    }
});

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

    change: function(evt) {
        var self = this;
        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;
                //self.get('parentView').get('controller').send('file_upload', data);
                self.get('targetObject').send('file_upload', data);
            }
            reader.readAsText(input.files[0]);
        }
    },
});

Upvotes: 1

Related Questions