Emberjs bindAttr inside of #each - ember.js

Code for view is
Ember.View.extend({
template: Ember.Handlebars.compile(html), // html is in string
content: function() {
return [
{ Title: "Dashboard", ID: "dashboard" },
{ Title: "Invoices", ID: "invoices" },
{ Title: "Expenses", ID: "expenses" },
{ Title: "People", ID: "people" },
{ Title: "Reports", ID: "reports" },
{ Title: "Settings", ID: "settings" }
]},
iconClass: function(link){
return "icon icon-" + link.ID
}
});
Template (show above as "html") looks like this:
<ul>
{{#each link in view.content}}
<li>
<a>
<span class="icon" {{bindAttr class="view.iconClass(link)"}}></span>
<span class="title">{{link.Title}}</span>
</a>
</li>
{{/each}}
</ul>
This renders
<span class="icon" data-bindattr-2="2"></span>
So additional class attribute is not rendered. Am I doing something wrong with scope or?
NOTE:
I changed my code to show what I want to do.

EDIT
According to the new question, you'll have to use an Ember.CollectionView:
App.FooCollectionView = Ember.CollectionView.extend({
itemViewClass: Ember.View.extend({
iconClass: function() {
return "icon-dashboard icon " + this.get('content.ID');
}.property('content.ID')
})
});
As you can see, each itemViewClass has a property iconClass which depends on its content.id.
Now you'll have to add the collection view in the template of the view FooView:
<ul>
{{#collection App.FooCollectionView contentBinding="view.content"}}
<li>
<a>
<span {{bindAttr class="view.iconClass"}}>foo</span>
<span class="title">{{view.content.Title}}</span>
</a>
</li>
{{/collection}}
</ul>
Here we are using the {{collection}} Handlebars helper, and we bind the content of the FooView to the FooCollectionView.
It will automatically create an itemViewClass instance for each object in the CollectionView.content, set the its to the associated object, and add it to the view.
I suggest you to read the Ember.CollectionView documentation.
And you could try this solution in this JSFiddle.

For others who are having the problem with bindAttr resulting in something like:
data-bindattr-1="1"
make sure you are using
{{bindAttr src="myvariable"}}
instead of {{bindAttr src="{{myvariable}}"}}
Might save you some time searching for the answer, this was what caused my code not to work.

Another simple way to do this is to add a computed property to your model. In the example below I needed a specialized style atribute.
Model ----
App.Photo = Em.Object.extend(
objectId: null
url: ""
style: (->
"background-image:url('" + #get("url") + "')"
).property("url")
)
Template -----
{{#each item in App.photoController}}
<div {{bindAttr style="item.style"}}></div>
{{/each}}

Related

Serving Static Pages ember.js

I'm currently retrofitting an old site and added ember. Previously when the user went products > product there was a link to a static page from there.
What's the best way to route to these static pages? (sellsheet in the object)
{
id: 32,
room: "String",
subroom: "String",
category: "String",
image: "Content/Images/Products/img.PNG",
name: "String",
description: "String",
bullets: [
{ content: "String" },
{ content: "String" },
{ content: "String" }
],
sellsheet: "Content/Sellsheets/conveyor.html"
}
I know this isn't the best way of doing it but it fit my needs.
I ended up just displaying the static page in an iframe at the bottom of the product page. Whenever you click view more, I hide the page with jQuery then show the iframe which gets the html loaded in through the anchor tag. Then I added a 'view less button' that hides the iframe and shows the page again.
HTML
<script type="text/x-handlebars" id="product">
<div id="valueprop-container">
<div class="centered">
<div class="col-left"><img {{bind-attr src=image}} /></div>
<div class="col-right">
<h2>{{{name}}}</h2>
<p>{{{description}}}</p>
<ul>
{{#each bullets}}
<li><span>{{{content}}}</span></li>
{{/each}}
</ul>
{{#if sellsheet}}
View More
{{/if}}
</div>
</div>
</div>
<div class="shadow"></div>
<div class="sellsheet">
<button class="expand">View Less</button>
<iframe name="frame" width="100%" height="100%" allowfullscreen style="position: absolute; border: none;"></iframe>
</div>
</script>
View
App.ProductView = Ember.View.extend({
didInsertElement: function(){
var productPage = $('#valueprop-container');
var sellSheet = $('.sellsheet');
$('.sell-sheet-click').click('on', function(){
productPage.hide();
sellSheet.show();
});
$('.sellsheet').click('on', function(){
productPage.show();
sellSheet.hide();
});
}
});

Ember - Adding an extra action for #link-to

I have a simple Ember application where I have a dynamically generated list of links-to. On a single click per link, I show some detail about the clicked item.
I also want to add "ondblclick" event to the link-to. Is this at all possible? If not, I'm not attached to having link-to for single and double clicks.
I want to be able to keep the functionality I already have for the single-click event, and also add a double-click event (ideally, without firing a single-click event meanwhile ). I want to be able to store the double clicked titles and ids associated with them.
I tried using Ember.View object for this (commented out below), and I tried adding {{action "collectDoubleClicked" on="doubleClick"}} into the link-to, but that doesn't work. No luck so far.
Any help will be much appreciated.
Here is my HTML:
<script type="text/x-handlebars" data-template-name="application">
<div id="headerDiv">
<ul>
<li>
<image src="logo.png" style="width:439px;height:102px;"/>
</li>
<li>
{{#link-to 'home'}}Home{{/link-to}} | {{#link-to 'help'}}Help{{/link-to}}
</li>
</ul>
</div>
<div class="collections">
{{partial 'collections'}}
</div>
<div class="statistics">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" id="collections">
<div id='collectionsDiv'>
{{#each}}
{{#link-to 'item' this }} <!-- {{action "collectToSearch" on="doubleClick"}} -->
{{title}}
{{/link-to}}
{{/each}}
</div>
</script>
And my JavaScript:
App = Ember.Application.create();
App.Router.map(function () {
this.resource('home', {
path: '/'
});
this.resource('help');
this.resource('item', {
path: ':item_id'
});
});
App.ApplicationRoute = Ember.Route.extend({
model: function () {
return items;
}
});
var items = [{
id: 1,
title: 'Item 1',
contentType: 'Image',
description: 'description 1'
}, {
id: 2,
title: 'Item 2',
contentType: 'Text',
description: 'description 2'
}, {
id: 3,
title: 'Item 3',
contentType: 'Undefined',
description: 'description 3'
}];
App.ApplicationController = Ember.ArrayController.extend({
actions: {
collectDoubleClicked
function () {
console.log("collectToSearch: ", this.get('model.title'));
}
}
});
/**
App.Application = Ember.View.extend({
itemTitles: [items.length],
itemIds: [items.length],
itemCountDoubleClick: 0,
doubleClick: function (title, id) {
console.log("double click");
itemTitles[itemCountDoubleClick] = title;
itemIds[itemCountDoubleClick] = id;
itemCountDoubleClick++;
return false;
}
});
**/
You can try something like this:
{{#link-to 'item' this }}
<span {{action "doubleClickAction" on="doubleClick"}}>
{{title}}
</span>
{{/link-to}}
If my understand is true, span catch event before link.
But I think, we have more true way
I don't think there's a way to handle doubleClick without triggering singleClick. Have a look at this jsBin. Before doubleClick is even triggered, click was already triggered twice. I understand what you're trying to do and it makes sense.

EmberJS: How to update model attributes

I've got a list of messages that are provided by a Rails backend. What I need is when the "toggle_visibility" action button is pressed, it would toggle the "publicly_viewable" property. This means, making a corresponding REST call (to effect the database) and changing the state of the corresponding cached message. Here is where I'm at so far.
Here's what I've got so far, that manages to end up on the debug console:
# app.js
App.Store = DS.Store.extend({
revision: 12,
adapter: DS.RESTAdapter.extend({
url: 'http://localhost:3000'
})
});
App.Message = DS.Model.extend({
body: DS.attr('string'),
mobile_number: DS.attr('string'),
publicly_viewable: DS.attr('boolean'),
created_at: DS.attr('date')
});
App.Router.map(function() {
this.resource('messages');
});
App.MessagesRoute = Ember.Route.extend({
model: function() { return App.Message.find() }
});
App.MessagesController = Ember.ArrayController.extend({
toggle_visibility: function(){
debugger;
}
});
# index.html
{{#each model}}
<button class="close" {{action toggle_visibility this}}><i class="icon-eye-close"></i></button>
<p class="message_body lead">{{body}}</p>
<small class="source_number">from {{mobile_number}}, received {{date created_at}}</small>
{{/each}}
I've been spending the past few hours reading through the Ember Guides and while I've gotten an idea on what the different classes there are, I still can't visualize clearly how to go about it. Particularly, I'm not sure if this should be a route concern or a controller, and I know that if ever it was a controller responsibility, I know that it should be on an ObjectController but I've been having trouble making it work.
You can use ArrayController#itemController and define a controller for the individual record in your ModelArray. Then you have to specify in the Array Controller the Object Controller responsible for a single object, which you have to reference as well in Handlebars. You can do something like this:
JS:
App.MessageController = Ember.ObjectController.extend({
visibilityClass: function() {
var visibility = this.get('model.publiclyViewable');
return 'toggle-visibility mdi-action-visibility%#'.fmt(
visibility ? '':'-off'
);
}.property('model.publiclyViewable'),
actions: {
toggleVisibility: function() {
var model = this.get('model');
model.toggleProperty('publiclyViewable');
model.save();
}
}
});
Handlebars:
<script type="text/x-handlebars" data-template-name="messages">
<!--
At this point the {{each}} helper will know how to lookup for
the controller simply by it's name
-->
{{#each model itemController="message"}}
<div class="panel panel-primary">
<div class="panel-heading">
<div class="pull-left">
<h3 class="panel-title">{{title}}</h3>
</div>
<div class="pull-right">
<a {{action 'toggleVisibility'}}>
<i class={{visibilityClass}} style="color: #FFF"></i>
</a>
</div>
</div>
<div class="panel-body">
{{body}}
</div>
<div class="panel-footer">
<i class="mdi-communication-quick-contacts-dialer"></i> {{mobileNumber}}
<i class="mdi-notification-event-note"></i> {{createdAt}}
</div>
</div>
{{/each}}
</script>
(see fiddle)
Note: Updated to Ember 1.11.x-beta and changed the code a little bit

Mark the current detail entry in a master list

I have a list of users which are displayed in a master view on the left side (Twitter Bootstrap CSS). Details of each user can be shown by clicking the show button. They will be displayed on the right side (detail).
How can I remove the show button for the currently displayed user? e.g. #/users/1 shouldn't render the show button for the first user.
index.html
<script type="text/x-handlebars" data-template-name="users">
<div class='row'>
<div class='span4'>
<table class='table table-striped'>
{{#each model}}
<tr>
<td>{{lastName}}</td>
<td>{{#linkTo 'user' this}}<button class="btn" type="button">show</button>{{/linkTo}}</td>
</tr>
{{/each}}
</table>
</div>
<div class='span8'>
{{outlet}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="user">
<h1>{{firstName}} {{lastName}}</h1>
</script>
app.js
App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
})
App.Router.map(function() {
this.resource('users', function() {
this.resource('user', { path: ':user_id' })
})
});
App.UsersRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
App.User = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string')
})
App.User.FIXTURES = [{
id: 1,
firstName: "Bill",
lastName: "Clinton"
}, {
id: 2,
firstName: "Barack",
lastName: "Obama"
}]
Ember provides some support for doing what you want. By default it sets the "active" css class on the selected element. You can find more information about that here: http://emberjs.com/api/classes/Ember.LinkView.html (note that the {{#linkTo}} is just a helper based on the LinkView).
The simplest way to override this behavior, since instead of "active" you want to hide the button, would be to make use of the hide class that comes with Twitter Bootstrap. So your users template would look like:
<script type="text/x-handlebars" data-template-name="users">
<div class='row'>
<div class='span4'>
<table class='table table-striped'>
{{#each model}}
<tr>
<td>{{lastName}}</td>
<td>{{#linkTo 'user' this activeClass="hide"}}<button class="btn" type="button">show</button>{{/linkTo}}</td>
</tr>
{{/each}}
</table>
</div>
<div class='span8'>
{{outlet}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="user">
<h1>{{firstName}} {{lastName}}</h1>
</script>

Ember.js Router: Embedded resources

I'm creating an Ember application to display twitter feeds but I am having trouble with displaying individual tweets through embedded resources.
The code is as follows:
Templates
<script type="text/x-handlebars" data-template-name="tweets">
<div id="stream">
{{#each tweet in controller}}
<div class="tweet">
<p class="tweet_text">{{tweet.text}}</p>
<p> {{#linkTo "tweet" tweet}} {{tweet.id}} {{/linkTo}}</p>
</div>
{{/each}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="tweet">
<div id="detail">
{{text}}
</div>
</script>
Router
window.App = Ember.Application.create({
LOG_TRANSITIONS: true
});
App.Router.map(function(){
this.resource('tweets',function(){
this.resource('tweet',{path: ':tweet_id'})
});
});
// (1) App.Router.map(function(){
// this.resource('tweets')
// this.resource('tweet',{path: ':tweet_id'})
// });
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('tweets');
}
});
App.TweetsRoute = Ember.Route.extend({
model: function(){
var me = [];
$.getJSON("http://search.twitter.com/search.json?q=emberjs&rpp=200&count=200&callback=?",
{},
function (data) {
$.each(data.results,function(k,tweet){
var tweet = App.Tweet.create({
created_at: tweet.created_at,
from_user: tweet.from_user,
profile_image_url: tweet.profile_image_url,
text: tweet.text,
id: tweet.id
});
me.pushObject( tweet );
});
});
return me;
}
});
Objects & Controllers
App.TweetsController = Ember.ArrayController.extend({});
App.Tweet = Ember.Object.extend({
created_at: "",
from_user: "",
profile_image_url: "",
text: "",
id: 0
})
As you can see, I have a commented our router (1) which works in finding the correct tweet, and rendering it in the tweet template. However, I would like this route to be nested so that I can implement it as a Master-Detail application.
Using the LOG_TRANSITIONS, I can see that the correct routes are initialised, but I cannot get the nested resource path to render.
Any ideas would be hugely appreciated, thanks in advance.
I got this working. For anyone stuck on something similar, this is how I did it:
Templates - Changed the {{#linkTo}} "tweet"... to {{#linkTo}} "tweets.tweet"... AND added an {{outlet}}
<script type="text/x-handlebars" data-template-name="tweets">
<div id="stream">
{{#each tweet in controller}}
<div class="tweet">
<p class="tweet_text">{{tweet.text}}</p>
<p> {{#linkTo "tweets.tweet" tweet}} {{tweet.id}} {{/linkTo}}</p>
</div>
{{/each}}
</div>
{{ outlet }}
</script>
Router - Changed 'this.resource' to 'this.route'
App.Router.map(function(){
this.resource('tweets',function(){
this.route('tweet',{path: ':tweet_id'})
});
});
Caveat
I think this is a workaround and that the nested resource was the correct approach in this context. I understand that a nested route should be "a verb" or action route. I would still be grateful if anyone knows the correct approach to the question but hope the above helps others where relevant.