Why is itemController not set in the child view? - ember.js

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.

Related

Component layout not rendered in ContainerView

I'm having trouble adding a Component dynamically to a ContainerView. See this jsfiddle:
http://jsfiddle.net/theazureshadow/7n2hz/
The component root element is placed in the container, but the layout associated with the component is not rendered.
App = Ember.Application.create({});
App.IndexRoute = Ember.Route.extend({});
App.IndexView = Ember.View.extend({
didInsertElement: function() {
console.log('IndexView inserted');
var container = this.get('container');
container.pushObject(Ember.TextField.create());
container.pushObject(App.MyComponent.create());
}
});
App.MyContainer = Ember.ContainerView.extend({
didInsertElement: function() {
console.log('MyContainer inserted');
}
});
App.MyComponent = Ember.Component.extend({
classNames: ['my-component'],
//template: Ember.Handlebars.compile('<p>Compiled directly</p>'),
didInsertElement: function() {
console.log('MyComponent inserted');
}
});
If I uncomment the template property, it does render that content properly. I also insert the component directly into the index view, where it renders properly (though the classNames are not added in this case).
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
<h2>Render component directly:</h2>
{{my-component}}
<hr/>
<h2>Add component to container:</h2>
{{view App.MyContainer viewName="container"}}
</script>
<script type="text/x-handlebars" data-template-name="components/my-component">
<b>My component</b>
</script>
I've tried messing around with Ember.run, but no luck so far. Is didInsertElement the wrong hook for pushObject?
That should fix your problem. See the layoutName property.
App.MyComponent = Ember.Component.extend({
classNames: ['my-component'],
layoutName: 'components/my-component',
didInsertElement: function() {
console.log('MyComponent inserted');
}
});

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();

Accessing a single record from a simple model

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

Ember: controller properties not available in partial views - what am I doing wrong?

See JSFiddle: http://jsfiddle.net/cyclomarc/aYmuJ/3/
I set a property in the application controller and want to display this property in a partial view. This does not seem to work. I can access the property in the template itself, but not in the partial view rendered within the template ..
index.html
<script type="text/x-handlebars">
<h3>Ember access to controller properties</h3>
{{#linkTo 'about'}}About{{/linkTo}} <br><br>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="about">
Access to property in index template: <br>
<b>{{controllers.application.applicationVersion}}</b>
<br><br>
{{render "_footer"}}
</script>
<script type="text/x-handlebars" data-template-name="_footer">
Footer text (partial view) with a controller property:<br>
<b>{{controllers.application.applicationVersion}} MISSING</b>
</script>
app.js
var App = Ember.Application.create({});
App.Router.map(function () {
this.resource('about', { path: "/about" });
});
App.IndexRoute = Ember.Route.extend({
redirect: function () {
this.transitionTo('about');
}
});
App.ApplicationController = Ember.Controller.extend({
//Set some properties
applicationVersion: "1.0.0"
});
App.AboutController = Ember.Controller.extend({
needs: "application"
});
render view helper have your own context.
To use the current context in a other template use the partial view helper.
{{partial "footer"}}
When you use render a new controller is created, in that case named generated _footer controller.
Using partial will preserve the controller bound to the template that called the partial template
And since you used needs in about controller, you don't have it in the new generated controller.
Here is a sample

{{#each loop}} not working. What would be the right way to get it going

I am following an example at "emberjs.com" which isn't going too well. I have a "GuestController" and "GuestView" within my application. I would like to use the "{{#view}} & {{#each}} to output an object called "guests" from the "GuestView". I am following this online example:
http://emberjs.com/documentation/#toc_displaying-a-list-of-items
fiddle: http://jsfiddle.net/exciter/MjA5A/8/
Here is the code:
APP CODE:
$(function(){
App = Ember.Application.create({
ready: function(){
//alert("APP INIT");
}
});
App.ApplicationController = Ember.Controller.extend();
App.ApplicationView = Ember.View.extend({
templateName: "application",
classNames: ['']
});
App.GuestController = Ember.Controller.extend();
App.GuestView = Ember.View.extend({
guests: [{name:"The Doctor" },
{name:"The Scientist" },
{name:"The Maestro"}]
});
App.initialize();
});
HTML:
<script type="text/x-handlebars" data-template-name="application">
{{#each App.GuestController}}
{{#view App.GuestView}}
{{guests}}
{{/view}}
{{/each}}
</script>
First of all, we use {{each}} block helper to iterate over an array of items, now when you say {{#each GuestController}} the controller should be of type Ember.ArrayController, and the {{#each GuestController}} looks for the content property inside the GuestController which will be used to iterate over, As per the example I think this is what you are trying to implement...Instead if you want to iterate over an Array inside a view check this