Query server for data on bind/observe - ember.js

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'
});

Related

Ember js store and pass clicked {{#link-to}} property to Route model

I am new to Ember.js, and I am trying to learn it by rewriting an old app of mine. Unfortunately I got stuck fairly early and need some help, as couldn't find the answer on the Web.
I have the following code:
App = Ember.Application.create({
LOG_TRANSITIONS: true
});
App.Router.map(function () {
this.resource("users", function () {
this.resource("score", {path: "/:nickname"});
});
});
App.UsersRoute = Ember.Route.extend({
model: function () {
var url = "server url";
return new Ember.RSVP.Promise(function (resolve, reject) {
resolve($.getJSON(url));
});
}
});
App.ScoreRoute = Ember.Route.extend({
model: function () {
var url = "server url" + "?nickname=";
url += passedNicknameFromclickedLink;
return new Ember.RSVP.Promise(function (resolve, reject) {
resolve($.getJSON(url));
});
}
});
<script type="text/x-handlebars">
<nav>
{{#link-to "users"}}Users{{/link-to}}
</nav>
<main class="main-wrapper">{{outlet}}</main>
</script>
<script type="text/x-handlebars" data-template-name="users">
<div class="user-score">{{outlet}}</div>
<h2>Users</h2>
{{#each}}
<div>{{#link-to "score" nickname}}{{nickname}}{{/link-to}}</div>
<div>{{games}}</div>
<div>{{score}}</div>
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="score">
<h2>Score for {{nickname}}</h2>
{{#each categoryScores}}
<div class="scores">
<div>{{category}}</div>
<div>{{gamesPlayed}}</div>
<div>{{score}}</div>
</div>
{{/each}}
</script>
What I am trying to do is to take the {{nickname}} property clicked by the user in the {{#link-to}} in "users" template and pass it as "passedNicknameFromclickedLink" variable to App.ScoreRoute in order to make a server request. I was wondering what is the proper way to do this?
It looks like you've almost everything right, you just need 2 things. First, you can access the nickname dynamic segment in the model hook with the params parameter.
model: function(params) {
var passedNicknameFromclickedLink = params.nickname;
}
Secondly, because Ember assumes quite a few conventions with your route and models, you'll have to override the serialize method in the route as well. With the serialize method, what you want to do is start with your model (the JSON from the server) and give back the nickname that was used to find that model. I don't know exactly what your JSON looks like, but it might look something like this:
serialize: function(model) {
return encodeURI(model.nickname);
}

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

ember.js: using routes, templates and outlets to render model data

I'm going round in circles here, trying to pull all the components together to produce the desired view. I feel as if I just need to just tweak the dial to bring it all into focus but at the moment it aludes me.
I have two models - Person and Address - which I have created two templates for; I then want to render these two templates in another 'main' template. At the moment I am not linking them in anyway (eventually 1 person will have many nested addresses) because I want to understand the general principes first.
The two templates work individually using App.Router.map
this.resource('listOfPeopleTemplate', { path: '/' });
or
this.resource('listOfAddressesTemplate', { path: '/' });
but not together or when I add the mainViewTemplate and try to add both into that:
App.Router.map(function () {
//this.resource('listOfAddressesTemplate', { path: '/' });
//this.resource('listOfPeopleTemplate', { path: '/' });
this.resource('mainViewTemplate', { path: '/' });
});
The problem seems centered around:
App.MainViewTemplateRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('listOfPeopleTemplate', {into: 'mainViewTemplate', outlet: 'peops'});
this.render('listOfAddressesTemplate', {into: 'mainViewTemplate', outlet: 'address'});
}
});
Errors returned are "outlet (people) was specified but not found"; and "The value that #each loops over must be an Array..". I can see that I may need to do something about the controller for both the Addresses and People but I don't know what. Fact is, i've got myself into such a muddle I now can't even get the originally successfull version working (with either the address or people displaying in their own template).
I have made the following fiddle http://jsfiddle.net/4gQYs/4/. Please, bring me into focus!
I hope I understood your problem!
I have two routes people and places.
App.Router.map(function(){
this.resource('people');
this.resource('places');
});
I am loading the model for both the controller in model hook of people route.
App.PeopleRoute=Ember.Route.extend({
model:function(){
var places=Em.A();
$.getJSON("js/places.js").then(function(json){places.setObjects(json)});
var placesController=this.generateController('places',places);
placesController.set('content',places);
var people=Em.A();
$.getJSON("js/people.js").then(function(json){people.setObjects(json)});
return people;
},
renderTemplate:function(){
this.render('people',{into:"application",outlet:"people"});
this.render('places',{into:"application",outlet:"places"});
}
});
The following is not needed.May be useful in displaying some related data.
App.PeopleController=Ember.ArrayController.extend({
needs:'places'
});
Now I am rendering the two templates in main application template.
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet people}}
{{outlet places}}
</script>
<script type="text/x-handlebars" data-template-name="people">
{{#each controller}}
<p>{{name}}</p>
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="places">
{{#each controller}}
<p>{{name}}</p>
{{/each}}
</script>

Ember does not render after transition

I have just written extremly simple Ember app, built on top of the Rails app, working with Ember Data and displaying, creating and persisting just one entity type to the server. Everything with the latest tools (Ember v1.0.0-pre.4-134-gaafb5eb).
However, there is very strange problem I have encountered. My app has two views: entity list (index) and form for creating new entities. When I enter the index directly, everything displays OK. But when I go to the other view and then back to the list, the view is not rendered again. Where could be the problem?
I guess it might be caused by my (maybe incorrect) using new Ember router. So I'm pasting important (from my point of view) parts of the app here:
Router:
App.Router.map(function() {
this.resource('bands', function() {
this.route('new');
});
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('bands');
}
});
App.BandsRoute = Ember.Route.extend({
model: function() {
return App.Band.find();
}
});
App.BandsNewRoute = Ember.Route.extend({
renderTemplate : function(){
this.render('bands_new',{
into:'application'
});
}
});
Link back to list - which does not work:
App.BandsNewController = Em.ArrayController.extend({
cancel: function() {
this.transitionTo('bands');
}
});
Have a look at the whole app here: https://github.com/pavelsmolka/roommating
(It's hugely inspired by great https://github.com/dgeb/ember_data_example)
I don't believe it, but could it be bug in Ember itself?
I think your "render" call in your BandsNewRoute is messing things up.Try making things go more with Ember defaults. So I would refactor your app to do this:
(working fiddle: http://jsfiddle.net/andremalan/DVbUY/)
Instead of making your own render, all you need to do is create a "bands" template (it can be completely empty except for {{outlet}} if you want) and a "bands.index" template.
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="bands/index">
<h2>Bands Index</h2>
{{#linkTo bands.new}}New Band{{/linkTo}}
</script>
<script type="text/x-handlebars" data-template-name="bands">
<h1>Bands</h1>
<p>
{{#linkTo index}}Start Again{{/linkTo}}
{{#linkTo bands.new}}New Band{{/linkTo}}
</p>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="bands/new">
I'm in new band!
<a {{action "cancel"}}>Cancel</a>
</script>
Your routes also clean up really nicely this way:
App.Router.map(function() {
this.resource('bands', function() {
this.route('new');
});
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('bands');
}
});
App.BandsNewController = Ember.Controller.extend({
cancel: function() {
this.transitionTo('bands');
}
});
I hope that helps!

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.