Using ember.js I have an input:
{{input id="my_input" name="my_input" type="text"}}
Then I want to create a link, using linkTo, but want the value of the input to be part of the href. Like this:
{{#linkTo 'my_resource' my_input}}the link{{/linkTo}}
I have the resource defined like this:
App.Router.map(function() {
this.resource("my_resource", {path: ":my_input"});
});
Is that possible?
Thanks.
Definitely:
http://emberjs.jsbin.com/eFAGixo/1/edit
App.Router.map(function() {
this.resource('search', {path:'search/:search_text'});
});
App.SearchRoute = Ember.Route.extend({
model: function(params){
// at this point params.search_text will have
// what you searched for
// since you are sending a string, it will hit the model
// hook, had you sent an object it would have skipped this
// hook and just used the object as your model
return { searchInThisRoute: params.search_text};
}
});
<script type="text/x-handlebars" data-template-name="application">
<h2>Welcome to Ember.js</h2>
{{input value=searchText}}
{{#link-to 'search' searchText}} Go To Search{{/link-to}}
{{outlet}}
</script>
PS: This actually seems like it would make more sense as a button and using Ember Actions, but this works just as dandy.
Related
I have an Ember JS application with 99% of it's resources on the root level, which the user has to be logged in to access. All those resources, with the exception of /login, have the same template.
/login, however, has an entirely different template (no nav bar, etc). What's the best way to override the application.hbs template for the login page?
Most of the answers I see in other questions involve a sort of parent route for the authenticated routes, like so:
this.route('login');
this.route('main', { path: '/' }, function() {
this.route('posts');
});
That solution yields the right URL's, but it means link-to helpers and such have to use 'main.posts', etc routes, which rubs me the wrong way, and I'd like not to have that extraneous route level all over the place.
In Rails, I'd just say something like layout: nil, and and that would prevent the main template from being applied. Is there any equivalent in Ember, or another way to achieve the same goal cleanly?
To get rid of {{link-to 'main.posts'}}. You can add {resetNamespace:true} to a given route.
this.route('main', {path:'/'}, () => {
this.route('posts', {resetNamespace:true});
});
{{link-to 'posts'}} will navigate to your posts route
I got another solution
{{!-- application.hbs --}}
{{#if session.isAuthenticated}}
{{outlet}}
{{else}}
Then the welcome/login page goes here
{{/if}}
Inspired by samselikoff in this post
======EDIT======
Maybe we can even do this
//in app/routes/login.js
export default Ember.Route.extend({
renderTemplate: function() {
this.render({ outlet: 'un-auth' });
}
});
and
{{!-- application.hbs --}}
{{#if session.isAuthenticated}}
{{outlet}}
{{else}}
{{outlet "un-auth"}}
{{/if}}
I want to show the user-profile of the user after login. So i figured I'll make the Application.Controller an ObjectController and set it's model property to the user-object.
The problem I'm having right now is that I don't know how to show the user-properties in the nested profile template?
I actually saw other answers on SO using {{#each todo in App.todosController.content}}. but that only works for an ArrayController. Maybe this is only possible with an ArrayController, I really don't know, but that would be strange. Thanks for any help!
To test this I'm setting the model of the Application.Controller to the user-object when login is succesful.
Here is my code:
<script type="text/x-handlebars" id="profile">
<div class="content">
<div class="content-padded">
<div>
<p><h3>Username: {{{user.username}}}</h3><p>
<p>trials: {{user.trials}}</p>
<p>results: {{user.results}}</p>
<p>email: {{user.email}}</p>
</div>
</div>
</div>
</script>
App.Router.map(function() {
this.resource('signup');
this.resource('login');
this.resource('profile');
this.resource('practice');
this.resource('overview');
});
App.LoginController = Ember.ObjectController.extend({
model: {},
needs: ['application'],
loggedInUser: {id: 9,
username: "rooty",
trials: "fghsds",
results: "fdfsd",
email: "dsefs#ds.com"},
loginHandler: function(data) {
// sign in logic
this.set("controllers.application.isLoggedIn", true);
this.set("controllers.application.model", loggedInUser);
}
});
App.ApplicationController = Ember.ObjectController.extend({
isLoggedIn: false
});
The user-object looks like this:
User {id: 9,
username: "rooty",
trials: "fghsds",
results: "fdfsd",
email: "dsefs#ds.com"}
You're so close! So your login controller sets the model on the application controller. Your controller's Login Controller's needs property means that all properties on the scope of the Application controller will be available to your Login controller. Following that logic, couldn't your profile controller also needs: ['application']?
With a little work, we can call refer to users like you'd like from your profile controller:
needs: ['application'],
users: Ember.computed.alias('controllers.application.model')
Then in your profile template, what you already have should work:
<div>
<p><h3>Username: {{{user.username}}}</h3><p>
<p>trials: {{user.trials}}</p>
<p>results: {{user.results}}</p>
<p>email: {{user.email}}</p>
</div>
JSBin
Notice in the JSBin that on the index controller, I have a computed alias for the applications model. The input helper's value in the index template is bound to this computed alias, aka the application's model. Observe in the application template that I only use {{model}}. As you type in the input, you see the model change thanks to Ember's powerful binding so we've definitely targeted the application model via the needs and computed alias.
Same thing with the profile template.
I'm trying to create a page that contains master/detail, which is obviously easy using a static {{ outlet }} in ember, however, I'd like the detail to slide down after the row that was selected. For example, on this page: http://jsbin.com/ijejap/1/edit, if the detail of the name was to appear after the currently selected name instead of at the bottom of the page.
The problem I'm trying to solve is that I can't have an outlet within a repeater so that when I click Row 1, I want the outlet to be positioned below Row 1, and when I click Row 2, I want the outlet after Row 2. In other words, I want to dynamically position an outlet, I think, unless there's another way to do it.
Ok, I finally made a conditionnal outlet depending on wether the user is selected or not, but I had to work with DS.Model and its FIXTURES to make this work.
To be brief, I added an action before the link to set a selected property on the user object and record which user is selected to prevent having two users at the same time.
The position of the {{outlet}} then depends on the current selected user.
Here's the template part:
{{#each model}}
<li {{action 'select' this}}>
{{#link-to "user" this}}{{first}}{{/link-to}}
</li>
{{#if selected}}
{{outlet}}
{{/if}}
{{/each}}
And the App.ApplicationController part:
App.ApplicationController = Ember.ArrayController.extend({
currentUser: null,
actions: {
select: function(newUser) {
var oldUser = this.get('currentUser');
if(newUser !== oldUser) {
if(oldUser)
oldUser.toggleProperty('selected');
this.set('currentUser', newUser);
newUser.toggleProperty('selected');
}
}
}
});
Finally, the model definition:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return this.store.find('user');
}
});
App.User = DS.Model.extend({
first: DS.attr(),
last: DS.attr(),
avatar: DS.attr()
});
Here's the link to the JSBin: http://jsbin.com/cilari/3/
You can make use of components power here.
I made it on JSBin : http://jsbin.com/zovos/2/
First, you declare your user details as a new component, say user-details :
<script type="text/x-handlebars" id="components/user-details">
<li>{{user.first}}</li>
<h2>
{{user.first}} {{user.last}}
<img {{bindAttr src="user.avatar"}} class="pull-right" width=50 />
</h2>
<dl>
<dt>First</dt>
<dd>{{user.first}}</dd>
<dt>Last</dt>
<dd>{{user.last}}</dd>
</dl>
</script>
Then you call it from your list, using the current user as the user in the component :
<script type="text/x-handlebars">
<div class="container">
<div class="row-fluid">
<ul class="nav nav-list span3 well">
{{#each model}}
{{user-details user=this}}
{{/each}}
</ul>
</div>
</div>
</script>
You add the selected property to your component and an action like expandDetails :
App.UserDetailsComponent = Ember.Component.extend({
selected: false,
actions: {
expandDetails: function() {
this.toggleProperty('selected');
}
}
});
Finally, you add the action to your component template and a conditional display on the selected property :
<script type="text/x-handlebars" id="components/user-details">
<li><a {{action "expandDetails"}}>{{user.first}}</a></li>
{{#if selected}}
<h2>
{{user.first}} {{user.last}}
...
</dl>
{{/if}}
</script>
And, of course, you get rid of your user route.
Here is a link to Ember's Guide that show this on a sample post example : http://emberjs.com/guides/components/handling-user-interaction-with-actions/
Hope this helps.
Are you looking for an accordion?
http://addepar.github.io/#/ember-widgets/accordion
I have an idea but unproved yet, maybe you can try it.
For detail, you only need one template. For master, you need write several named outlet (see: http://emberjs.com/guides/routing/rendering-a-template/) and place each below the corresponding item of master view.
When each master item has been clicked, you need pass a param to the detail route in order to let it know what kind of data should be populated.
The key part is the renderTemplate hook of route (see: http://emberjs.com/api/classes/Ember.Route.html#method_renderTemplate). You can take advantage of it because it allows you to specify which named outlet should be used to render the template.
Or you may load all details' data at once and cache it, then pass an identifier when master item got clicked, in this way you can filter the model in advance and get prepared for later renderTemplate use.
This is just an idea and currently don't have spare time to try it, but as your wish, you got the dedicated route, controller and model. I think it is highly doable, let me know it you make any progress.
I have ember routes set up like so:
App.Router.map(function() {
this.resource("subreddit", { path: "/r/:subreddit_id" }, function() {
this.resource('link', { path: '/:link_id'} );
});
});
But I want the to view each link in a completely separate template. In other words, I want to render a different block of html, rather than render the link html into subreddit's {{outlet}}.
When you hit /r/xxx in the browser Ember will look for two templates related to subreddit. First it looks for 'subreddit' and then it looks for 'subreddit/index'. If 'subreddit' if found, then it is rendered and then if 'subreddit/index' is also found, it is rendered into the {{outlet}} of 'subreddit'. If 'subreddit' is not found Ember will just move on to 'subreddit/index'. The 'subreddit' template is also rendered for any sub-paths that are nested under /subreddit, such as the 'link' template for /r/xxx/yyy. 'subreddit' is kind of like a "per model layout" template that allows you to wrap (decorate) all of the sub-path templates.
Something like this should let you keep your nested routes, and allow you to use Ember default behavior.
<script type="text/x-handlebars" data-template-name="subreddit">
<p>A static header bar for this subreddit could go here, or not.</p>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="subreddit/index">
<p>
All of the details of the subreddit go here, this will be rendered
into the {{outlet}} of "subreddit".
</p>
</script>
<script type="text/x-handlebars" data-template-name="link">
<p>
Link view goes here. This template will replace "subreddit/index"
in the {{outlet}} of "subreddit".
</p>
</script>
If you want to render the links in separate templates you should not nest your routes:
App.Router.map(function() {
this.resource("subreddit", { path: "/r/:subreddit_id" });
this.resource('link', { path: '/:link_id'});
});
A different approach you can take could be to use named outlet's and override renderTemplate of the corresponding route and render the template in the place of the named outlet's, have a look here for more info on that: http://emberjs.com/guides/routing/rendering-a-template/
Hope it helps.
I have started learning Ember.js and have been trying to make a page with some data that updates when I get an event (pushed by web sockets).
To make it simple I made an example where I have a list of nodes and when I get a new node I want to call addNode on the controller to add the node. The UI should then update.
The problem I have is that the only way I managed to do this is by having a Global Controller, and then using that in my template instead of my model for the template.
I would like to link up the controller to the route, and have a method on the controller add data when the event arrives - not by having some global list or something.
is this possible? And if so how?
I have included my sample so you can change it and show me how its done.
js:
x = {title : 'test'};
App = Ember.Application.create();
App.Router.map(function() {
this.resource("system");
});
App.NodesController = Ember.ArrayController.create({
content: [],
addNode: function(nodeInfo){
this.pushObject(nodeInfo);
}
});
html:
<script type="text/x-handlebars" id="system">
{{#each App.NodesController}}
{{title}}
{{/each}}
</script>
Thanks, Jason
This is a little shady, but I'm not sure if the ember guys have provided another method of getting the controller externally yet.
jsbin
You've to put needs in your system controller.
App.SystemController - Ember.Controller.extend({
needs:['nodes']
});
and in your system template,
<script type="text/x-handlebars" id="system">
{{#each controllers.nodes}}
{{title}}
{{/each}}
</script>