How to create an external link from emberjs loop? - ember.js

I'm looping through several items in a model. For each item I would like to create an external link to a page in my application that is not using emberjs. I thought this would be trivial but its not working the way I thought it would work.
This is what I have:
<tbody>
{{each model itemViewClass=App.ColorView}}
</tbody>
<script type="text/x-handlebars" id="colorTemplate">
<tr>
<td>{{date}}</td>
<td>{{name}}</td>
</tr>
</script>
App.ColorView = Em.View.extend({
templateName: 'colorTemplate'
});
I thought this would create links like these:
/myapp/colors/5/shades
/myapp/colors/45/shades
/myapp/colors/6/shades
...etc.
However, the link is being created like this:
localhost:8080/myapp/colors/%3Cscript%20id='metamorph-33-start'%20type='text/x-placeholder'%3E%3C/script%3E56%3Cscript%20id='metamorph-33-end'%20type='text/x-placeholder'%3E%3C/script%3E/shades

You Should implement a itemController to the {{#each}} collection view. In that itemController you can use a computed property to generate the url as
url : function () {
return "/myapp/colors/"+this.get('id')+"/shades";
}.property()
Here is a Sample Bin
Hope it helps

you should use {{bindAttr href="url"}} for this. url is a method that generates url

Related

Listing multiple items using EmberJS 2.0

I have a basic app having using the Fixture Data Adapter and have a model with some basic seed data.
I am having an issue getting the new emberjs 2.0 to list multiple items in a model. The objects are loaded and showing in them ember inspector but are not looping through and displaying in the browser. I have been able to list one at a time but when i switch to adding multiple items it is blank. Any help would be great.
I have updated with some more information. Here is the route for the list i am trying to list out.
<!--dashboard/route.js -->
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.findAll('dashboard');
}
});
Here is the template file
<!-- dashboard/template.hbs -->
<tbody>
<tr>
{{#each dashboards as |dashboard|}}
<td>dashboards.status</td>
<td>dashboards.orderid</td>
{{/each}}
</tr>
</tbody>
I think I have everything setup right but cant get it to work.
Thanks!
Give this a try for the model hook. It doesn't need to be a hash.
model: function() {
return this.store.findRecord('dashoard', 2);
}
And if you want to return all the dashboards
model: function() {
return this.store.findAll('dashoard');
}
Then in dashboard/template.hbs
{{order-list dashboards=model}}
Now you have access to all your dashboards in your component
{{#each dashboards as |dashboard|}}
<td>dashboard.status</td>
<td>dashboard.orderid</td>
{{/each}}
When you fetch something in a model, it sets the model property on the respective controller.
So, instead of looping through dashboards, you need to do this:
<tbody>
<tr>
{{#each model as |dashboard|}}
<td>dashboards.status</td>
<td>dashboards.orderid</td>
{{/each}}
</tr>
</tbody>
An alternative is to use the setupController hook to set up the dashboards property on the controller. Something like this:
setupController (controller, model) {
this._super(...arguments)
controller.set('dashboards', model)
}
This isn't necessary; you're basically creating a dashboards alias that points to model. So, now, when you loop through dashboards, it loops through the model.
You can find more about how a controller is set up here: http://guides.emberjs.com/v2.0.0/routing/setting-up-a-controller/

How to use content.pagination.current_page in controller as a bound value

I am using this example ( https://gist.github.com/tcjr/6935684 ) to implement server-side pagination in an Ember.js app. Using that example, Router places metadata about the pagination on the results content array directly.
At the template layer the code looks like this:
<tfoot>
<tr>
<td>
<ul class="pagination pagination-sm">
<li><a {{action 'firstPage'}}><<</a></li>
<li><a {{action 'previousPage'}}><</a></li>
<li class="disabled"><a>{{ content.pagination.current_page }} of {{ content.pagination.total_pages }}</a></li>
<li><a {{action 'nextPage'}}>></a></li>
<li><a {{action 'lastPage'}}>>></a></li>
</ul>
</td>
</tr>
</tfoot>
What I want to do is have a conditional so that if, for example, I'm on the first page the first two links are hidden. E.g. (using my latest helper attempt)
{{#ifGT content.pagination.current_page 1}}
<li><a {{action 'firstPage'}}><<</a></li>
<li><a {{action 'previousPage'}}><</a></li>
{{/ifGT}}
But this doesn't work. Efforts to create a helper either return the string (i.e. the string 'content.pagination.current_page' > 1) or, if using registerBoundHelper, gives me the error: registerBoundHelper-generated helpers do not support use with Handlebars blocks..
// currently this fails with:
// registerBoundHelper-generated helpers do not support use with Handlebars blocks.
Ember.Handlebars.registerBoundHelper('ifGT', function(val1, val2, options) {
console.log(val1);
console.log(val2);
if(val1 > val2) {
return options.fn(this);
}
return options.inverse(this);
});
If a helper won't work (and maybe I'm just doing it wrong) how do I get access to the value of content.pagination.current_page from within the controller or route so that I can create a normal bound value use the normal Handlebars if block?
I would declare various properties in the controller/component (or a mixin), for example:
isFirstPage: Ember.computed.equal('page', 1);
isFirstPageLinkEnabled: Ember.computed.not('isFirstPage');
To get this to work I ended up using the setupController function on the Route like this:
setupController: function (controller, model) {
this._super(controller, model);
var pagination = model.get('pagination');
var page = pagination.current_page;
var total_pages = pagination.total_pages;
controller.set('isFirstPage', page === 1);
controller.set('isLastPage', page >= total_pages);
},
Once it was set up, I was able to reference the controller properties like normal.

Controller not seeing the updated model - asynchronous handling

I have a very simple requirement but like with many things in Ember.JS, I'm banging my head against the wall trying to get it implemented.
I have an overview screen where a couple of records are displayed in a table.
To render the overview screen I'm using the following Route
App.LocationsIndexRoute = Ember.Route.extend({
setupController: function(controller) {
var locations = App.Location.find();
controller.set('content', locations);
},
renderTemplate: function() {
this.render('locations.index',{into:'application'});
}
});
This is working fine.
I would now like to conditionally render the overviewtable.
If records are present render the table.
If no records are present display a message.
I tried implementing this using the following controller.
App.LocationsIndexController = Ember.ArrayController.extend({
locationsPresent: function() {
var model = this.get('content');
return model.content.length > 0;
}.property()
});
and the following template
{{#if locationsPresent}}
<table class="table table-hover">
<tr>
<th>Latitude</th>
<th>Longitude</th>
<th>Accuracy</th>
<th></th>
<th></th>
</tr>
{{#each location in model}}
<tr>
<td>{{location.latitude}}</td>
<td>{{location.longitude}}</td>
<td>{{location.accuracy}}</td>
<td>{{#linkTo locations.edit location}}Edit{{/linkTo}}</td>
<td><button {{action removeItem location}}>Delete</button></td>
</tr>
{{/each}}
</table>
{{else}}
No locations present.
{{/if}}
The computed locationsPresent property is called once, before the page is rendered. At that time I assume that the model is still being loaded as the length = 0.
When the page is rendered, the locations from the App.Locations.find() are available but the locationsPresent is not called anymore, meaning the page decided to render the No locations present. message.
I went through the Managing Asyncrony in Ember page and assumed that the computer property locationsPresent would be updated if the underlying model changed (if it was completely loaded) as the page states :
Using a computed property for author eliminated the need to explicitly invoke the computation in a callback when the underlying property changed.
I'd love to know what I'm doing wrong and how I can fix this but more importantly why I seem to be missing some of these core concepts of Ember.JS. If somebody can point me where in the docs / guides this is explained properly I'd love to know.
I think it is a easy fix. You need to add the property you are observing. like so:
locationsPresent: function() {
var length = this.get('content.length');
return length > 0;
}.property('content.#each')
adding the #each is necessary if locationsPresent needs to recalculate wen content is added. I think you can also observe 'content.isLoaded'

Assigning 'active' class to selected list item in EmberJS

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.

How to nest views in the new Ember.js router API?

I have a setup screen, it consists of a sidebar and a body. The body can have a form to update the user's profile or password.
The routing looks like this:
App.Router.map(function(match) {
(match("/")).to("index");
(match("/user")).to("user", function(match) {
(match("/")).to("userIdx");
(match("/settings")).to("userSetup", function(match) {
(match("/")).to("userSetupIdx");
(match("/profile")).to("userSetupProfile");
(match("/password")).to("userSetupPassword");
});
});
});
and the wrapping setup template like this:
<script type="text/x-handlebars" data-template-name="userSetup">
<div class='sidebar'>
Sidebar
</div>
<div class='main'>
Setup <br/>
{{outlet}}
</div>
</script>
A complete example can be found here:
http://jsfiddle.net/stephanos/mgp7F/6/
What's the right approach to do this in Ember.js?
EDIT
With the help of sly7_7 I managed to make the fiddle above work: http://jsfiddle.net/ygvsS/9/. All I did was rename everything from userSetup (template, view, route) to setup. BUT obviously this is not a solution (since I have appSetup, too).
I think I have done what you are looking for, based on your comment example: http://jsfiddle.net/rt8fv/
router code:
App.Router.map(function(match){
match('/').to('home');
match('/about').to('about');
match('/contributors').to('contributors', function(match){
match('/').to('contributorsIndex');
match('/:contributor_id').to('contributor', function(match){
match('/').to('contributorIndex');
match('/details').to('contributorDetail');
match('/repos').to('contributorRepos');
});
});
});
Maybe related question: Best approach to fetch data in every state of app