Accessing a single record from a simple model - ember.js

I'm starting simple, trying to display a single value from a simple model.
This answer to "accessing the model from the template" suggests that it's necessary to extend ObjectController. At this point, there's have no application logic, so it doesn't seem like a controller (or a view) is really needed yet.
Likewise, there are no routes yet, so it doesn't seem like anything should be needed beyond App.IndexRoute.
The single object in the dictionary fixture has a title property with the value Hello Ember. I'm expecting to see that text displayed between two hard-coded arrows. Instead, all I get is the arrows.
The Index.html is:
<!DOCTYPE html>
<html>
<head>
<title>Dictionary</title>
</head>
<body>
<!-- Main body of the application -->
<script type="text/x-handlebars">
<p>Title: -->{{title}}<--</p>
</script>
<!-- ... Ember.js and other JavaScript dependencies ... -->
<script src="js/libs/jquery-1.10.2.min.js"></script>
<script src="js/libs/handlebars-1.0.0.js"></script>
<script src="js/libs/ember.js"></script>
<script src="js/libs/ember-data.js"></script>
<script src="js/app/application.js"></script>
<script src="js/routers/router.js"></script>
<script src="js/models/dictionary_model.js"></script>
<script src="js/controllers/dictionary_controller.js"></script>
</body>
</html>
And then the JavaScript:
// application.js
window.App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter.extend();
// router.js
App.Router.map(function() {
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('dictionary', 0);
}
});
// dictionary_model.js
App.Dictionary = DS.Model.extend({
title: DS.attr("string")
});
App.Dictionary.FIXTURES = [
{
id: 0,
title: "Hello Ember"
}];
// dictionary_controller.js
App.DictionaryController = Ember.ObjectController.extend({
});

I'm not sure where you're reading in the documentation that's contradicting, please update your question with the contradicting statements so they can be fixed.
The controller really only need be defined if you need to add additional computed properties, actions, or other methods. In your case you are correct in that it needn't be defined.
That being said, the application template (or unnamed template as in your case) is the root of your ember app. Any child routes/resources will be rendered in the {{outlet}} located in the application template(examples below).
The index route is a route underneath the application route. Resources are considered routes that can have children and generally associated with a model.
All this comes up to the main problem you're seeing. You've returned your model from the index route, but you are attempting to use it in the application route's template.
Here's a simplified version of your code
Code
App = Ember.Application.create();
App.ApplicationAdapter= DS.FixtureAdapter;
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('dictionary', 0);
}
});
App.Dictionary = DS.Model.extend({
title: DS.attr("string")
});
App.Dictionary.FIXTURES = [
{
id: 0,
title: "Hello Ember"
}];
Templates
<script type="text/x-handlebars">
<h2>Application Template</h2>
Here we Are in the Application Template
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
<h2>Index Template</h2>
{{title}}
</script>
Example in action
http://emberjs.jsbin.com/OxIDiVU/443/edit

Related

Why is itemController not set in the child view?

I want App.IndexRowController to be the controller for the three row views. Instead Ember sets them to plain Objects. I believe I'm properly setting itemController in the DataIndexController. I a version of this code without the nested route working as expected. Do I need to do something special when working with nested routes/needs?
JSBin: http://jsbin.com/sazafi/edit?html,css,js,output
To see the behavior go to #/data/index. Notice there are three li elements but no corresponding text (from getName). The getName controller property isn't accessible from the row template. Ember docs say that setting the itemController in the ArrayController should make that controller available to the template specified in itemViewClass. Take a look at the Ember Inspector and see that the controller for the three views is an Ember.Object, not App.IndexRowController.
JavaScript:
App = Ember.Application.create();
App.Router.map(function() {
this.resource("data", function() {
this.route("index")
});
});
App.DataIndexRoute = Ember.Route.extend({
model: function() {
return(
[
Ember.Object.create({name: 'row 1'}),
Ember.Object.create({name: 'row 2'}),
Ember.Object.create({name: 'row 3'})
]);
}
});
App.DataController = Ember.ArrayController.extend({
filter: ''
});
App.DataIndexController = Ember.ArrayController.extend({
needs: ['data'],
itemController: 'indexRow',
filter: Ember.computed.alias("controllers.data.filter"),
filteredContent: function(){
var filter = this.get('filter');
var list = this.get('arrangedContent');
return list.filter(function(item) {
return item.get('name').match(filter);
});
}.property('content', 'filter')
});
App.IndexRowController = Ember.ObjectController.extend({
// This method isn't accessible from the row template
getName: function() {
return(this.get('content').get('name'));
}.property()
});
App.DataIndexView = Ember.CollectionView.extend({
tagName: 'ul',
content: function(){
return this.get('controller.filteredContent')
}.property('controller.filteredContent'),
itemViewClass: Ember.View.extend({
controllerBinding: 'content',
templateName: 'row'
})
});
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Collection View</title>
<script src="http://code.jquery.com/jquery-2.0.2.js"></script>
<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.1.2.js"> </script>
<script src="http://builds.emberjs.com/release/ember.js"></script>
<script src="app.js"></script>
</head>
<body>
<script type="text/x-handlebars" data-template-name="application">
<h1>CollectionView With Item View</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="data">
{{input type="text" placeholder='row 1' value=filter}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="row">
{{getName}}
</script>
</body>
</html>
EDIT: I have a working example of how to set the controller in a child view of a Ember.ContainerView and how to filter the contents here: https://github.com/mkolenda/ember-listview-with-filtering. ListView is a descendent of ContainerView.
Simple solution is to use an {{each}} instead of a CollectionView.
This is a well-known "feature", aka design bug, in the Ember design for array controllers, item controllers, and collection views. It shouldn't be too hard to find references on the web about the problem and some suggested hacks/workarounds. You might start with https://github.com/emberjs/ember.js/issues/4137 or https://github.com/emberjs/ember.js/issues/5267.

Getting a weird persistance issue with ember data fragments and localstorage

Apologies if this isn't quite the right place (as opposed to either libraries own github issue page, but as I've not been able to determine exactly which library is not quite working correctly hard to log it specifically).
I'm using ember data fragments on my model (an array), and localstorage to save down my model. When calling rollback upon the saved model, it seems to reset the fragments back to their original state (i.e. no values), but it still maintains the fragment itself on the array, rather than dropping the item out of the array.
I've got a fiddle setup, click 'add' to add a model, click to view it's details, then click 'add' in there, followed by 'cancel'. You can see that the type + desc values drop out, but the element is still there.
If I switch out to using the Fixture adapter then it all works as expected, just not sure where to start even attempting to debug, I've stepped through many lines of _super calls, and what not trying to figure it out, but just get lost.
Note
This is a pseudo version of my actual app, and curiously enough when you navigate to the home page and then back to the details page, it seems to resolve the type/desc correctly, which it is not doing on my actual app, it still maintains the default values. However refreshing the page makes it work perfectly from then onwards.
Any help greatly appreciated!
<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="//builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.3.0.js"></script>
<script src="//builds.emberjs.com/tags/v1.7.0/ember.js"></script>
<script src="//builds.emberjs.com/canary/ember-data.js"></script>
<script src="//raw.githubusercontent.com/lytics/ember-data.model-fragments/master/dist/ember-data.model-fragments.js"></script>
<script src="//raw.githubusercontent.com/kurko/ember-localstorage-adapter/master/localstorage_adapter.js"></script>
<script>
window.App = Ember.Application.create();
App.ApplicationStore = DS.Store.extend();
App.ApplicationSerializer = DS.LSSerializer.extend();
App.ApplicationAdapter = DS.LSAdapter.extend({
namespace: 'cars'
});
App.Car = DS.Model.extend({
make: DS.attr(),
model: DS.attr(),
features: DS.hasManyFragments('feature')
});
App.Feature = DS.ModelFragment.extend({
type: DS.attr(),
description: DS.attr()
});
App.Router.map(function () {
this.route('index', { path: '/' });
this.route('car', { path: '/car/:car_id'});
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('car');
},
actions : {
add: function(model) {
var car = this.store.createRecord('car', {
make: 'Dodge',
model: 'Viper',
features: []
});
car.save();
}
}
});
App.CarRoute = Ember.Route.extend({
actions: {
add: function(model) {
model.get('features').createFragment({
type: 'Something',
description: 'Some desc'
});
model.save(); //*/
},
cancel: function(model) {
model.rollback();
}
}
});
</script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<script type="text/x-handlebars" data-template-name="index">
{{#link-to 'index'}}Home{{/link-to}}
<ol>{{#each}}
<li>{{#link-to 'car' this}}{{name}} {{model}}{{/link-to}}</li>
{{else}}
<button {{action 'add' model}}>Add</button>
{{/each}}</ol>
</script>
<script type="text/x-handlebars" data-template-name="car">
{{#link-to 'index'}}Home{{/link-to}}
<dl>
<dt>Make</dt>
<dd>{{make}}
<dt>Model</dt>
<dd>{{model.model}}</dd>{{#each features}}
<dt>{{_view.contentIndex}}. {{type}}</dt>
<dd>{{description}}</dd>
{{/each}}
</dl>
<button {{action 'add' model}}>Add</button>
<button {{action 'cancel' model}}>Cancel</button>
</script>
</body>
</html>
I havent worked with data fragments but fragment is a model itself so the element/fragment is still there because you have created a record for it.
This record is stored in the ember store until you do something with it.
Rollback, via emberjs.com,does this - "If the model isDirty this function will discard any unsaved changes".
The model in this case seems to be the fragment. Rollback gets rid of the changes, which is what it is doing in your case, removing the type and desc values, but the record itself is still in the store.
If you want to get rid of the fragment altogether you would have to delete it. http://emberjs.com/guides/models/creating-and-deleting-records/

Inheriting singular controller with render helper

I am trying to render a set of tabs for a set of objects (conversations) using the render helper for each. This is not part of a route as it is a persistent part of the interface. I have run into a problem where only the view with the same name as the model gets the intended controller (i.e. the panel contents and not the tab headers).
I have a Chat model, object controller and array controller (deliberately simplified here):
App.Chat = DS.Model.extend({ });
App.ChatsController = Ember.ArrayController.extend({
needs: 'application',
content: Ember.computed.alias('controllers.application.currentChats'),
});
App.ChatController = Ember.ObjectController.extend({ });
The ArrayController needed the needs/content properties because the chats are loaded in the application controller. I used the currentChats name as other routes may load non-current chats.
App.ApplicationController = Ember.Controller.extend({
init: function(){
this.store.find('chat', {"current": true});
this.set('currentChats', this.store.all('chat'));
}
});
I have no difficulty rendering the chat contents with the appropriate controller (into the 'chat' template). However, the chat tabs are given the default ObjectController, and therefore can't fire actions.
<script type="text/x-handlebars" id="application">
<!--application template-->
{{outlet chats}}
</script>
<script type="text/x-handlebars" id="chats">
<div id="chats">
<ul id="chat-tabs">
{{#each}}
{{render 'chatTab' this}}
{{/each}}
</ul>
{{#each}}
{{render 'chat' this}}
{{/each}}
</div>
</script>
<script type="text/x-handlebars" id="chatTab">
<!--tab template-->
</script>
<script type="text/x-handlebars" id="chat">
<!--chat template-->
</script>
The application router is as follows:
App.ApplicationRoute = Ember.Route.extend({
model: function(){ },
renderTemplate: function(){
this.render('application', { });
this.render('chats', {
into: 'application',
outlet: 'chats',
controller: 'chats'
});
}
});
This seems to come solely down to naming of the templates. The template called 'chat' inherits the correct controller, but chatTab doesn't despite receiving a chat as the model. Is there any way to force the view to inherit the correct controller? Or am I going about this in an idiosyncratic way.
Many thanks for your help to this Ember novice.
Andrew
It goes solely off the name provided to the render. The easiest way is to just create the other controller and extend the chat controller.
App.ChatTabController = App.ChatController.extend();

Ember js version 1: Route hierarchy / activation

We have a scenario along these lines:
Quote
--->Create
So route names quote and quote.create.
The issue is that we need to render the templates into the main outlet. So in our main route (that all other are inherited from) we have this:
renderTemplate: function() {
this.render({ into: 'application' });
}
When I navigate to quote it renders the quote view. From there I navigate to quote.create and it renders the create view. However, going back to quote from quote.create renders nothing.
How can I get around this?
When I go back to the \quote url route 'quote.index' is sought. Since it is defined 'automagically' nothing happens. When I define the route explicitly ember tries to find the quote.index template and view and these do not exist.
A workaround I tried is to have this:
App.QuoteIndex{Route|Controller|View} = App.Quote{Route|Controller|View}.extend()
EDIT:
Hey diddle-diddle, here is my fiddle :) http://jsfiddle.net/EbenRoux/Mf5Dj/2/
Ember.js does not rerender a parent view when transitioning to a parent route, so using into with a parent view template is not recommended.
There is an easier way to create what you are trying to: use a quote/index route:
<script type="text/x-handlebars" data-template-name="application">
<h1>Rendering Issue</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="quote">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="quote/index">
<h2>Quote View</h2>
{{#linkTo 'quote.create'}}Create a new quote{{/linkTo}}
</script>
<script type="text/x-handlebars" data-template-name="quote/create">
<h2>Quote Create View</h2>
<p>Some controls would go here.</p>
{{#linkTo 'quote'}}Go back to quote view{{/linkTo}}
</script>
App = Ember.Application.create({});
App.ApplicationRoute = Ember.Route.extend({
activate: function () {
this.transitionTo('quote');
}
});
App.Router.map(function () {
this.resource('quote', function () {
this.route('create');
});
});
See http://jsfiddle.net/eYYnz/

Using the new Ember RouterV2, how do you immediately redirect to another state from the index state?

What I have so far:
App = Ember.Application.create({
LOG_TRANSITIONS: true
});
App.Router.map(function(match){
match('/').to('application');
match('/edit').to('edit');
});
App.ApplicationRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('edit');
},
events: {
startEdit: function( context ){
this.transitionTo( 'edit' );
}
}
})
App.EditRoute = Ember.Route.extend({
init: function(){
this._super()
console.log('EditRoute')
},
});
Handlebars:
<script type="text/x-handlebars" data-template-name = 'application'>
Hello World
{{ outlet main }}
</script>
<script type="text/x-handlebars" data-template-name = 'edit'>
<div class = 'edit-background'> Edit State: {{ title }} </div>
</script>
I have four questions:
When I open the application it just remains in the home page, is the redirectTo hook suppose to immediately redirect you to another state?
In addition, I have this events hash in AplicationRoute per suggestion from here: How to programmatically transition between routes using Ember.js' new Router. but I read through the answers and still am not sure how you are supposed to use it.
How do I test the router on the console? before you could navigate between the states by calling transitionTo commands, what do I do now?
For some odd reason, my application template seem to rendered twice, as in there are two 'Hello World' up there, and when try to add something like: <li>{{#linkTo edit}}edit{{/linkTo}}</li>
I get this error:
'Uncaught TypeError: Cannot read property 'container' of undefined -- ember.js:2223'
This is how you would initially load the editView/route/template on application start up:
Router
App.Router.map(function(match){
match('/').to('application',function(match){
match('/').to('edit')
})
})
ApplicationTemplate
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
EditTemplate
<script type="text/x-handlebars" data-template-name="edit">
I am embedded!
</script>
EditRoute
EditRoute = Ember.Route.extend({
renderTemplates:function () {
this.render('edit', {
into:'application'
});
})