Has anyone been able to get paper.js working with ember.js? I am new to both libraries and have not been able to get things to work. When I refer to the 'paper' object I get an error: 'paper' is not defined.
Any help would be much appreciated!!
-nt
This is an example of using paper.js along with ember.js. When clicking an event is sent to an ember view from the paperjs event handling code, which populates an array of the corresponding controller, which is bound to the template and renders it on the output.
http://emberjs.jsbin.com/vunegose/1
http://emberjs.jsbin.com/vunegose/1/edit
js
function getView($el){
return Ember.View.views[$el.closest(".ember-view").attr("id")];
}
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexController = Ember.Controller.extend({
entries:[]
});
App.IndexView = Ember.View.extend({
actions:{
testAction:function(){
this.get("controller.entries").pushObject("now:"+Date.now());
}
}
});
hbs
<script type="text/x-handlebars">
<h2> Welcome to Ember.js with paper.js example</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
<div class="canvas">
<canvas resize="true" id="canvas-1"></canvas>
</div>
<div class="output">
<b>output:</b>
{{#each entry in entries}}
<p>{{entry}}</p>
{{/each}}
</div>
</script>
paperscript (from http://paperjs.org/examples/chain/)
<script type="text/paperscript" canvas="canvas-1">
// Adapted from the following Processing example:
// http://processing.org/learning/topics/follow3.html
// The amount of points in the path:
var points = 25;
// The distance between the points:
var length = 35;
var path = new Path({
strokeColor: '#E4141B',
strokeWidth: 20,
strokeCap: 'round'
});
var start = view.center / [10, 1];
for (var i = 0; i < points; i++)
path.add(start + new Point(i * length, 0));
function onMouseMove(event) {
path.firstSegment.point = event.point;
for (var i = 0; i < points - 1; i++) {
var segment = path.segments[i];
var nextSegment = segment.next;
var vector = segment.point - nextSegment.point;
vector.length = length;
nextSegment.point = segment.point - vector;
}
path.smooth();
}
function onMouseDown(event) {
path.fullySelected = true;
path.strokeColor = '#e08285';
getView($("#canvas-1")).send("testAction");
}
function onMouseUp(event) {
path.fullySelected = false;
path.strokeColor = '#e4141b';
}
</script>
Related
I have the following index.html:
<!DOCTYPE html>
<html>
<body>
<script type="text/x-handlebars" id="index">
<ul>
{{#each todo in todos}}
<li>{{todo}}</li>
{{/each}}
</ul>
<button {{action 'generate'}}/>Generate a to-do</buton>
</script>
<script src="js/libs/jquery-1.10.2.js"></script>
<script src="js/libs/handlebars-1.1.2.js"></script>
<script src="js/libs/ember-1.6.1.js"></script>
<script src="js/app.js"></script>
</body>
</html>
And app.js:
App = Ember.Application.create();
App.Router.map(function() {});
App.IndexRoute = Ember.Route.extend({
model: function() {
return {todos: ['To-do 1', 'To-do 2']};
},
});
// This is a function I cannot change, because I don't own it.
// So I'm forced to get the updated model as the result of this.
// Here is some dummy-but-working implementation, for simulation purpose:
function generate(todolist) {
var n = todolist.todos.length + 1;
todolist.todos.push("To-do " + n);
return todolist;
}
App.IndexController = Ember.ObjectController.extend({
actions: {
generate: function() {
var oldToDoList = this.get('model');
var newToDoList = generate(oldToDoList);
this.set('model', newToDoList);
console.log(this.get('model').todos);
},
},
});
When I click on the generate button, I effectively see the growing to-dos array in console, but UI doesn't update.
Shouldn't #each content update automatically when completely replacing controller's model, or am I missing something?
your generate method doesn't actually generate a new array, so Ember won't notice that you've changed the property (because it's a reference to the same array). In your particular instance you should just use pushObject and Ember will know you're modifying the same array.
function generate(todolist) {
var n = todolist.todos.length + 1;
todolist.todos.pushObject("To-do " + n);
return todolist;
}
on a crusade to learn ember, I'm trying to create a blog with it, and now I'm on the pagination step.
Everything is working, except all of my posts with an id of over 100 don't show up in the beginning.
This is what my PostsIndexController looks like ->
Blog.PostsIndexController = Ember.ArrayController.extend({
sortProperties: ['id'],
sortAscending: false,
page: 1,
perPage: 8,
totalPages: (function() {
return Math.ceil(this.get('length') / this.get('perPage'));
}).property('length', 'perPage'),
pages: (function() {
var collection = Ember.A();
for(var i = 0; i < this.get('totalPages'); i++) {
collection.pushObject(Ember.Object.create({
number: i + 1
}));
}
return collection;
}).property('totalPages'),
hasPages: (function() {
return this.get('totalPages') > 1;
}).property('totalPages'),
prevPage: (function() {
var page = this.get('page');
var totalPages = this.get('totalPages');
if(page > 1 && totalPages > 1) {
return page - 1;
} else {
return null;
}
}).property('page', 'totalPages'),
nextPage: (function() {
var page = this.get('page');
var totalPages = this.get('totalPages');
if(page < totalPages && totalPages > 1) {
return page + 1;
} else {
return null;
}
}).property('page', 'totalPages'),
paginatedContent: (function() {
var start = (this.get('page') - 1) * this.get('perPage');
var end = start + this.get('perPage');
return this.get('arrangedContent').slice(start, end);
}).property('page', 'totalPages', 'arrangedContent.[]'),
selectPage: function(number) {
this.set('page', number);
}
});
and this is what my template looks like ->
{{#each post in paginatedContent}}
{{ render 'posts/post' post}}
{{/each}}
{{#if hasPages}}
<div class="pagination">
{{#if prevPage}}
<a href="#" class="previous_page" rel="previous" {{action "selectPage" prevPage}}>← Newer</a>
{{else}}
<span class="previous_page disabled">← Newer</span>
{{/if}}
{{#if nextPage}}
<a href="#" class="next_page" rel="next" {{action "selectPage" nextPage}}>Older →</a>
{{else}}
<span class="next_page disabled">Older →</span>
{{/if}}
</div>
{{/if}}
I think the problem is in the way arrangedContent is setting up the array --
paginatedContent: (function() {
var start = (this.get('page') - 1) * this.get('perPage');
var end = start + this.get('perPage');
return this.get('arrangedContent').slice(start, end);
}).property('page', 'totalPages', 'arrangedContent.[]'),
But, am a bit confused with what arrangedContent is, and how to fix this issue. Help much appreciated!
I Haven't quite figured out why this happened, but instead of sorting by id, I just sorted by created_at, which fixed the issue.
sortProperties: ['created_at'],
You can create an other "real" numeric type ID for the model, and order by this field. Works fine!
App.Msgboard = DS.Model.extend({
numericId: function(){
var id = this.get('id');
if (id) { return +id; }
}.property('id'),
name: DS.attr('string')
});
I'm rendering two lists based on two arrays in my controller. The contents of the first array are added immediately, but the second is populated after a delay of 500ms. The list on top scrolls as expected, but the second (delayed) one doesn't. The delay simulates loading content through an ajax request via Ember data, which has the same effect. Any clues?
The code is available here: http://jsbin.com/owejoz/5/
This is how the list is displayed:
<div class="content">
<div class="content_list" data-scrollable="x">
{{#each content1}}
<div class="list_item">{{name}}</div>
{{/each}}
</div>
</div>
<div class="content">
<div class="content_list" data-scrollable="x">
{{#each content2}}
<div class="list_item">{{name}}</div>
{{/each}}
</div>
</div>
The javascript code is as follows:
App.IndexController = Ember.ArrayController.extend({
content1: [],
content2: []
});
App.IndexRoute = Ember.Route.extend({
setupController: function(controller) {
var content1 = [];
var content2 = [];
for (var j = 1; j < 5; j++) {
content1.push({name: "Item " + j});
}
controller.set('content1', content1);
setTimeout(function () {
for (var i = 1; i < 5; i++) {
content2.push({name: "Delayed item " + i});
}
controller.set('content2', content2);
}, 500);
}
});
Change your setTimeout to Ember.run.later :
Ember.run.later(function () {
for (var i = 1; i < 5; i++) {
content2.push({name: "Delayed item " + i});
}
controller.set('content2', content2);
}, 500);
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"}}
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
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]);
}
},
});
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."
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.
I'm trying to use Handlebars helper, but the helper view does not get updated.
The view,
<script type="text/x-handlebars">
{{#view App.NumberView}}
<button {{action changeNumber}}>Change number</button><br>
{{formatNumber view.number}} <br>
{{view.number}}
{{/view}}
</script>
The code,
App = Ember.Application.create({});
App.NumberView = Ember.View.extend({
number: 5,
changeNumber: function(e) {
this.set('number', this.get('number') + 1);
}
});
Em.Handlebars.registerHelper('formatNumber', function(timePath, options) {
var number = Em.Handlebars.getPath(this, timePath, options);
return new Handlebars.SafeString("Formated number: " + number);
});
The live example in jsfiddle http://jsfiddle.net/LP7Hz/1/
So what's wrong ?
Because Handlebars helpers in Ember which aren't bound helpers. So you can instantiate an auxiliary view to do that like this fiddle:
http://jsfiddle.net/secretlm/qfNJw/2/
HTML:
<script type="text/x-handlebars">
{{#view App.NumberView}}
<button {{action changeNumber}}>Change number</button><br>
{{formatNumber view.number}} <br>
{{view.number}}
{{/view}}
</script>
Javascript:
App = Ember.Application.create({});
App.NumberView = Ember.View.extend({
number: 5,
changeNumber: function(e) {
this.set('number', this.get('number') + 1);
}
});
App.registerViewHelper = function(name, view) {
Ember.Handlebars.registerHelper(name, function(property, options) {
options.hash.contentBinding = property;
return Ember.Handlebars.helpers.view.call(this, view, options);
});
};
inlineFormatter = function(fn) {
return Ember.View.extend({
tagName: 'span',
template: Ember.Handlebars.compile('{{view.formattedContent}}'),
formattedContent: (function() {
if (this.get('content') != null) {
return fn(this.get('content'));
}
}).property('content')
});
};
App.registerViewHelper('formatNumber', inlineFormatter(function(content) {
return new Handlebars.SafeString("Formated number: " + content);
}));
This link is useful: http://techblog.fundinggates.com/blog/2012/08/ember-handlebars-helpers-bound-and-unbound/ from #Jo Liss
You're looking for a bound helper, which doesn't exist just yet. There is a Pull Request and associated discussion.