subdirectories within ember controller not working - ember.js

from this post it seems that sub directories within ember controller should work.
https://github.com/ember-cli/ember-cli/issues/1219
however its not working for me .
here is my code branch like (directory cm contains a child directory views):
/controllers/cm/views/item.js
/routes/cm/views/item.js
/templates/cm/views/item.js
when i try to populate the model in route using the code below i see the data but when i put the same code in controller it never gets executed.
model: function(){
return this.store.find('item',{id: "1"});
}
entry in router.js is as follows:
this.resource('cm', {path: '/cm/:id'} , function() {
this.route('views');
this.route('views.items', {path: '/views/items'});
});
Clearly ember is not able to resolve the controller correctly.
Not sure how to fix this ...

That its becuase the model hook in a route works differently than in the controller.
in a route it is a method that can return a promise, the route will wait for the promise to be resolved before setting up the controller.
in the controller, it is just an attribute, which won't get executed until you getit, and even then, all you will get its a promise.

Wat?! Subdirectories work just fine. First, I'm not sure it's the best idea to use views or items as route names, as they're both very generic, and also used in some of the ember internals and can confuse things. Declaring a model called View could very well even break things in your app.
The controller/route/template structures for your router.js will be as follows:
<controllers|routes|templates>/cm.js
<controllers|routes|templates>/cm/index.js
<controllers|routes|templates>/cm/views.js
And I'm not sure what views.items would look like, because this would probably be better suited to making views a resource instead, or using a dash in the name, in which case the route declaration would be this.route('views-items', {path: '/views/items'});
Overall, I think your router definition should look like so:
this.resource('cms', function() {
this.resource('cm', {path: '/:cm_id'}, function() {
this.route('views');
this.route('views-items', { path: '/views/items' });
});
});
This isn't meant to be a quip--I'm here to help--but I think you need to spend a little more time with Ember's routing documentation to understand the conventions that Ember is expecting for certain router definitions. Also, the ember inspector tool is a really great asset when debugging router issues: https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi

Related

Ember.JS - 'TypeError: internalModel.getRecord is not a function' when trying to reverse a collection of records

--Using Ember Data 2.7.1--
I am trying to reverse the order of a collection of records without first turning them into an array using toArray(). This collection of objects comes from the promise returned by this.store.findAll('history-item').
I want to do this the ember way instead of making them plain javascript. I am getting a TypeError: internalModel.getRecord coming from record-array.js. For some reason when it is trying to do objectAtContent(), the content it is looking seems to not have a type. Through the stack trace I can see that the object I am dealing with is [Class], class being the history-item model. A few stack calls before the objectAtContent(), the object being dealt with switches from that history-item model to some other Class object that has no type attribute.
I am able to use Ember Inspector to see my data correctly, and if I just displayed the original collection of records on my template, it shows properly.
Has anyone run into this?
Some thoughts and considerations:
-Is there anything special about how findAll() works with its promise that doesn't allow for reversal since it is reloading in the background? I do want it to keep reloading live data.
-I am using ember-cli-mirage to mock my db and endpoints and I've follow the instructions to the letter I think. I am using an unconfigured JSONAPISerializer for mirage and and a unconfigured JSONAPIAdapter for ember. Could it have anything to do with metadata that is being sent from the back? Could it have something to with the models or records not being set up? Is there something special I have to do?
Route Segment that defines model and tries to reverse it:
[note: I know it may not be convention to prep the data (ordering) in the route but I just put it in here for ease of description. I usually do it outside in the controller or component]
model(){
return this.get('store').findAll('history-item').then(function(items){
return items.reverseObjects();
}).catch(failure);
History list model declaration:
export default DS.Model.extend({
question: DS.attr('string'),
answer: DS.attr('string')
});
Ember-Cli-Mirage config.js end points:
this.get('/history-items', (schema) => {
return schema.historyItems.all();
});
Ember-Cli-Mirage fixture for history-items:
export default [
{id: 1, question: "1is this working?", answer: "Of course!"}
}
Error:
TypeError: internalModel.getRecord coming from record-array.js
This issue also happens when I try to create a save a record. The save is successful but when the model gets reloaded (and tries to reverse), it fails with the same error. It doesn't matter if I the fixture or not.
Controller:
var newHistoryItem = this.store.createRecord('history-item', {
question: question,
answer: answer
});
newHistoryItem.save().then(success).catch(failure);
The result returned from store.findAll and store.query is an AdapterPopulatedRecordArray (live array), mutation methods like addObject,addObjects,removeObject,removeObjects,
unshiftObject,unshiftObjects,pushObject,pushObjects,reverseObjects,setObjects,shiftObject,clear,popObject,removeAt,removeObject,removeObjects,insertAt should not be used.
Have a look at corresponding discussion and
Proposed PR to throw error and suggestions to use toArray() to copy array instead of mutating.
I think using toArray is fine, no need to reinvent the wheel. Even Ember's enumerable/array methods are implemented using toArray under the hood.
I like keeping transforms on controllers/components, so Routes are only concerned with [URL -> data] logic. I think here I would keep the model hook returning the server data, and use a computed property on the controller:
import Ember from 'ember';
export default Ember.Controller.extend({
reversedItems: Ember.computed('model.[]', function() {
return this.get('model').toArray().reverse();
})
});
Twiddle: https://ember-twiddle.com/6527ef6d5f617449b8780148e7afe595?openFiles=controllers.application.js%2C
You could also use the reverse helper from Ember Composable Helpers and do it in the template:
{{#each (reverse model) as |item|}}
...
{{/each}}

LoadingRoute only called once for dynamic segments

How does one get ember to call the LoadingRoute each time one a dynamic route e.g. product/1 and product/2.
I've create a jsbin to illustrate the problem.
If you don't want to eager load all zoobats, the first time you go to any bat resource, you could change your router to
App.Router.map(function() {
this.route('foo');
this.resource('bat', {path: "/zoo/:bat_id"});
});
Now define App.BatRoute and change link-to's route from 'zoo.bar' to 'bar'. You will get loading for each dynamic route, without adding ZooLoadingRoute and you don't have to eager load all zoobats.
Check this jsbin
A work around that sly7-7 posted which works can be found here
Basically define a LoadingRoute at most one level down from the route with the dynamic segment. For
App.Router.map(function() {
this.route('foo');
this.resource('zoo', function(){
this.route('bat', {path: "/:bat_id"});
});
});
this means having a ZooLoadingRoute in your app.

Ember Routing: Reusing a resource under multiple parent routes

I have the following routing setup:
this.resource('blog',function(){
this.resource('selectimage',{path:"selectimage/:returncontext"},function(){});
});
this.resource('post',{path:":post_id"},function(){
this.resource('selectimage',{path:"selectimage/:returncontext"},function(){});
});
What I would have expected is, when navigating to blog.selectimage and post.selectimage, that the same router,controller and view would be used.
Unfortunately it seems that the last entry wins.
Is it possible to achievie this, without leaving the parent context(post/blog), when navigating to selectimage
(Obviously I could inherit from the base-selectimages-classes, but I'm hoping there is some generic ember way of doing this.)
You cannot have same resource name for two different routes as Ember relies more on naming conventions. But Ember provides ways to reuse the same controller and templates
Rename your selectimage under post to something else
this.resource('post',{path:":post_id"},function(){
this.resource('xxx',{path:"xxx/:returncontext"});
});
And in the router, you could specify the templateName and controller that has to be reused. Something like this should make it reusable
App.xxxRoute = Em.Route.extend({
controllerName: 'selectimage',
templateName: 'selectimage'
});
Sample Demo

Ember Data's API call signature

I'm a newbie to Ember Data and I've just switched from FIXTURE data to the RESTAdapter but I don't know enough about how to connect the model and the API's call signature. Specifically I'd like to be able to call an endpoint GET /activities/[:user_id]/[:by_date]. This would load an array of "Activity" objects but only those for a given date.
Router:
this.resource('activities', { path: '/activities' }, function() {
this.route('by_date', {path: '/:user_id/:by_date'});
});
Route:
App.ActivitiesByDateRoute = Ember.Route.extend({
serialize: function(activity) {
return {
userId: 1,
dateBy: "2013-07-01"
};
}
});
First off I tried to hard code the values for userId and dateBy making the adjustments to the Route above. Sadly that did not work. I think I understand why -- although I don't have a quick way to fix this -- but more disturbing for me was that when I manually put in the parameters into the URL: http://restful.service.com/api/activities/1/2013-07-01. The results are quite surprising to me:
Initially the debugging messages suggest a success:
This however, is not correct as no network requests are actually made
If you reload the browser, it will now go out and get the Activities but to my surprise it also goes out to find the specified user. Hmmm. That's ok, the user 1 is pulled back successfully.
The Activity, however, is just a GET /activities call which fails because this endpoint needs the user and date qualifier to work. Why weren't these included in the request?
I don't think ember data supports "composite keys" (which is essentially what you are trying to do). You'd probably need to build your own Ajax for that specific model and implement it in the model hook for that specific route. You can always fetch the data and sideload it into ember-data if you'd like to have an ember data model.

"Dynamic segment" in Ember.js?

Throughout the Ember.js documentation, one finds the concept of dynamic segment mentioned at several places. What does it mean?
Updating with a proper sample: Demo | Source
Edit due to questions in comments:
In Ember, think of the Router mechanism as a State Machine: Each Route can be seen as a State. Sometimes tho, a state can have it's own little state machine within it. With that said: A resource is a state which you have possible child states. A PersonRoute can be defined as either as a resource our a route in the <Application>.Router.map callback; it really depends on your end goal. For example, if we think of a resource for a list of people based on a person model, we would potentially have a route to list all records.
App.Router.map(function() {
this.resource('people');
});
With this map, I'm telling my app that it needs a people template (and maybe a view), a people controller and a people route. A resource is also assumed to have a index route, which is implied and you don't have to code it, but if you need to, it would be PeopleIndexRoute, after the name of the resource itself, by convention.
Now I can (a) create a person route under people resource to be a single state of a person record; or (b) I can create a person resource under the people resource, so I would have more options under person resource (edit, detail, delete); or (c) I could create a separate resource for person, and use the path to override the url if I want to.
I sometimes go for option c:
App.Router.map(function() {
this.resource('people');
this.resource('person', {path: 'person/:person_id'}, function() {
this.route('edit');
this.route('delete');
});
});
That would make sense that edit is route since it doesn't have child states , only siblings (delete) and a parent (person). The url for a record would be something like this: ~/#/person/3/edit).
The routes, when not defined as a resource, won't have any child route/state, so you don't have person.edit.index like you have person.index, in other words, routes don't have child, only siblings and resources can have both.
Right now, the Routing Guide is the most solid piece of documentation we have about this. I strongly recommend it.
Dynamic Segment is a part of a route URL which changes according to the resource in use. Consider the following:
App.Router.map(function() {
this.resource('products', function() {
this.route('product', { path: ':product_id' })
}
});
In the stub above, the line:
this.resource('products', function() {
will produce the url
~/#/products
and the following line will produce
~/#/products/:product_id
replacing the dynamic part, you could have an url like this
~/#/products/3
the :product_id is what makes this route dynamic. The router will serialize the id of a resource (for example a Product model) to the URL and it also uses that id to find the a model in your DS.Store. You'll often see this in routes like the following:
App.ProductRoute = Em.Route.extend({
model: function(params) {
return App.Product.find(params.product_id);
}
});
So for this example, if you access ~/#/products/3, the app will then try to load an instance of the Product model from your store or try to fetch from your backend API.
You can see a fiddle that illustrates that here | source here
I also recommend this screencast by Tom Dale where he builds a blog reader app with Ember.js using the router and the ember-data API to load blog records based on the dynamic part of the URL.