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'
Related
I'm having trouble understanding how to update a record in an Ember.js that uses EmberFire and Firebase.
I have a small test application built to try to understand all of the CRUD functions. I can return a list of tasks that have been created, create a new task, and delete a task. But I cannot correctly update a record in a list.
When I look in the inspector, on my index page, I see that in the Ember debugger, under Data, it shows my model, and there is an Id field that contains the value that Firebase generated when a record was created on the server.
But when I console log the object that is getting passed to my Update Action in the route, there is no Id attribute. I think that is why I get an error of:
Error: no record was found at https://taskline.firebaseio.com/tasks/id
When hitting this piece of code:
export default Ember.Route.extend({
model: function () {
return this.store.findAll('task');
},
actions: {
updateTask: function (model) {
console.log(JSON.parse(JSON.stringify(model)));
this.store.findRecord('task', 'id').then(function(task){
task.set( 'taskname', model.taskname);
task.set( 'startdate', model.startdate);
task.set( 'enddate', model.enddate);
task.set( 'banding', model.banding);
return task.save();
});
},
Ember Fire is supposed to handle the Id isn't it? But if it's not in my model object, how am I supposed to pass it to the find query?
When you would like to update a model, one of the best option to pass the record back from the template/controller to the route action. In this case the record will be ready in your function param, so your updateTask method would look like this (only):
updateTask(record) {
record.save();
}
The whole process:
Firstly, in the Route handler download all records, as you did in your example, and implement the action:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
this.store.findAll('task');
},
actions: {
updateTask(task) {
task.save();
}
},
});
Secondly, in your template list each task with input box. Maybe it could be a table. (I just have one field here taskname, but you could have more, of course.)
<table>
<thead>
<th>ID</th>
<th>Taskname</th>
<th>Action</th>
</thead>
<tbody>
{{#each model as |task|}}
<tr>
<td>{{task.id}}</td>
<td>{{input value=task.taskname}}</td>
<td><button {{action 'updateTask' task}}>Update</button>
</tr>
{{/each}}
</tbody>
</table>
As you see, you are sending back the task record to the updateTask action in your route, so we are able to save it immediately. You can learn all these tricks in this free tutorial, which focuses on the Ember Way and uses Firebase: http://yoember.com
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/
I have select view within a loop, and I wanted to set the default selection on the drop down. I tried setting "value" attribute as well as "selection" attribute but nothing worked for me. I was trying to create jsbin to demonstrate the issue, but then it is giving me completely different issue which I don't see in my dev code though.
My controller is defined like :
App.AnswerController = Ember.ObjectController.extend({
answerLayouts: function () {
return this.get('store').findAll('layout');
}.property(),
selectedAnswerLayout: null,
initialize: function () {
this.set('selectedAnswerLayout', this.get('store').find('layout', this.get('id')));
}.on('init')
});
and in the template I am doing:
<table>
{{#each answers itemController="answer"}}
<tr>
<td>{{name}}</td>
<td>{{view Ember.Select
content=answerLayouts
optionValuePath="content.name"
optionLabelPath="content.displayName"
class="form-control"
prompt="Answer Layout"
selection=selectedAnswerLayout}}
</td>
</tr>
{{/each}}
</table>
and does't see answerLayouts as an array, but when I check {{answerLayouts.length}} it returns 3!
Here is a jsbin link that demonstrates the issue: http://jsbin.com/AcUPIpEl/1/
It's an issue with the old version of Ember, it was fixed somewhere between 1.0+ and 1.2
http://emberjs.jsbin.com/ODaKIjIw/1/edit
Ok had to do things bit differently to get things working. Rather than defining "answerLayouts" as a property, I defined it as an array and when the controller gets initialized, I populated the array and set the selectedAnswerlayout there like this:
App.AnswerController = Ember.ObjectController.extend({
answerLayouts: Ember.A(),
selectedAnswerLayout: null,
initialize: function () {
var self = this,
selectedlayoutId = this.get('id');
this.get('store').find('layout').then(function(data){
data.forEach(function(layout){
self.get('answerLayouts').pushObject(layout);
if(layout.get('id') === selectedlayoutId){
self.set('selectedAnswerLayout',layout);
}
});
});
}.on('init')
});
Here is the working jsbin link : emberjs.jsbin.com/ODaKIjIw/3.
I have realized that when we can define things in init, it's best to do so there. I have had issues with bindings when relying on computed property before. Lesson learned :)
I am using a simple Ember.ArrayController in an application with ember-data (latest), ember rc6 and a stock REST controller.
I have delete actions next to each item in the list rendered by the array controller. When an item is deleted, the proper REST API call is made and it's removed from the database properly. Server responds with the correct 204 response.
Here is my router setup, notice the find filter being applied
App.CategoriesIndexRoute = Ember.Route.extend({
setupController : function(controller, model) {
this._super(controller, model);
controller.set("content", App.Category.find({
"parent": null,
}));
}
});
If I remove the find filter and load all categories, everything works fine (item is automatically removed from the list immediately after commit). However, when I add the filter to only display categories that don't have a parent, the list is not updated when an item is deleted. If I transition to another section and come back, the list is reloaded and the category is gone.
Here is deleteCategory method in the ArrayController:
deleteCategory: function(category) {
var transaction = this.get("store").transaction();
transaction.add(category);
category.deleteRecord();
transaction.commit();
}
Is this an ember-data or emberjs bug? If not, what am I doing wrong here? If it's a bug, is there a way to force-reload the ArrayController contents after I delete an item? Alternatively, can I remove the category from the ArrayController manually?
UPDATE 1:
I managed to force-update the array controller's contents by setting its content:
category.one("didDelete", this, function() {
this.set("content", App.Category.find({
parent: parent_category
}));
});
UPDATE 2:
Here is how I am displaying the list of items in the template:
{{#each category in controller.content }}
<tr>
<td><a {{ action "detailCategory" category }}>{{ category.name }}</a></td>
<td><a {{ action "deleteCategory" category }}>Delete</a></td>
</tr>
{{/each}}
Thank you!
This is no longer the way to do things in Ember Data. One would not use transactions any longer, as can be seen in the transition doc: https://github.com/emberjs/data/blob/master/TRANSITION.md
I am using Ember.js to create a table from a json feed. I have a requirement to flash the values (within a table row) that are changing. How do I do this in Ember.js?
My template is shown below:
<tbody>
{{#each Quotes.quotesController.content}}
<tr>
<td class="quote-name">{{name}}</a>
<td class="quote-code">{{code}}</a>
<td class="quote-value">{{value}}</a>
<td class="quote-bid">{{bid}}</a>
<td class="quote-offer">{{offer}}</a>
</tr>
{{/each}}
</tbody>
Here's the code that fetches and updates the quotes:
fetchQuotes: function() {
var scope = this;
$.getJSON('/rest/quotes', function(data) {
$.each(data, function(index, quote) {
scope.updateQuote(quote);
});
});
}
Just as a comparison, in backbone.js, I was binding to change events at the attribute level and firing animations in the callbacks. For example:
this.model.bind('change:value', this.updateValue);
updateValue: function() {
var td = $(this.el).children('.quote-value');
td.html(this.model.get('value'));
td.animate({color: animationColor}, 200)
.animate({color: '#000000'}, 3800, 'easeInQuint');
},
I don't know if such an approach is possible/recommended using Ember.js.
Edit:
I almost have it now. I can handle an event on a table cell and change its color to anything. See this jsfiddle - click of any cell in the value column and it will turn red. The only thing I don't know is how to capture a change to the table cell value instead of a click event. Is there any such thing as a change event that I can capture?
See the following jsFiddle (a modification of an answer another one of your questions). I think from there you can do what you need, but in short you need to make an observer that watches for changes to the property on your controller that is holding the array that you are modifying.
Edit in response to the 1st comment:
Here is a working solution in response to that comment: http://jsfiddle.net/ud3323/ykL69/