In my view I wanted to mockup a collection of featured items.
I want to do something like
{{#each featuredItems}}
{{title}}
{{/each}}
in my controller I have
App.ItemsController = Ember.ArrayController.extend
featuredApps = [
title: 'hi'
,
title: 'Ok'
,
title: "Three"
]
How do I iterate over this simply in my view? Right now I get this error:
Assertion failed: Expected hash or Mixin instance, got [object Array]
If I don't do it that way, then how would I do it if I added a feature flag to the Todo fixtures and wanted to iterate over a collection in the todos.hbs file specifically for featured and then below that show all of them?
Basically how do I do this
{{#each featured}}
{{title}}
{{/each}}
{{#each}}
{{title}}
{{/each}}
That puts the featured ones on the top and then everything all together in the bottom.
Maybe you can use an if statement to check if an item is featured or not and then display it. Something like this:
<ul>
{{#each item in model}}
{{#if item.featured}}
<h1>{{item.color}}</h1>
{{/if}}
{{/each}}
</ul>
You can check this jsBin:
http://emberjs.jsbin.com/OTotazu/2/edit
Related
I'm new to ember. I have a demo app working, and I'm moving towards making it look nice.
One issue I'm starting to grapple with is how to manipulate DOM elements. Coming from a server-side world, it's been pretty easy to just throw some jquery at stuff like this. Doesn't appear to be as straightforward in ember. But I'm probably missing something or approaching it wrong.
The immediate problem is: I have a list of 40-some <li> elements and I want to create a toggle to show/hide the list after the first 10 items.
I got something to work in my component like this:
import Ember from 'ember';
let $ = Ember.$;
export default Ember.Component.extend({
didInsertElement() {
this._super(...arguments);
Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
},
afterRenderEvent() {
let listTotal = $("#myList li").length;
$("#myList li").slice(10, listTotal).hide();
}
});
The problem is that when actions trigger and the view is re-rendered, afterRenderEvent() doesn't get called again, and the list shows in its entirety.
The above component corresponds to this template:
<ul id="myList">
{{#each aggs.categories as |category|}}
<li><a href="#" {{action (action add "filter_breadcrumb" category.key)}}>{{category.key}} ({{category.doc_count}})</a></li>
{{/each}}
</ul>
Is there a way to get around this? OR, is there a more "ember" way to approach this problem (and DOM manipulation in general)?
Introduce showCount property in controller and have action to set showCount to total list count.
import Ember from 'ember';
export default Ember.Component.extend({
showCount: 10,
actions: {
setShowCount(count) {
//you can set total count
this.set('showCount', count);
}
}
});
Install ember truth helpers addon or write computed property to check.
<ul id="myList">
{{#each aggs.categories as |category index|}}
<li style="{{if (gt index showCount) 'display:none'}}"><a href="#" {{action (action add "filter_breadcrumb" category.key)}}>{{category.key}} ({{category.doc_count}})</a></li>
{{/each}}
</ul>
If you don't want to display then you can just iterate just showCount aggs.categories alone by writing computed property or using ember-composable-helpers junk method
<ul id="myList">
{{#each (chunk showCount aggs.categories) as |category index|}}
<li><a href="#" {{action (action add "filter_breadcrumb" category.key)}}>{{category.key}} ({{category.doc_count}})</a></li>
{{/each}}
</ul>
I have a collection of models in my Ember.js app, which I would like to render. The catch is that I want to be able to specify a specialized view and controller for each of the models.
The controller part seems to be easy: I would just wrap the array in an ArrayController and implement itemController method. The view part is where it gets tricky. I don't see an obvious idiomatic way of doing this.
The best way we came up with is the combination of ArrayController and CollectionView with an overridden createChildView. For instance:
createChildView: function(viewClass, attrs) {
var viewInstance,
widgetType = attrs.content.get('type');
// lookup view, if found, use it, if not, pass empty view
var viewDefined = this.container.lookup('view:' + widgetType);
var createWidgetType = viewDefined ? widgetType : 'empty';
// create view instance from widgetType name
// it causes lookup in controller
viewInstance = this._super(createWidgetType, attrs);
// if `attrs.content` is controller (item of `ComponentsController`)
// set it as controller of newly created view
if(attrs.content.get('isController')) {
viewInstance.set('controller', attrs.content);
}
return viewInstance;
}
This feels unnecessarily convoluted, I don't like that I have to connect the view with the controller manually like that. Is there a cleaner way?
You can create a component, which will act as controller and have a view associated with it:
App.XItemComponent = Ember.Component.extend({
controllerProperty: '!',
tagName: 'li'
});
Then, you can just do:
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each model }}
{{ x-item item=this }}
{{/each}}
</ul>
</script>
http://emberjs.jsbin.com/wehevixolu/1/edit?html,js,output
I'd use the {{render}} helper. It'll create a view and controller for each instance.
{{#each item in model}}
{{render "item" item}}
{{/each}}
Example: http://emberjs.jsbin.com/vuwimu/2/edit?html,js,output
Render helper guide: http://emberjs.com/guides/templates/rendering-with-helpers/#toc_the-code-render-code-helper
Additionally:
In your comment you mentioned you want different controller/view types for particular model types. This could be done like this:
{{#each item in model}}
{{#if item.typeX}}
{{render "itemX" item}}
{{/if}}
{{#if item.typeY}}
{{render "itemY" item}}
{{/if}}
{{/each}}
or if you'd choose to go with components:
{{#each item in model}}
{{#if item.typeX}}
{{component-x item=item}}
{{/if}}
{{#if item.typeY}}
{{component-y item=item}}
{{/if}}
{{/each}}
Without knowing what you are trying to accomplish in more detail it’s hard to tell what the best solution is.
Template:
{{#each document in documents}}
<div class="col-md-6" {{action "selectDocument" document}}>{{view Ember.Checkbox checked=document.isSelected}} {{document.name}}</div>
{{/each}}
Controller:
App.IndexDocumentsController = Ember.ArrayController.extend({
actions: {
selectDocument: function(document){
document.set('isSelected', !document.get('isSelected'));
}
}
});
When I click on the div, the checkbox toggles 'checked' property. But when I click on the ckeckbox - nothing happens. What can be the reason?
UPDATED
Link to jsbin: http://emberjs.jsbin.com/nuvocumuteto/1/edit?html,css,js,output
The issue is that when you click on the checkbox 2 things happen.
the checkbox toggles the isActive property, then
the selectRow action is ran which again toggles the isActive property
So the isActive property ends up staying in the same state that it was.
In your case I would get rid of the action, wrap the checkbox in a <label> and set the label to display: block.
The template would look like
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each item in model}}
<li {{bind-attr class="item.isActive:active"}}><label>{{input type="checkbox" checked=item.isActive}}{{item.name}}</label></li>
{{/each}}
</ul>
</script>
and the css would look like
label {
display: block;
}
you would then be able to get rid of the selectRow action completely because clicking on the label will trigger the checkbox.
You can see a working bin here: http://emberjs.jsbin.com/nuvocumuteto/3/edit
I would argue that you are not following "The Ember Way" in two different ways here.
First, Ember.Checkbox is an internal Ember class (http://emberjs.com/api/classes/Ember.Checkbox.html). The recommended way to render a checkbox is to use the Handlebars input helpers (http://emberjs.com/guides/templates/input-helpers/#toc_checkboxes). This is just wrapping Ember.Checkbox anyway.
Second, if you want to update the value of isSelected, the "Ember Way" is to use two-way data bindings. Your code uses one-way data-binding when it reads document.isSelected and then tries to manually re-create the the data-binding in the other direction when the user clicks by manually writing a selectDocument action and calling it from an {{action}}.
Instead, simply bind the Ember Handlebars Input Helper directly to your value like this:
{{#each document in documents}}
<div class="col-md-6">{{input type="checkbox" checked=document.isSelected}} {{document.name}}</div>
{{/each}}
So far, it looks like Ember does not work as I expected, and I'm hoping I've just missed something. What I need to do is iterate over my model, a blob of JSON made up of arrays and objects, and build out a form. When the user marks a checkbox, the controllers Action updates the model.
Here's how I want it to work...
<form {{action 'answerSupplied'}}>
{{#each model.questions}}
<h3>{{text}}</h3>
{{#each answers}}
{{input type='../type' answerId='id' data-bind-questionNum='../id' text}}
{{/each}}
{{/each}}
</form>
Here's how I can get close to that...
<form {{action 'answerSupplied' this}}>
{{#each model.questions}}
<h3>{{text}}</h3>
{{#each answers}}
{{formbuilder ../type id ../id text}}
{{/each}}
{{/each}}
</form>
==================
Handlebars.registerHelper('formbuilder', function(type, id, qnum, text, options)
{
// console.log(options);
var q_type = options.contexts[0][type],
a_id = options.contexts[1].id,
q_number = options.contexts[0][qnum],
a_text = options.contexts[1].text;
return new Handlebars.SafeString(
'<input type='+ q_type +' id='+ a_id +' name='+
q_number +'>'+ a_text + '</input><br/>'
);
}
});
The big problem wit this is, I can not identify which element was clicked because I don't have access to the event object.
Can I manually bind the action with something like 'data-ember-action', and pass in params? Or, is this too far outside the Ember way?
== update ==
Here's the above JSFiddle, improved by passing parameters to the Action. The event propagation seems to get halted in the Action resulting in the inputs not getting properly marked. Radio buttons loose their grouping, and checkboxes do not get checked.
Ember can easily handle what you're doing here. First it's easier to keep your scope and generate named each loops.
<form {{action 'answerSupplied'}}>
{{#each question in model.questions}}
<h3>{{question.text}}</h3>
{{#each answer in question.answers}}
{{input type=question.type answerId=answer.id data-bind-questionNum=question.id placeHolder=answer.text}}
{{/each}}
{{/each}}
</form>
You may have to do some if statements for labels etc around your input statements.
http://emberjs.jsbin.com/zofoqeje/1/edit
I have a list and I'd like to set one item as class="active" automatically. Given the following bootstrap code:
<ul class="nav">
<li {{bindAttr class="atIndex:active"}}>{{#linkTo "index"}}Index{{/linkTo}}</li>
<li {{bindAttr class="atAbout:active"}}>{{#linkTo "about"}}About{{/linkTo}}</li>
<li {{bindAttr class="atLogin:active"}}>{{#linkTo "login"}}Login{{/linkTo}}</li>
</ul>
atIndex, atAbout and atLogin reside in my ApplicationController.
To render as:
<ul class="nav">
<li class="active"><a...>Index{{/linkTo}}</li>
<li><a...>About<a></li>
<li><a...>Login<a></li>
</ul>
What's the best way to do this with Ember 1.0 pre4? I'd rather not add special code to every view or controller.
PS - atIndex: true works, but atIndex: function() {return true; }.property().volatile() does not. Which makes me think I'm doing something wrong.
Thank you!
{{#link-to "dashboard" tagName="li" href=false}}
<a {{bind-attr href="view.href"}}>
Dashboard
</a>
{{/link-to}}
By far the cleanest way to solve this is by taking advantage of the linkTo helper's built-in support for setting the active class when rendering links. AFAIK this is not yet documented other than in the source code:
implementation: https://github.com/emberjs/ember.js/blob/master/packages/ember-routing/lib/helpers/link_to.js#L46
example: https://github.com/emberjs/ember.js/blob/master/packages/ember/tests/helpers/link_to_test.js#L120
To take advantage of this feature just adjust your css to style based on having an active class on the link instead of the li element. If you really need to style the li you can create a custom view and helper that extends Ember.LinkView and uses an li but changing css will be far easier.
--- UPDATE ----
Since we all love twitter bootstrap just changing the css is perhaps not such a great option. In that case, the following will do the trick:
App.ApplicationView = Ember.View.extend({
currentPathDidChange: function() {
Ember.run.next( this, function() {
this.$("ul.nav li:has(>a.active)").addClass('active');
this.$("ul.nav li:not(:has(>a.active))").removeClass('active');
});
}.observes('controller.currentPath')
});
Working example using ember linkTo helper with bootstrap pills: http://jsbin.com/ekobod/5/edit (requires ember-1.0.0-pre.4)
the active route's path is updated automatically in the ApplicationController via currentPath so I did something like that in my App...
In ApplicationController added properties like so:
isProductsActive: function(){
if ( this.get('currentPath') === 'products' ) return 'active';
else return '';
}.property('currentPath')
and in my ApplicationView template:
<li {{bindAttr class="isProductsActive"}}>
{{#linkTo "products"}}Products{{/linkTo}}
</li>
I made an ember-cli addon that handles this:
https://github.com/alexspeller/ember-cli-active-link-wrapper
EDIT:
Finally, the best way I've found to use the activate class of bootstrap li element using ember.js of the link.
{{#linkTo "dives" tagName="li"}}
<a {{bindAttr href="view.href"}}>Dives</a>
{{/linkTo}}
--------------8<--------------
DEPRECATED:
I guess the previous answers were relevant before Ember.js introduced the activeClass attribute for linkTo helper.
Now I would solve the problem like this :
<ul class="nav">
<li >{{#linkTo "index" activeClass="active"}}Index{{/linkTo}}</li>
<li >{{#linkTo "about" activeClass="active}}About{{/linkTo}}</li>
<li >{{#linkTo "login" activeClass="active}}Login{{/linkTo}}</li>
</ul>
Enber will automatically add the class when relevant.
If I may suggest another solution that requires nothing but Handlebars:
<li {{bind-attr class="view.innerLink.active:active"}}>
{{#link-to "path" viewName="innerLink"}}Click{{/link-to}}
</li>
This sets the LinkView object as a member of the parent view, which's active attribute you can then reference.
I found a pretty simple Solution using linked items in a list group(http://getbootstrap.com/components/#list-group-linked).
<div class="list-group">
{{#each thing in list}}
{{#link-to "details" thing.id tagName="a" href="view.href" class="list-group-item" {{thing.name}} {{/link-to}}
{{/each}}
</div>
Works with Bootstrap v3.1.1 and Ember v1.7.0
Just nest the {{link-to}} with a tagName on the outer one. I'm doing this on EmberJS 2.0.
{{#link-to "admin.websocket" tagName="li"}}
{{#link-to "admin.websocket"}}WebSocket{{/link-to}}
{{/link-to}}
If you want to use Bootstrap navigation in Ember then you can use Bootstrap for Ember that has out of the box support for this:
Github: https://github.com/ember-addons/bootstrap-for-ember
Demo: http://ember-addons.github.io/bootstrap-for-ember/dist/#/show_components/tabs
A lot of these answers are outdated. Here is a much cleaner (and DRY) version for Bootstrap and Ember 2.x:
{{#link-to "index" tagName="li" as |link|}}
Index Page
{{/link-to}}
I solved a similar problem by creating a view for each item and using classNameBindings (I have to say that i don't have a HTML list, i.e.<a>...</a> in my app, just list of <div>).
Here is the way it works for me:
In tasklist.handlebars i iterate over my custom view
{{#each tasks}}
{{view App.TaskListItemView contentBinding="this"....}}
{{/each}}
Ember will insert a view (i. e. <div>) for each item.
The view class for each item is defined in task_list_item_view.js as
App.TaskListItemView = Ember.View.extend({
controller: null,
classNameBindings: ['isSelected', 'isClosed'],
isClosed: function() {
var content = this.get('content');
return content && !content.isOpen(new Date);
}.property('controller.content.#each'),
isSelected: function() {
return (this.get('controller').isSelectedTask(this.get('content')));
}.property('controller.taskSelection.#each'),
....
});
Finally the template for the view just renders my link in tasklistitem.handlebars
<a {{action "selectTask" view.content target="view"}} rel="tooltip"
{{bindAttr title="view.content.comment"}} class="taskListLink">
....
</a>
AFAIK you have to specify the source data in the property() call to let ember know when to (re-) evaluate the property.
Hope that helps
I went with:
Ember.LinkView.reopen({
didInsertElement:function(){
if(this.$().hasClass('active')){
this.$().parent().addClass('active');
}
}
});
I didn't want to use the accepted answer as I wanted to keep my li elements as plain old html. There might be a better way to check the active state but I couldn't get access to the right property.