Ember route map url with query parameters - ember.js

this is my route configuration (route.js using ember-cli)
this.resource('xero-invoices', {path:'/loans/xero/:loan_id/invoices'})
but ember cuts query string when trying to route this address . How to fix problem?

The portion of your route definition had a minor typo missing a closing curly brace, but I am assuming that is not really the issue. Here it is fixed for clarity.
this.resource('xero-invoices', {path:'/loans/xero/:loan_id/invoices'})
The resource above has a dynamic segment of lone_id not a query string. Query string support is currently in the beta builds of Ember and not in stable. Any query string functionality you are trying to use would be handled in your controller. http://emberjs.com/guides/routing/query-params/
If you had a jsbin or more code I may be more helpful.
Here is a trivial jsbin showing the dynamic segment working - http://emberjs.jsbin.com/casana/1/edit
Edit:
In your example jsbin you are trying to use the (query-parms) helper for link-to which is only available if you are using the beta version of Ember. In your application controller if you remove it from your application template you won't get an error. In your route, since you are returning the query param as the model, the oauth_token is accessible via the model property in the controller.
Source: http://jsbin.com/bufukiqisika/8/edit
App = Ember.Application.create();
App.Router.map(function() {
this.resource('xero-invoices', { path:'/loans/xero/:loan_id/invoices' });
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return ["red","blue","green"];
}
});
App.XeroInvoicesRoute = Ember.Route.extend({
model: function(params) {
window.console.log(params);
return params.queryParams["oauth_token"];
}
});
Templates:
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="xero-invoices">
{{model}}
</script>
Example: http://jsbin.com/bufukiqisika/8#/loans/xero/09870987/invoices?oauth_token=foo

You can use query-parameters which are defined on controllers.
Something like this should work:
MyController = Ember.Controller.extend({
queryParams: ['searchvalue'],
searchvalue : null
})
And in you Template:
{{input type="text" valueBinding=controller.searchvalue}}
So your search value will be represented in the URL, e.g. “…/myapp/seach?searchvalue=foo”
See http://emberjs.com/guides/routing/query-params/

Related

Dynamic value in application template

I tried to implement user name displaying after log in. It displays in top menu. But top menu is getting displayed before log in, so it user name is getting cached.
I tried many approaches, and using volatile() is seems the best option, but it doesn't work. In this simple example currentTime calculates only once:
<script type="text/x-handlebars" data-template-name="application">
{{currentTime}}
</script>
App.ApplicationController = Ember.Controller.extend({
currentTime: function() {
console.log('computing value');
var time = new Date();
return time;
}.property().volatile()
});
Ember version 1.3
P.S. I prepared the gist to illustrate this issue: http://jsbin.com/OPUSoTaF/1
Actually, I can't find ANY way do display dynamic value in Ember's application template. Tried to display value from another controller using {{render}} helper, value still gets cached.
It seems that I just need to update value on ApplicationController from some other controller, and to do it in a proper way. Like this:
App.LoginController = Ember.Controller.extend({
needs: 'application',
setTime: function() {
this.get('controllers.application').set('currentTime', new Date());
}
});
The application to illustrate: http://jsbin.com/OPUSoTaF/4/edit
You can change ember properties and thus views using Handlebars {{action 'actionName'}} helper. You can add action helper to almost any UI element in your handlebars template an it is usually triggered on click. When triggered it calls actionName method on the controller.
Example:
Handlebars template:
<script type="text/x-handlebars" data-template-name="application">
<button {{action 'login'}}>Login</button>
{{loginTime}}
</script>
Controller:
App.ApplicationController = Ember.Controller.extend({
loginTime: 'User not logged in yet',
actions: {
login: function() {
// ... Do some login stuff ...
this.set('loginTime', new Date());
}
}
});
Working jsbin example is here: http://jsbin.com/udUyOXaL/1/edit

Different result from App.Model.find() and App.Model.find({params})

I'm trying to create a todo-app (original, right?) and have a problem that ember doesn't display my newly created objects if use find({some parameters}) instead of find().
The problem seems to be that App.Model.find() returns a different result than, for example, App.Model.find({checked: false}).
I've created TasksRoute and defined the model data as App.Task.find().
When using App.Task.find() I can create a new task by typing:
var task = App.Task.createRecord({name: "taskName"});
task.get("store").commit();
The list is updated and the view displays the newly created task.
But if I instead use App.Model.find({}), or with any other hash, and then create a task nothing happens.
Here's the html:
<script type="text/x-handlebars" data-template-name="application">
{{#linkTo "tasks"}}Tasks{{/linkTo}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="tasks">
{{#each controller}}
{{name}}<br />
{{/each}}
</script>
Javascript:
App = Em.Application.create();
App.Store = DS.Store.extend({
revision: 12,
adapter: DS.RESTAdapter.create({url: "http://localhost/backend/"})
});
App.Task = DS.Model.extend({
name: DS.attr('string')
})
App.TasksRoute = Ember.Route.extend({
model: function() {
return App.Task.find();
}
});
App.Router.map(function(){
this.route('tasks');
});
App.TasksController = Em.ArrayController.extend({});
The reason that I don't want to use find() is that I want to be able to specify my query. For example I would like to be able to retrieve all tasks that are checked by providing a hash to find().
(I tried creating a jsFiddle but didn't manage to get it to work with fixtures)
Is this a bug or have I done something wrong?
Thanks in advance
You might want to use filterProperty('checked', false) instead of find({params})

Ember.js router v2 App.Router.map inconsistent in documentation?

I'm creating an app with ember.js. I started on PRE.2 but ended up using ember-data v11 so upgraded to master for ember proper. This meant having to change to the new v2 router interface (which as a side note I think is so much better, so thank you.)
I'm having a couple of problems trying to figure out how it works, I'm deep in the guide but there are a couple of inconsistencies I can't quite get my head around:
1)
It seems there are two different conventions used to configure the route map:
In the 'Templates' section, a match().to() interface is used
App.Router.map(function(match) {
match('/').to('index');
match('/posts').to('posts');
match('/posts/:post_id').to('post');
});
( this method is also used in Tom Dale's gist )
In the 'Routing' section, a resource / route interface is used:
App.Router.map(function() {
this.resource('posts', function() {
this.route('new');
});
});
Here it states that a "resource" should be used for noun routes, and "route" for verb routes.
Then in the "Redirecting to a different URL" section, this noun/verb convention isn't followed:
App.Router.map(function(match) {
this.resource('topCharts', function() {
this.route('choose', { path: '/' });
this.route('albums');
this.route('songs');
this.route('artists');
this.route('playlists');
});
});
My first question is:
Going forward, what is the proper convention for creating routes?
My second question follows on from that and is more relevant to my application:
How do I link from a top level "resource" route to a nested "route" route and pass through the appropriate models?
( there is an example of this in the 'Links' section of the 'Templates' doc, but it pertains to the match().to() interface, and I'm specifically working with the resource/route interface )
Here's my example:
I have created a simple site structure based on streams, a stream consists of details, a set of posts, handles and history. My routing is set up like so:
App.Router.map(function() {
this.resource('streams');
this.resource('stream', { path: '/stream/:stream_id' }, function(){
this.route('details');
this.route('posts');
this.route('handles');
this.route('history');
});
});
My streams route looks like this:
App.StreamsRoute = Ember.Route.extend({
model: function() {
return App.Stream.find();
},
setupController: function(controller, model) {
controller.set('content', model);
}
});
and the template:
<script type="text/x-handlebars" data-template-name="streams">
<ul>
{{#each stream in controller}}
<li>{{#linkTo "stream" stream}} {{stream.title}} {{/linkTo}}</li>
{{/each}}
</ul>
</script>
My stream (singular) route:
<script type="text/x-handlebars" data-template-name="stream">
<nav>
{{#linkTo "stream.details" }}Details{{/linkTo}}
</nav>
{{outlet}}
</script>
Now, I'd like to link to my sub route "details", but I'm not sure what to place in the linkTo so that my model (which is a stream) is passed down into this sub-route:
App.StreamDetailsRoute = Ember.Route.extend({ });
My "details" template just displays some attributes off the stream object.
<script type="text/x-handlebars" data-template-name="stream/details">
<h2>Stream Details</h2>
<p>Id: {{id}}</p>
<p>Title: {{title}}</p>
</script>
I will also want to link through to posts, history and handles sub-routes and be able to display these aggregations based on the stream model. I'm not sure exactly how to do this. I assume I need to use setupController to get the items to display, I'm just not sure how to get the stream object down into these sub routes.
Going forward, what is the proper convention for creating routes?
The Resource/Route approach as described in http://emberjs.com/guides/routing/defining-your-routes/
How do I link from a top level "resource" route to a nested "route" route and pass through the appropriate models?
Specify the name of a route as the first parameter, followed by any contexts that are required. So in your example, when creating a link to "stream.details" from the stream template you need to specify this as the context.
{{#linkTo "stream.details" this}}Details{{/linkTo}}
The approach described in http://emberjs.com/guides/templates/links/ still covers the basics.
When in doubt I recommend checking the test cases for link_helper. For example: https://github.com/emberjs/ember.js/blob/master/packages/ember/tests/helpers/link_to_test.js#L249

ember.js routers and connectOutlets

I'm trying to use an already instantiated controller in my ember route.
Is it not normal to ever have instantiated a controller and want to use that in a route? I know that if I the application instantiate a controller for me, I can then to router.get("myController") but that won't access one that I had instantiated myself.
How do I get the piece of code at the bottom of my router to work?
HTML
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="instanced">
<h1>Hello from instanced template</h1>
{{showinstancedvalue}}<hr>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="foobar">
<h1>hello from foobar</h1>
{{foobarvalue}}
</script>
Javascript
var App = Ember.Application.create();
// application view and controller
App.ApplicationView = Ember.View.extend({
templateName: 'application',
});
App.ApplicationController = Ember.Controller.extend();
// foobar controller and view
App.FoobarController = Ember.Controller.extend({
foobarvalue: "working"
});
App.FoobarView = Ember.View.extend({
templateName: 'foobar'
});
// instantiated controller and view
App.InstancedController = Ember.Controller.extend({});
App.instancedController = App.InstancedController.create({
myvar: "a value from an instantiated controller"
});
App.InstancedView = Ember.View.extend({
templateName: 'instanced',
});
App.instancedView = App.InstancedView.create({
showinstancedvalueBinding: 'App.instancedController.myvar'
});
App.instancedView.append();
App.router = Ember.Router.create({
enableLogging: true,
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
redirectsTo: 'works'
}),
works: Ember.Route.extend({
route: '/works',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('foobar');
}
}),
broken: Ember.Route.extend({
route: '/broken',
connectOutlets: function(router) {
// no error in console, but foobar doesn't appear
// router.get('instancedController').connectOutlet('foobar');
// *** this one was my best guess as to what I thought would work ***
// no error in console, but foobar doesn't appear
// App.instancedController.connectOutlet('App.Foobar');
// Uncaught Error: assertion failed: The name you supplied foobar did not resolve to a view FoobarView
// App.instancedController.connectOutlet('foobar');
}
}),
})
});
App.initialize(App.router);​
Take a look at the connectOutlet definition, it includes a basic documentation as a comment so you can have a better understanding of how it's supposed to or and to be used.
Basically, you should really connect it to the applicationController, since the {{outlet}} sits on the ApplicationView template. In this scenario, the framework will find a view and controller that should be used in that state (in your case FoobarView and foobarController since it's specified with the argument 'foobar') and add to a collection (named controllers) inside the applicationController. If you try to connect directly to your instance of foobarController, it won't find a outlet on its view (which at that moment shouldn't be instantiated, I believe) AND you'd be saying to that controller "hey, find yourself, then connect yourself to you" kind of thing.
This could work if you had an outlet inside the foobar view template and you'd connect this outlet to something other than foobar (as a child state/route). So you should read more about the outlets and named outlets.
Additionally, I strongly recommend these links:
Router Primer - This article is about two weeks fresh, and it's apparently the best there is right now, make sure you read this one!
Ember.js Routing - the Director’s Cut - Step-by-Step post about the Router, make sure you read this one too.
Outlets - This is a bit old but is being mantained
JSFiddle Sample 1 - Sample Fiddle with routing you can use as reference
JSFiddle Sample 2 - Sample Fiddle with routing you can use as reference (this is newer than previous)
You should try to elaborate on your requirements. This makes no sense from my point of view.
broken: Ember.Route.extend({
route: '/broken',
connectOutlets: function(router) {
App.foobarController.connectOutlet('foobar');
}
}),
This code just can't work, since you are invoking connectOutlet on your fooBarController. So it searches for {{outlet}} in the assigend view. But in the template named foobar you do not not have a {{outlet}} specified. And even if you fix that, it makes just no sense, since this line would try to connect an outlet with a new instance of Foobar View. So you basically have the FooBarView of your FooBarController and inside its view, you try to connect a outlet again with a new instance of FooBarView??
So without explanation of requirements this question cannot be answered.

Query server for data on bind/observe

I apologize if this has a painfully obvious answer but I am both a JS and Ember noob and I am having trouble finding a solution to what I think is a common scenario. Essentially, I have a multi-page app with html/css/js front end and a java back end with an exposed REST api. I have 1 app.js file that I include in all screens and multiple controllers, some of which only apply to individual screens.
EDIT: Forgot my question. My question is how do I delay the query to my server for my user data until my controller has an observer. Since the controller is present on multiple screens (which dont all need it) I do not want to blindly query on creation of the object since it would be wasteful. For now i have a hacky way of doing it where at the end of my inline script tag of a page I call the populate method. Below is what my code currently looks like.
Section of app.js:
App = Ember.Application.create();
User = Ember.Object.extend({
username: 'empty',
fullname: 'empty user'
});
App.UserDataSource = Ember.Object.extend({
fetchMyUser: function(callback) {
$.get('ncaa/user', function(data) {
callback(User.create({
username: data.username,
fullname: data.fullname}));
});
}
});
App.userDataSource = App.UserDataSource.create();
App.UserController = Ember.Object.extend({
content: null,
populate: function() {
var controller = this;
this.get('dataSource').fetchMyUser(function(data) {
controller.set('content', data);
});
}
});
App.userController = App.UserController.create({
dataSourceBinding: Ember.Binding.oneWay('App.userDataSource')
});
Ember.run.sync();
Section of index.html
<script type="text/x-handlebars">
Welcome, {{App.userController.content.fullname}}
</script>
....other code....
<script type="text/javascript">
$(document).ready(function() {
....other code....
App.userController.populate();
});
</script>
I am pretty sure my first steps will be modifying that handlebars template to extend Ember.View but would like to know what the community believes is the best practice. Also, is it wrong for me to try and put all of this in one app.js file? It would be ok to query on creation of my controller if it was only imported on screens that required the user to display.
The answer for my question did end up being in the Ember.View. Essentially what I do is override the init function of my view which adds the call to populate the necessary controller with data. The view is that instantiated via the handlebars template so no more unnecessary calls or hacky work around. Important changes below.
Index.html:
<script type="text/x-handlebars">
{{#view App.UserNameView }}
Welcome, {{fullName}}
{{/view}}
</script>
App.js:
App.UserNameView = Em.View.extend({
init: function() {
this._super();
App.userController.populate();
},
fullNameBinding: 'App.userController.content.fullname',
userNameBinding: 'App.userController.content.username'
});