I'm JS beginner and recently using ember.js for ui development I came across a problem that I can't solve.
I'm trying to reduce amount of posts to fit them on one page. Simply calling slice method on return value of this.get('store').find() doesn't work. I also tried to trim content of return value of all function, but still without success. Any ideas?
You can use the following:
App.YourController = Ember.ArrayController.extend({
arrangedContent: function() {
return this.get('content').slice(0 , 10);
}.property('content')
});
Where YourController is the controller that belongs to your route. So the content will be the resolved promise from this.store.find('modelName'). The arrangedContent property is the place where you modify the content when you want to perform filtering, ordering etc. Without changing the content directly and preserving all the data.
Give a look in that sample http://jsfiddle.net/marciojunior/pwQ5b/
If you really are calling slice on the return value of this.get('store').find you will run into trouble. The find function returns a promise and you need the result the promise resolves to.
You can solve this in one of two ways:
If you are using the standard Ember pattern for loading in your model, you should have a model function defined in your route. This will actually wait until the result is resolved and in your controller you can call slice on this.get('content')
If you are loading your data within the controller you will need to so something like below:
c = this
this.store.find('myModel').then(function(result) {
c.set('paginatedContent', result.slice(0, 10));
});
Here we are waiting for the promise returned by find to resolve to a result before setting the paginated content based on that result.
Related
On my route im requesting via ember-data some records. Lets say the model-type is 'item'.
model: function(){
return this.get('store').find('item');
}
now ive got a component named 'my-foo' which should use the records to do something with the data. Therefore Im calling the component like that:
{{my-foo myItems=model}}
in my routes template. In the components js part, Im trying to get the myItems-field and iterate over them.
this.get('myItems').forEach(...);
Unfortunalety its not clear for me if the model i want to overgive to the component is an collection from records or just a single record (since on some routes the model is the result of store.find('item') on other store.find('item', 23424).
How can I check what kind of data arrives in the component.
(Also Im wondering what kind of object is it since im using ember-data. Is it a DS.recordarray or a promise or something else at this time?)
I can see two solutions to the problem:
Making component aware of the form that model receives
Checking and/or adjusting data type in component (in my opinion better default scenario)
As for making component aware - you could go with 2 approaches. Either differentiate in a way how your component take arguments, so there could be:
{{my-foo myItems=model}} - when you expect to receive multiple items
{{my-foo item=model}} - when you expect to receive single one
And then work accordingly further on, or - the second approach - is to actually split component (while extracting shared part to a different structure) so you would have my-foo for single items and my-foo-array for multiple.
Advantage of this approach is that you don't deal with what-if-multiple logic, that might grow to something unmanagable later on, yet usage of it is dependant on project requirements.
As for checking and/or adjusting - you already have data in, so could make assumption that your data is dirty and sanitize it using computed property. Below example, where single item is wrapped into an array.
export default Ember.Component.extend({
sanitizedItems: Ember.computed('items', function() {
var items = this.get('items');
if(!Array.isArray(items)) {
return [items];
} else {
return items;
}
})
});
Since you're using Ember.Data, depending on your setup, you might get a promise instead of object/array. In this case, you might want to resolve promise using this.get('items').then(function(items) { ... }) before doing sanitization, yet the idea behind is exactly the same.
You can check full example: Gist, Twiddle
Is there a standard way of handling errors when a 'findHasMany' call fails? Use case:
Model: App.User
{
DS.hasMany('comments', {'async': true});
}
Template
{{#each comment in comments}}
<p>{{comment.title}}</p>
{{/each}}
The issue is that when the lazy loading of comments fails, due to some server issue for example, I want to be able to respond to that error in the UI (by routing somewhere else, showing a popup about errors on the page, etc). At the moment the promise just rejects. I thought that Ember Data might have some hook on the ManyArray for cases like this, but it doesn't seem to, and the store seems to define precisely nothing as the action to carry out in such cases: https://github.com/emberjs/data/blob/v1.0.0-beta.8/packages/ember-data/lib/system/store.js#L1758 - the promise is given a 'resolve' method, but not a reject method.
My options seem to be either subclassing the store, and adding in some reject code there, or subclassing DS.PromiseArray and observing the 'isRejected' property. Any thoughts would be very welcome!
EDIT:
This issue seems to boil down to the fact that, when handling models defined in a route, Ember and Ember Data work well together (you can catch rejecting promises in an error action) there is no similar structure for async requests directly through a template. One solution might be to have an observer in the controller that observes something like 'model.isError', but a failing hasMany relationship does not trigger an error on the owning model. I suppose instead I can do 'comments.isRejected', but again, I would have to code that in for every controller that has a model with a hasMany relationship, in other words, all of them, which doesn't seem very satisfactory. If models had an observable enumerable property (like "hasManyIsError": {comments: false, posts: true}) then it would be easy to observe any of them with 'hasManyIsError.length
Assuming a var called user that has been fetched, you'd do this:
var itWorked = function(comments) { return comments; }
var itFailed = function(error) { return error; }
user.get("comments").then(itWorked, itFailed);
async: true means it'll get using a promise... so you can use then... you can't do that on a relationship that doesn't specify async: true.
[edit] sorry I just realised it might not be obvous that whatever you put in the itFailed function will eval when the request for comments fails, and likewise inversely for itWorked... :)
I'm having some difficulty reloading my ember RESTful models, and I'm not sure why. Here's [conceptually] what I'm trying to do... http://jsbin.com/EfuBiNo/4/edit
The only difference between that code and my code is that I'm not using the FixtureAdapter, I'm using the RESTAdapter. Unfortunately, reloading my RESTful models is causing the number of records in the DS.RecordArray to double. So you can see the console is logging that (on every reload) there are two records in the RecordArray.
When I run this with my RestAdapter, the count goes 2...4...8...16....etc. So I'm not sure why it's doubling them every time, but if anybody has any insight on why -- or better yet, another way to reload these records -- I'd be very grateful. Thanks.
If you need to refresh a collection of records after you've already loaded them, you could do something like:
App.ThingsRoute = Ember.Route.extend({
model: function () {
return this.store.find('thing');
},
actions: {
refreshThings: function () {
var controller = this.controller;
this.store.find('thing').then(function (things) {
controller.set('content', things);
});
}
}
});
This will simply fetch all the things again and set the record array as the content on your controller whenever the promise resolves. If the items that come back are already catalogued in your store (the ids are already present) then you won't get a ton of duplicate records hanging around; stuff will just get updated. If there are new records that you didn't previously know about, then you'll get those now on your ThingsController.
This is also useful if you are doing some type of querying:
this.store.find('thing', {color: 'red'})
I have a backend resource that contains user activities and in the application I would like to present activities based on a single day's worth of activities. I have an ArrayController called ActivitiesController defined in the router like this:
this.resource('activities', { path: '/activities/:by_date' }, function() {
this.route('new');
});
The REST API provides the following GET method:
GET /activities/[by_date]
So far this looks pretty symmetrical and achievable but I'm running into two problems:
Parameterized array find. Typically a parameterized route would be serviced by a ObjectController but in this case the by_date parameter simply reduces/filters the array of activities but it's still an array that's returned. I'm not sure how to structure this in the model hook in the ActivitiesRoute so that its effectively doing a "findAll" rather than expecting a singular resultset.
Since functionality. As there is a reasonable network cost in bringing back these arrays of activities I would like to minimize this as much as possible and the REST API supports this by allowing for a since parameter to be passed along with the date of the last request. This way the server simply responds with a 304 code if no records have been updated since the last call and if there are new records only the new records are returned. Is there anyway to get this "out of the box" with ember-data? Does this require building a custom Adaptor? If so, are there any open source solutions that are available?
p.s. I was thinking that part of the answer to #2 might be to incorporate Alex Speller's Query Parameters: http://discuss.emberjs.com/t/query-string-support-in-ember-router/1962/48
What does your route's model hook look like? I am thinking something like this should work:
model: function(params) {
return this.store.find('activity', { by_date: params.by_date });
}
I been trying to compare some values in handlebars if statement {{#if value == 'otherValue'}}, obviously unsuccessfully because handlebars do not like this and expecting a string, boolean, or function name. Well that would be ok, but then I tried to pass parameter in the function like you can do with {{action}} helper, and well that didn't workout either, got this in console
Error: assertion failed: You must pass exactly one argument to the if helper
So then I decided to do this in a View, even so ember js guides points that accessing template values in-scope is unusual and they provide only poor paragraph with no examples.
http://emberjs.com/guides/understanding-ember/the-view-layer/#toc_accessing-template-variables-from-views
So when I tried to do this, I got a problem of accessing those variables, I tried this way this.get('controller.templateVariables') and via full path to View, but value was either undefined or .get() wasn't exists as a method.
So at this moment I decided to save variable in the DOM data property, but turns out this {{#view App.TabsView data-title="{{tab}}"}} is going to literately give me a string {{tab}} when I try to access it from View with this.get('data-title').
The only way left to me was to insert additional element inside view and store variable there, and afterwards access it with jQuery class selector. but element is not yet exist in the DOM at the time of isVisible function gets executed, so I have no access to values at that time. That explains why this.get('element') was returning null.
Similar examples on ember js mostly ends up with something like if (someLogic) {}, but how I can do any logic when there is no variables available to me.
Question
To simplify my question - is there a way how I can do such a thing in ember js? Simple as
// have objects stored in controller
var obj = [{title:'Tab1'}, {title:'Tab2'}, {title:'Tab3'}];
// loop via them in the DOM
obj.forEach(function(tab) {
// do this kind of comparison
if( tab.title == currentTab() ) {
// do something here
}
});
If that is not possible, then what would be the other way to achieve similar functionality?
You can write a handlerbar helper to do this
{{activeTab tab}}
Handlebars.registerHelper('activeTab', function(tab) {
})
See a question about the same issue
Active Tab
Or look at existing helpers to write your own
Bind Helper
Template Helper
I think the best way for me to demonstrate this is with a heavily commented JSFiddle that I've put together for you: http://jsfiddle.net/PbLnm/
Please ask any questions below if you're not sure about anything.
The main part which determines when to add the active class is in the computed property:
// Determine if the object we have for this view is the same as the activeTab's object. If it is the same, then this view is the current active tab.
active: function() {
return Boolean(this.get('parentView.activeTab') == this.get('tab'));
}.property('parentView.activeTab')