Here I am at the beginning of a project. I am using zurb-foundation and marionette. I have an element that is rendering a template that is supposed to be tabs. As it stands:
define([
"backbone",
"marionette"
], function(Backbone, Marionette) {
MyItem = Backbone.Marionette.ItemView.extend({
template: "#design-tabs",
className: "section-container tabs",
onRender: function() {
$(this.el).foundation();
}
});
return MyItem;
});
there are no tabs. I think this is because the <div> being rendered to replace the <script> tag in the template does not have a particular data attribute (data-section). I went looking for something like 'className' that I could add to the ItemView declaration above in order to include data-attributes, but I have come up dry. I want something like:
MyItem = Backbone.Marionette.ItemView.extend({
template: "#design-tabs",
data: {
data-section: "",
data-foo: "bar"
},
className: "section-container tabs",
.
.
.
How do I add data attributes to the <div> (or otherwise) that replaces the <script> in a template?
To add data properties, use Backbone's attributes hash:
var MyView = Backbone.Marionette.ItemView.extend({
template: "#design-tabs",
className: "section-container tabs",
attributes: {
"data-section": "",
"data-foo": "bar"
}
});
Documentation: http://backbonejs.org/#View-attributes
If you prefer or need dynamic values, you can do in this way:
attributes: function() {
return {
'src': this.model.get('avatar_src')
};
}
Related
I have a list of foos:
[
{ name: 'Foo 1' type: 'important' },
{ name: 'Foo 2' type: 'normal' },
{ name: 'Foo 3' type: 'purple' }
]
I want to render that list, but some of them have special handling -- not just a class, but entirely different markup.
My first instinct would be to use the component helper:
{{#each foos as |foo|}}
{{component (join 'foo-' foo.type) foo=foo}}
{{/each}}
The downside to that is that I must define a component for each type and the server may send me new types that I don't know about. So I'd like to fall back to a basic foo-generic component if the foo-${type} component isn't defined.
To do that, I figured I'd create a helper:
{{#each foos as |foo|}}
{{foo-entry foo}}
{{/each}}
// app/helpers/foo-entry.js
Ember.Helper.helper(function([ foo ]) {
// ...
});
But I'm left with two questions:
how do I get a container inside that helper so I can check whether component:foo-${foo.type} exists?
how do I invoke the component helper from within another helper?
I think I can do it with a component instead of a helper:
// app/pods/foo-entry/component.js
export default Ember.Component.extend({
tagName: null,
foo: null,
componentName: Ember.computed('foo.type', function() {
const type = this.get('foo.type');
const exists =
this.container.lookupFactory(`component:foo-${type}`) != null ||
this.container.lookupFactory(`template:foo-${type}`) != null;
return exists ? `foo-${type}` : 'foo-generic';
})
})
{{!-- app/pods/foo-entry/template.hbs --}}
{{component componentName foo=foo}}
I have this:
App.LoginRegisterController = Ember.ObjectController.extend({
init: function () {
this.set('products', this.store.find('product'));
},
subscriptionOptions: function () {
return this.get('products').map(function (product) {
return {id: product.id, title: product.title};
});
}.property('products')
});
Then in my template:
{{view "Ember.Select"
content=subscriptionOptions
optionValuePath='content.id'
optionLabelPath='content.title'}}
However subscriptionOptions only get called once before the products are populated. If I change the template to:
{{view "Ember.Select"
content=products
optionValuePath='content.id'
optionLabelPath='content.title'}}
The data gets populated correctly, but I need to insert an option into the select that isn't related to any of the models, so I need to populated it with references. How can I get the select to update from subscriptionOptions as products change?
Ah, I needed to use #each on the property and get() when accessing the title for it to show up properly.
App.LoginRegisterController = Ember.ObjectController.extend({
init: function () {
this.set('products', this.store.find('product'));
},
subscriptionOptions: function () {
return this.get('products').map(function (product) {
return {id: product.get('id'), title: product.get('title')};
});
}.property('products.#each')
});
I am trying to build a blog application with Ember. I have models for different types of post - article, bookmark, photo. I want to display a stream of the content created by the user for which I would need a collection of objects of all these models arranged in descending order of common attribute that they all have 'publishtime'. How to do this?
I tried something like
App.StreamRoute = Ember.Route.extend({
model: function() {
stream = App.Post.find();
stream.addObjects(App.Bookmark.find());
stream.addObjects(App.Photo.find());
return stream;
}
}
where the resource name is stream
But it doesn't work. I am using the latest released Ember 1.0.0 rc 2 and handlebars 1.0.0 rc 3 with jQuery 1.9.1 and ember-data.
Probably the way I am trying to achieve this whole thing is wrong. The problem is even if I am able to use the collection of objects of multiple models to iterate in the template, I would still need to distinguish between the type of each object to display its properties apart from the common property of 'publishtime'.
You can use a computed property to combine the various arrays and then use Javascript's built in sorting to sort the combined result.
Combining the arrays and sorting them
computed property to combine the multiple arrays:
stream: function() {
var post = this.get('post'),
bookmark = this.get('bookmark'),
photo = this.get('photo');
var stream = [];
stream.pushObjects(post);
stream.pushObjects(bookmark);
stream.pushObjects(photo);
return stream;
}.property('post.#each', 'bookmark.#each', 'photo.#each'),
example of sorting the resulting computed property containing all items:
//https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort
streamSorted: function() {
var streamCopy = this.get('stream').slice(); // copy so the original doesn't change when sorting
return streamCopy.sort(function(a,b){
return a.get('publishtime') - b.get('publishtime');
});
}.property('stream.#each.publishtime')
});
rendering items based on a property or their type
I know of two ways to do this:
add a boolean property to each object and use a handlebars {{#if}} to check that property and render the correct view
extend Ember.View and use a computed property to switch which template is rendered based on which type of object is being rendered (based on Select view template by model type/object value using Ember.js)
Method 1
JS:
App.Post = Ember.Object.extend({
isPost: true
});
App.Bookmark = Ember.Object.extend({
isBookmark: true
});
App.Photo = Ember.Object.extend({
isPhoto: true
});
template:
<ul>
{{#each item in controller.stream}}
{{#if item.isPost}}
<li>post: {{item.name}} {{item.publishtime}}</li>
{{/if}}
{{#if item.isBookmark}}
<li>bookmark: {{item.name}} {{item.publishtime}}</li>
{{/if}}
{{#if item.isPhoto}}
<li>photo: {{item.name}} {{item.publishtime}}</li>
{{/if}}
{{/each}}
</ul>
Method 2
JS:
App.StreamItemView = Ember.View.extend({
tagName: "li",
templateName: function() {
var content = this.get('content');
if (content instanceof App.Post) {
return "StreamItemPost";
} else if (content instanceof App.Bookmark) {
return "StreamItemBookmark";
} else if (content instanceof App.Photo) {
return "StreamItemPhoto";
}
}.property(),
_templateChanged: function() {
this.rerender();
}.observes('templateName')
})
template:
<ul>
{{#each item in controller.streamSorted}}
{{view App.StreamItemView contentBinding=item}}
{{/each}}
</ul>
JSBin example - the unsorted list is rendered with method 1, and the sorted list is rendered with method 2
It's a little complicated than that, but #twinturbo's example shows nicely how to aggregate separate models into a single array.
Code showing the aggregate array proxy:
App.AggregateArrayProxy = Ember.ArrayProxy.extend({
init: function() {
this.set('content', Ember.A());
this.set('map', Ember.Map.create());
},
destroy: function() {
this.get('map').forEach(function(array, proxy) {
proxy.destroy();
});
this.super.apply(this, arguments);
},
add: function(array) {
var aggregate = this;
var proxy = Ember.ArrayProxy.create({
content: array,
contentArrayDidChange: function(array, idx, removedCount, addedCount) {
var addedObjects = array.slice(idx, idx + addedCount);
addedObjects.forEach(function(item) {
aggregate.pushObject(item);
});
},
contentArrayWillChange: function(array, idx, removedCount, addedCount) {
var removedObjects = array.slice(idx, idx + removedCount);
removedObjects.forEach(function(item) {
aggregate.removeObject(item);
});
}
});
this.get('map').set(array, proxy);
},
remove: function(array) {
var aggregate = this;
array.forEach(function(item) {
aggregate.removeObject(item);
});
this.get('map').remove(array);
}
});
I have a simple add event in Backbonejs, I also have template:
initialize: function()
{
this.template = _.template($("#person-template").html());
}
renderperson: function(model)
{
//model.name is "Jack" for example
//Now I want to render this template with Name replaced with "Jack"?
$("#somelement").append( ); //What to do here???
}
template is simple:
<script type="text/template" id="person-template">
<div class="person"><%= Name %></div>
</script>
$("#somelement").append( this.template(this.model.toJSON()) );
you may also need to add
_.bindAll(this)
to your initialize method
may be this can help you:
http://backbonejs.org/#View-render
var Bookmark = Backbone.View.extend({
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
i have drop downlist in emberjs and i declare it like that below
{{view Ember.Select contentBinding="ResAdmin.adminController.serviceAreaList" selectionBinding="ResAdmin.adminController.serviceAreaSelection" optionLabelPath="content.ZipCode" optionValuePath="content.ServiceAreaID"}}
but i want to use kendo ui's dropdownlist which i can use like below
<input id="dropDownList" />
$(document).ready(function() {
$("#dropDownList").kendoDropDownList({
dataTextField: "text",
dataValueField: "value",
dataSource: [
{ text: "Item1", value: "1" },
{ text: "Item2", value: "2" }
]
});
});
i want to use kendoui dropownlist with ember
Try it like wycats' examples for jqueryUI
App.KendoSelectView = Em.Select.extend({
didInsertElement: function () {
this.$().kendoDropDownList({
dataTextField: ....
});
}
});
You can probably pick up the attributes for the DDL from the select properties.