Emberjs New routing and query string or custom route matcher - ember.js

I'm trying to migrate my app to the new emberjs routing API.
With old router I had some workarounds to provide similar URI for objects saved by ID and for new objects which described by set of params. This were done for ability of exchange links to objects between users without permanently saving it. This is two simplified valid routes from my app:
/objects/12 // fetch object by id (/objects/:object_id)
/objects/<serialized params> // build new object from params (/objects/:params)
Both of this routes are similar to router because they all have dynamic parts and static parts are equal. So I wrote custom RouteMatcher to pickup right route. Lack of query string parsing forced me to do this hack as quick and semilegal solution, also there is ancient ticket about this feature on github.
With the new router matching has been extracted to separate package (route-recognizer) so I cannot do the trick (or it will be full of hacks and injections).
As I can see I have to choose from these options:
Totally rewrite my URIs and separate all intersecting routes
Rewrite URIs but try to implement query string parser for the new Ember.Router
Put all logic into one route and reimplement only serialize/deserialize methods (something dirty)
Second solution seems to be more clean.
What will be the best non complicated decision? Should I try to find another way?

The current router does not support query-string parameters.
We are tracking this bug at https://github.com/emberjs/ember.js/issues/1773. You may want to follow it.
In the meantime, your best bet is probably to use a dynamic segment and manually serialize (with the serialize hook) and deserialize (with the model hook).

Related

Ember JS automatically register directory classes to DI

Creating in-repo-addon in Ember.JS gives a lot of possibilities. Today I've stumbled into a case when I wanted to register all classes in directory (for example my-dir) into Dependency Injector (same way it's done with services by Ember, but I wanted to use some other namespace).
For example I want to allow developer to create multiple classes inside proxy directory, and register all of them in DI under proxy: namespace. So later we can use:
Ember.Component.extend({
myProxy: Ember.inject('proxy:my'),
});
You'll need to do this using an initializer. More details on this here: https://guides.emberjs.com/v2.12.0/applications/dependency-injection/
The hard part may be getting all proxy items in s folder to automatically register ...
Edit
Looks like I didn't spend enough time thinking about this. You should be able to do at least part of this easily. There are two parts to this.
Part 1
Ember currently uses the ember-resolver to handle lookups for various items. If you check the tests for the resolver you'll notice that you should be able to map in anything you want: https://github.com/ember-cli/ember-resolver/blob/master/tests/unit/resolvers/classic/basic-test.js
So in your case, if you do a Ember.getOwner(this).lookup('proxy:main') from within an Ember instantiated class (a route, controller or component for instance) it would look in app/proxy/main.js which your addon could be populating.
Details on the Ember.getOwner lookup are available here: https://emberjs.com/api/classes/Ember.html#method_getOwner
Part 2
So at this point you can lookup proxies (which would be doable in an init method). But if we want to get truly elegant we'd want to allow Ember.inject.proxy('main') syntax.
Doing so would involve calling a private method inside of Ember.inject in an initializer. Because that naming scheme is changing in the new Javascript modules RFC, it may be unwise to try to add this syntactic sugar ...
So I'd advise avoiding touching that private API unless it's really important to your app design.

Idiomatic way to use __container__.lookupFactory in Ember.js

In the notes of this commit, the Ember team have made it very clear that App.__container__.lookup() is not the way to get at controllers. Instead we should use the needs property.
I understand the rationale behind this, and the idiomatic way to access singleton controllers.
However, in my app, I have some cases where I need instance controllers. In that case, I am using App.__container__.lookupFactory() to get at the prototype which I can then create() or extend()
Is there a better way to do this (without using __container__?
Edit:
Here is an example use case.
App.MyContainerView = Ember.ContainerView.extend
...
addChildView: ->
#get("content").pushObject(App.MyChildView.create(...))
The above example will push a new view onto the stack (allowing views to be dynamically created)
However, these views will (may?) not have the right container (and other properties?) set due to being created using App.MyChildView.create(). This is especially true in cases where we are doing a partial integration of Ember into an existing app.
The way to create these views would instead be:
App.__container__.lookupFactory("view:my_child").create()
In which case everything would be ok.
Additional use cases exist, for creating instance controllers outside the context of the router.. but the idea is the same.
I don't know if you're still looking for an answer. I am also struggling with how to do things "the Ember way".
This answer put me on the right track, and should be relevant to your question:
"Please ensure this controller was instantiated with a container"
As for me, I had the same problem as in the above question: when I manually instantiated my App.AnyOtherController with App.AnyOtherController.create(...), then inside this controller, I could not access dependency injections (such as a session object that I make available to all my controllers and routes).
Instantiating the same controller this way solves the problem by giving the controller a container:
this.container.lookupFactory('controller:any_other').create(...)
You should be able to access this.container from any view, and I guess, any controller, as long as they have been given a container.
You can Ember.String.decamelize('AnyOther') to convert the CamelCase controller name to a suitable string.
More on containers here: http://ember.zone/beginning-to-understand-the-ember-js-container/
If it doesn't help you, I still hope this helps someone out there, as this container stuff is a bit tricky at first...

ember-data adapter to read from cloudant RESTful API

The cloudant RESTful API is fairly simple but doesn't match the way ember-data expects things to be. How can I customize or create an Adapter that deals with these issues...
In my specific case I only want to load records from one of several secondary indexes (ie. MapReduce fnctions).
The URL for this is below, where [name] and [view] would change depending on user selection or the route I am in.
https://[username].cloudant.com/[db_name]/_design/[name]/_view/[view]
Looking at the ember-data source there doesn't seem to be an easy way of defining URLs like this. I took a look at findQuery and it expects to send any variables through as url params not as part of the actual URL itself.
Am I missing something? Is there an obvious way of dealing with this?
Then the data comes back in a completely different format, is there a way to tell ember what this format is?
Thanks!
I had similar problem where URL's are dynamic. I ended up creating my own adapater by extending DS.RESTAdapter and overriding the default buildURL method. For example:
App.MyAdapter = DS.RESTAdapter.extend({
buildURL: function(record, suffix) {
var username, db_name, name, view;
// Do your magic and fill the variables
return 'https://'+username+'.cloudant.com/'+db_name+'/_design/'+name+'/_view/'+view;
}
});
I ended up also defining my own find, findAll, findQuery, createRecord, updateRecord, deleteRecord etc. methods as I had to pass more variables to buildURL method.
If returning data is in different format then you can also write your own serializer by extending DS.JSONSerializer and define your own extraction methods extract, extractMany etc.
You should evaluate how well your API follows the data format required by ember/data RESTAdapter. If it is very different then it's maybe better to use some other component for communication like ember-model, ember-restless, emu etc, as ember-data is not very flexible (see this blog post). You can also write your own ajax queries directly from routes model hooks without using ember-data or other components at all. It is not very hard to do that.

Ember-Router: dynamically create state from a recursive path

I need a way for ember router to route to a recursive path.
For Example:
/:module
/:module/:submodule
/:module/:submodule/:submodule
/:module/:submodule/:submodule/...
Can this be done with Embers router, and if so, how?
I've been looking for examples, tearing apart the source, and I've pretty much come to the conclusion, it's not possible.
In a previous question, someone had pointed me to a way to get the url manually and split it, but I'm stuck at creating the state for the router to resolve to.
As of now, in my project, I currently just use the Ember.HashLocation to setup my own state manager.
The reason for the need of this, is because the module definitions are stored in a database, and at any given point a submodule of a submodule, recursively, could be added. So I'm trying to make the Application Engine handle the change.
Do your submodules in the database not have unique IDs? It seems to me that rather than representing your hierarchy in the path, you should just go straight to the appropriate module or submodule. Of course the hierarchy is still in your data model, but it shouldn't have to be represented in your routing scheme. Just use:
/module/:moduleId
/submodule/:submoduleId
And don't encode the hierarchy in the routes. I understand it might be natural to do so, but there's probably not a technical reason to.
If your submodules don't have unique ids, it's maybe a little tougher...you could build a unique ID by concatenating the ancestor ids together (say, with underscores), which is similar to splitting the URL, but a little cleaner probably. I will say that Ember/Ember Data doesn't seem to be too easy to use with entities with composite keys--if everything has a simple numeric key everything becomes easier (anyone want to argue with me on this, please explain to me how!).
DO you mean like this:
App.Router.map(function(match) {
match('/posts').to('blogPosts');
match('/posts/:blog_post_id').to('showBlogPost');
});

Django-tastypie creating a URL hierarchy

I'd like to create a URL hierarchy using Tastypie but am running into some errors. Here's how I'd like the hierarchy to work:
/recipe
/recipe/ID
/recipe/ID/spice
/recipe/ID/spice/ID
I can't find out how to do this. When I set this up following the Tastypi instructions my URLs would be like this:
/recipe
/recipe/ID
/spice
/spice/ID
If I change the resource_name for spice to "/recipe/spice" then I get a "NotFound: Invalid resource lookup data provided (mismatched type)" error.
Any suggestions about what I could do?
Tastypie is meant to help implement a REST API, and thus by default only supports URLs that conform to REST practices. Namely, each URL should contain a resource name ('recipe' or 'spice') and optionally an identifier for that resource ('ID'). Anything outside of this breaks from REST practices and if you're not implementing a REST API you may want to re-consider whether or not you should be using Tastypie.
That being said, Tastypie does provide a ton of hooks for customizing things. For custom URLs, you'll want to define the method override_urls to map certain URLs to custom views and do some pre-processing before sending it to the regular dispatchers.
If possible, I'd recommend just using standard REST practices and break things up as separate 'recipe' and 'spice' resources. If you need to filter on recipes based on spices that are in them, 'spices' should be passed in as a GET parameter rather than part of the base URL. Hope that helps.