Can someone provide code examples or documentation on implementing a form with a file field using EmberJS and Ember Data?
I'm familiar with Ember Data already but I'm not sure how to implement file-uploads correctly.
ember-cli-file-picker - An addon for ember-cli that provides a component to easily add a filepicker to your ember-cli app. emberx-file-input - A tiny Ember component which does one thing and only: select files beautifully. ember-profile-upload - A simple photo upload component.
To automate this process, there are 2 ways to fill the file upload control: If standard file dialog is used by the web page, upload action can be automated with pure selenium-webdriver. You can call sendKeys() on the file control to fill the file control with file path and then submit form.
Here is part of an ember-data adapter I wrote to do file uploads (same server -not cross domain)
DS.DjangoRESTAdapter = DS.RESTAdapter.extend({
bulkCommit: false,
createRecord: function(store, type, record) {
var root = this.rootForType(type), json = {};
var data = new FormData();
data.append('username', record.get('username'));
data.append('attachment', record.get('attachment'));
this.django_file_ajax('http://localhost:8000/people/new/', "POST", {
data: data,
context: this,
success: function(pre_json) {
json[root] = pre_json;
this.didCreateRecord(store, type, record, json);
}
});
},
django_file_ajax: function(url, type, hash) {
hash.url = url;
hash.type = type;
hash.contentType = false;
hash.processData = false;
hash.context = this;
jQuery.ajax(hash);
}
});
})();
It's not IE8 friendly as is because I'm using the "FormData" helper to do multipart file upload but it's a good proof of concept.
Here is the ember-data model to go w/ the above adapter
PersonApp.Person = DS.Model.extend({
id: DS.attr('number'),
username: DS.attr('string'),
attachment: DS.attr('string')
});
Here is the handlebars template
<script type="text/x-handlebars" data-template-name="person">
{{view PersonApp.UploadFileView name="logo_image" contentBinding="content"}}
</script>
Here is the custom ember view
PersonApp.PersonView = Ember.View.extend({
templateName: 'person'
});
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 = e.srcElement.result;
var person = PersonApp.Person.createRecord({ username: 'heyo', attachment: fileToUpload });
self.get('controller.target').get('store').commit();
}
reader.readAsDataURL(input.files[0]);
}
}
});
If you want to see a full blown spike with this in action checkout a multi file upload example I did recently.
https://github.com/toranb/ember-file-upload
Look at the links below. The first link or blog post contains a link to a working jsfiddle that handles upload with emberjs. Note I didn't write the blog or create the fiddle. But it should solve your issue.
http://chrismeyers.org/2012/06/12/ember-js-handlebars-view-content-inheritance-image-upload-preview-view-object-binding/ - dead link
http://devblog.hedtek.com/2012/04/brief-foray-into-html5-file-apis.html
It's fairly simple, the general steps are:
It should be noted though that base64 encoding large files has performance issues, but for smaller images or text it won't be a problem.
You could also send the file 'outside' of Ember Data, and push the response (such as a JSON payload representing a model) into the store via pushPayload
. If so, FormData
or other methods in XHR2 can be used.
Read more about client-side manipulation of files here: http://www.html5rocks.com/en/tutorials/file/dndfiles/
Read more about XHR2 and FormData for file uploads here: http://www.html5rocks.com/en/tutorials/file/xhr2/
I tried a few different solutions and ended up writing a FormData adapter and a File transform. Then any model that needs to upload file data can just use the FormDataAdapter and define the file attributes as type "file":
app/transforms/file.coffee
FileTransform = DS.Transform.extend
serialize: (jsonData) ->
jsonData
deserialize: (externalData) ->
externalData
app/models/user.coffee
User = DS.Model.extend
avatar: DS.attr('file')
app/adapters/form_data.coffee
get = Ember.get;
App.FormDataAdapter = ApplicationAdapter.extend
ajaxOptions: (url, type, hash) ->
hash = hash || {}
hash.url = url
hash.type = type
hash.dataType = 'json'
hash.context = @
if hash.data and type != 'GET' and type != 'DELETE'
hash.processData = false
hash.contentType = false
fd = new FormData()
root = Object.keys(hash.data)[0]
for key in Object.keys(hash.data[root])
if hash.data[root][key]
fd.append("#{root}[#{key}]", hash.data[root][key])
hash.data = fd
headers = get(@, 'headers')
if headers != undefined
hash.beforeSend = (xhr) ->
for key in Ember.keys(headers)
xhr.setRequestHeader(key, headers[key])
hash
app/adapters/user.coffee
UserAdapter = FormDataAdapter.extend()
Sorry about the CoffeeScript, but it's pasted from this blog post: http://blog.mattbeedle.name/posts/file-uploads-in-ember-data/. You can read a more detailed description there. This solution should probably be combined with a HTML5 FileReader input to enable image previews and client side file type validation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With