Having an issue with ember not destroying the index on exit.
Controllers/Routes:
App.AccountRoute = Ember.Route.extend({
activate: function () {
//Doing some stuff with login state here. Not important.
}
});
App.AccountController = Ember.Controller.extend({
needs: ['application'], //dependency
account: Ember.computed.alias('controllers.application.accountData'),
states: Ember.computed.alias('controllers.application.states'),
userToken: Ember.computed.alias('controllers.application.userToken'),
tabs: [{'pinned': true, 'name': { 'nestedLink': 'account.index', long: 'Account Overview' }}, {'name': { 'nestedLink': 'account.edit-profile', long: 'Edit Your Company Profile' }}, {'name': { 'nestedLink': 'account.edit-listings', long: 'Edit Your Company Listings' }}, {'name': { 'nestedLink': 'account.edit-payment-methods', long: 'Edit Your Saved Payment Methods' }}, {'name': { 'nestedLink': 'account.view-orders', long: 'View Orders' }}],
});
App.AccountIndexController = Ember.Controller.extend({
needs: ['account']
});
And here's the router:
App.Router.map(function () {
//...
this.resource('account', function() {
this.route('edit-profile');
this.route('edit-listings');
this.route('edit-payment-methods');
this.route('view-orders');
});
});
And the account template is set up like the following with the links to each nested route like: {{#linkTo account.index}}{{/linkTo}} {{#linkTo account.view-orders}}{{/linkTo}}
<script type="text/x-handlebars" data-template-name="account">
<h2>Account for {{account.name.company}}</h2>
<hr />
<div class="row">
<div class="col-md-2 account-sidebar">
<ul class="list-group">
{{#each tabs}}
{{#if pinned}}
{{#linkTo name.nestedLink class="list-group-item pinned-item"}}
{{name.long}}
{{/linkTo}}
{{else}}
{{#linkTo name.nestedLink class="list-group-item"}}
{{name.long}}
{{/linkTo}}
{{/if}}
{{else}}
<p class="text-danger">There are no options for your account.</p>
{{/each}}
</ul>
</div>
<div class="col-md-10 account-content">
{{outlet}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="account/index">
<h3>Account Overview</h3>
</script>
Switching back and forth between the overview (index) tab and a nested route results in:
You're missing a closing </div> in your "account/index" template. It should be
<script type="text/x-handlebars" data-template-name="account/index">
<h3>Account Overview</h3>
<hr />
<div class="row">
<div class="col-md-6">
<h4>Account Created:</h4>
<p class="text-muted"></p>
</div>
<div class="col-md-6">
<h4>Account Address:</h4>
</p>
</div>
</div>
</script>
See http://emberjs.jsbin.com/novib/3/
Related
When i run my webapp using handlebars as the template engine and want to go to the /dashboard page it renders an empty bracket instead of the actual code.
http://localhost:3001/dashboard
what is rendered
{}
Here is the route
router.get('/dashboard', withAuth, async (req, res) => {
try{
const dbUserData = await User.findByPk(req.session.user_id, {
attributes: { exclude: ['password'] },
include: [{ model: Post }],
});
const user = dbUserData.get({plain: true});
res.render('dashboard', {
...user,
loggedIn: req.session.loggedIn,
})
} catch(err){
console.log(err);
res.status(500).json(err);
}
})
Here is the dashboard.handlebars code
<section>
<div class="row">
<div class="col-auto">
<h2>Welcome, {{username}}!</h2>
</div>
</div>
<h2>Create New Post</h2>
<form id="new-form" class=" margin-10 new-post-form">
<div id="new-title">
<label for="post-title">Title</label>
<input type="text" id="post-title" name="post-title" />
</div>
<div id="new-text" class="margin-10">
<label for="post-text">Text</label>
<input id="post-text" name="post-text" />
</div>
<button type="submit" class="black-button">Create</button>
</form>
</section>
{{#if posts.length}}
<section>
<h2>Your Posts</h2>
<ol>
{{#each posts as |post|}}
<li>
{{> post-info post}}
Edit post
</li>
{{/each}}
</ol>
</section>
{{/if}}
<script src="/js/add-post.js"></script>
i'm new on VueJS and i'm making a chat following a tutorial.
in the tutorial, the professor puts a v-model in a component. When I do this, the component is not "rendered" on the screen, and the console responds to "text is not defined," I can not find this problem anywhere, if someone can help me, I'll be grateful, follow my html and js.
HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Chat JS</title>
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<div class="container">
<div class="row">
<div id="app">
<router-link to="/chat">Vá para o Chat</router-link><br>
<router-link to="/rooms">Vá para as Salas</router-link><br><br><br>
<router-view></router-view>
</div>
</div>
</div>
<script src="https://www.gstatic.com/firebasejs/3.5.2/firebase.js"></script>
<script type="text/javascript" src="node_modules/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script type="text/javascript" src="node_modules/vue/dist/vue.min.js"></script>
<script type="text/javascript" src="node_modules/vue-router/dist/vue-router.min.js"></script>
<script type="text/javascript" src="node_modules/vuefire/dist/vuefire.min.js"></script>
<script type="text/javascript" src="app/app.js"></script>
</body>
</html>
JS
var firebaseApp = firebase.initializeApp(config);
var db = firebaseApp.database();
const chatComponent = {
template: `
<div class="panel panel-primary">
<div class="panel-heading">Chat</div>
<div class="panel-body" style="height: 400px; overflow-y: scroll;">
<ul class="chat list-unstyled">
<li class="clearfix"
v-bind:class="{left: !isUser(o.email), right: isUser(o.email)}" v-for="o in chat.messages">
<span v-bind:class="{'pull-left': !isUser(o.email), 'pull-right': isUser(o.email)}">
<img v-bind:src="o.photo" class="img-circle"/>
</span>
<div class="chat-body">
<strong>{{o.name}}</strong>
<p>
{{ o.text }}
</p>
</div>
</li>
</ul>
</div>
<div class="panel-footer">
<div class="input-group">
<input type="text" class="form-control input-md" placeholder="Digite sua Mensagem..."/>
<span class="input-group-btn">
<button class="btn btn-sucess btn-md">Enviar</button>
</span>
</div>
</div>
</div>
`,
data: function() {
return {
user: {
email: 'jose#gmail.com',
name: 'Jose Joao',
},
chat: {
messages: [
{
email: "fulano#gmail.com",
text: "Olá eu sou o Fulano",
name: "Fulano",
photo: "http://placehold.it/50/000FFF/fff&text=00"
},
{
email: "jose#gmail.com",
text: "Estou Joia, eu sou o Robo",
name: "Robo",
photo: "http://placehold.it/50/FFFFFF/fff&text=EU"
},
{
email: "jose#gmail.com",
text: "Tudo bem com voce",
name: "Robo",
photo: "http://placehold.it/50/FFFFFF/fff&text=EU"
}
]
}
};
},
methods:{
isUser:function(email){
return this.user.email == email;
}
}
};
const RoomsComponent = {
template:`
<div class="col-md-12" >
<div class="col-md-4" v-for="o in rooms">
<div class="panel panel-primary">
<div class="panel-heading">
{{ o.name }}
</div>
<div class="panel-body">
{{o.description}}<br>
Entrar
</div>
</div>
</div>
<div class="col-md-4">
<input type="text" id="problem"/>
<ul>
</ul>
</div>
</div>
`,
firebase:{
array: db.ref('array')
},
data: function() {
return {
rooms: [
{id: "001", name: "Duvidas", description: "Duvidas Gerais"},
{id: "002",name: "Cadastros/Login", description: "Duvidas sobre Acesso"},
{id: "003",name: "Planos", description: "Duvidas sobre os Planos"},
{id: "004",name: "Creditos", description: "Duvidas sobre os Creditos"},
{id: "005",name: "Modelos", description: "Duvidas sobre os Modelos"},
{id: "006",name: "Envios", description: "Duvidas sobre os Envios"}
]
};
},
methods:{
goToChat: function(room){
router.push('/chat/'+room.id)
},
insertData: function(){
this.$firebaseRefs.array.push({
text: this.text
});
}
}
};
const routes =[
{ path: '/chat/:room', component: chatComponent },
{ path: '/rooms', component: RoomsComponent }
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
Recalling that the problem is in the input with id "problem" (to facilitate you) Basically as I said, just to put a V-model it of the error, if anyone knows, thank you!
You need to define your components as follows:
Vue.component('chat-component', {
template: `...`,
data: {
// your data
},
methods: {
// your methods
},
// and so on
});
You have only created javascript objects, not Vue components, in your sample code above.
You can also check the jsFiddle examples from previous questions on vue.js in stackoverflow, which will guide you on the syntax for creating Vue components, parent-child communication, etc.
I'm sure this one's gonna be extremely easy for you guys. I am trying to make a simple list of posts with the post titles always visible, and when you click a specific post in the list, you get the post's body. I used v-show for that. However, when I click a specific post, the bodies of all of the posts appear, instead of just the one that I clicked.
Here's the template:
<template>
<div class="container">
<h1>My Posts</h1>
<ul class="list-group">
<li class="list-group-item" v-for="post in list">
<div #click="changeShow">
<h4>{{ post.title }}</h4>
<p v-show="show">{{ post.body }}</p>
<span v-show="show" class="label label-primary">ID: {{ post.userId }}</span>
</div>
</li>
</ul>
</div>
And the logic:
<script>
export default{
data(){
return{
msg:'hello vue',
list: [],
show: false
}
},
ready(){
this.fetchPostList();
},
methods:{
fetchPostList: function () {
var root = 'http://jsonplaceholder.typicode.com';
this.$http.get(root + '/posts').then(function (response) {
this.list = response.data;
})
},
changeShow: function () {
this.show = !this.show;
}
}
}
There's a few ways to approach this depending on your needs.
Multiple Open
You can make each post it's own component, that way you can have show be tied to each individual post instead of all of them.
Vue.component('post', {
template: '#post-template',
props: {
post: Object,
},
data() {
return {
show: false,
}
},
methods: {
toggleShow() {
this.show = !this.show
},
},
})
Then you can have use it like this:
<post v-for="post in posts" :post="post"></post>
One Open
If you just want one open you can pass an id as a prop and show it based on that.
Vue.component('post', {
template: '#post-template',
props: {
post: Object,
selectedId: Number,
},
computed: {
show() {
return this.post.id === this.selectedId
},
},
})
Then you can do like
<post :selected-id="selectedId" :post="post" #click="selectedId = post.id"></post>
I cheated in a different way. Just added a show property to each post and toggled that.
new Vue({
el: 'body',
data: {
list: []
},
ready: function() {
this.fetchPostList()
},
methods: {
fetchPostList: function() {
setTimeout(function() {
this.list.push({
title: 'First Post',
body: 'This is the first Post',
userId: 'Joe',
show: false
});
this.list.push({
title: 'Second Post',
body: 'This is the second Post',
userId: 'Joe',
show: false
});
this.list.push({
title: 'Third Post',
body: 'This is the third Post',
userId: 'Joe',
show: false
});
}.bind(this), 2000);
},
changeShow: function(idx) {
this.list[idx].show = !this.list[idx].show;
}
}
});
<link href="//cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div class="container">
<h1>My Posts</h1>
<ul class="list-group">
<li class="list-group-item" v-for="post in list">
<div #click="changeShow($index)">
<h4>{{ post.title }}</h4>
<p v-show="post.show">{{ post.body }}</p>
<span v-show="post.show" class="label label-primary">ID: {{ post.userId }}</span>
</div>
</li>
</ul>
</div>
First of all, the method that I will give an example is not useful, but I am writing to show that there is such a way.
for example
<script>
export default {
data: {
isShow: false,
postUserId: null,
},
};
</script>
<template>
<div class="container">
<h1>My Posts</h1>
<ul class="list-group">
<li class="list-group-item" v-for="post in list">
<div #click="postUserId = post.userId; isShow = !isShow">
<h4>{{ post.title }}</h4>
<p v-show="show">{{ post.body }}</p>
<span v-show="isShow && postUserId == post.userId" class="label label-primary">ID: {{ post.userId }}</span>
</div>
</li>
</ul>
</div>
</template>
I'm using Intellij and Meteor to make an application and I'm trying to use Iron Router to create multiple pages, but when I call the Router in the Javascript file, it says that Router is an unresolved variable and that route is an unresolved function or method. I've checked the meteor folder and it appears that all the Iron Router files loaded fine. At the bottom of the root page I am working on it says
Oops, looks like there's no route on the client or the server for url:
"http://localhost:3000/."
If I navigate to http://localhost:3000/about, which is the only page I have a route set up for yet, the page is blank, except for my nav bar.
Here is my javascript file...
Items = new Mongo.Collection("items");
Found_items = new Mongo.Collection("found_items");
Router.route('home', {path: '/'}); // Add this route
Router.route('about', {path: '/about'});
if (Meteor.isClient) {
// This code only runs on the client
Template.body.helpers({
items: function () {
return Items.find({});
},
found_items: function () {
return Found_items.find({});
},
priceSum: function(){
var userItems = Found_items.find({
userId: this._id
}).fetch();
var prices = _.pluck(userItems, "price");
var totalTaxed = _.reduce(prices, function(sum, price){
var total = sum + parseFloat(price);
return total + (total * 0.04712);
}, 0);
return totalTaxed.toFixed(2);
},
calcTax: function () {
var userItems = Found_items.find({
userId: this._id
}).fetch();
var prices = _.pluck(userItems, "price");
var tax = _.reduce(prices, function(sum, price){
return (sum + parseFloat(price)) * 0.04712;
}, 0);
return tax.toFixed(2);
}
});
Template.body.events({
"submit .new-item": function (event) {
event.preventDefault();
var text = event.target.text.value;
Items.insert({
text: text,
createdAt: new Date(),
owner: Meteor.userId(),
username: Meteor.user().username
});
event.target.text.value = "";
}
});
Template.item.events({
"click .found": function (event, template) {
event.preventDefault();
var price = template.find('[name="price"]').value;
var text = template.find('.text').textContent;
Items.remove(this._id);
Found_items.insert({
text: text,
price: price
});
}
});
Template.body.events({
"click .remove": function(event) {
event.preventDefault();
Found_items.remove(this._id);
}
});
Accounts.ui.config({
passwordSignupFields: "USERNAME_ONLY"
});
}
And here is the HTML file
<head>
<title>Grocery List</title>
</head>
<template name="home">
<body>
<div>
<ul class="menu">
<li class="menuItem">{{> loginButtons}}</li>
<li class="menuItem">Home </li>
<li class="menuItem">About</li>
</ul>
</div>
{{#if currentUser}}
<div class="container">
<header>
<h1 id="title">Grocery List</h1>
<form class="new-item">
<input type="text" name="text" placeholder="Type to add new items" />
</form>
</header>
<ul>
{{#each items}}
{{> item}}
{{/each}}
</ul>
</div>
<div class="container">
<header>
<h1>Items Found</h1>
</header>
<ul>
{{#each found_items}}
{{> found}}
{{/each}}
</ul>
</div>
<div class="container">
<header>
<h3>
Tax: ${{calcTax}}
</h3>
<h2>
Total: ${{priceSum}}
</h2>
<button class="save">Save list</button>
</header>
</div>
{{else}}
<h3>Please log in first.</h3>
{{/if}}
</body>
</template>
<template name="item">
<li>
<button class="found">Got it!</button>
<input type="number" name="price" placeholder="Sale Price" />
<span class="text">{{text}}</span>
</li>
</template>
<template name="found">
<li>
<button class="remove">×</button>
<span class="text">{{text}}</span>
<span class="price">{{price}}</span>
</li>
</template>
<template name="about">
<head>
<title>About Grocery List</title>
</head>
<body>
<div>
<ul class="menu">
<li class="menuItem">{{> loginButtons}}</li>
<li class="menuItem">Home </li>
<li class="menuItem">About</li>
</ul>
</div>
<div class="container">
<header><h1>About</h1></header>
<p>This application was created using Meteor. It can be used to make, save and update grocery lists. Once the user is in the store, they can use it to check off items on the list, put in the price and see the total, with tax.<br>
Users can also save their previous lists to either reuse them, or compare current prices to previous ones.<br>
Future implementations of this page would also allow the user to change the tax rate depending on their location, and include coupons and other discounts in the pricing.</p>
</div>
</body>
</template>
Always add a route for the root.
Items = new Mongo.Collection("items");
Found_items = new Mongo.Collection("found_items");
Router.route('home', {path: '/'}); // Add this route
Router.route('about', {path: '/about'});
BTW, you have a head and body section within your template. That is rendered but does not have an effect in your browser.
Use the following syntax with IR's template helper pathFor:
<ul class="menu">
<li class="menuItem">{{> loginButtons}}</li>
<li class="menuItem">Home</li>
<li class="menuItem">About</li>
</ul>
In order to get your code working, I also fixed a couple of issues:
Removed head and body tags in templates.
Renamed Template.body.helpers to Template.home.helpers.
Renamed Template.body.events to Template.home.events.
Now it is adding new items to the collection and showing items.
you have to add a route with / to call localhost:3000
routing example
Router.configure({
layoutTemplate: 'layout',
});
Router.route('/', function () {
this.render('home');
},{
name: 'home'
});
Router.route('/about', function () {
this.render('about');
},{
name: 'about'
});
html
<template name="layout">
{{> yield}}
</template>
<template name="home">
<p>i am the homepage</p>
</template>
<template name="about">
<p>i am the about page</p>
</template>
I have been going through the emberjs course on codeschool and I came across this interesting problem. In 5.3, the question I am stuck on is:
Provide a model for the ContactsIndex Route which will provide just
one model – the contact for Anostagia. You can look this contact up by
ID.
No matter what I try it will not accept this app.js file?
I am nearly 90% sure I have this setup correctly... any thoughts?
var App = Ember.Application.create({
LOG_TRANSITIONS: true
});
App.Router.map(function() {
this.route('credits', { path: '/thanks' });
this.resource('products', function() {
this.resource('product', { path: '/:product_id' });
});
this.resource('contacts', function() {
this.resource('contact', { path: '/:contact_id' });
});
});
App.IndexController = Ember.ArrayController.extend({
productsCount: Ember.computed.alias('length'),
logo: 'images/logo-small.png',
time: function() {
return (new Date()).toDateString();
}.property()
});
App.ContactsIndexController = Ember.ObjectController.extend({
contactName: Ember.computed.alias('name'),
avatar: 'images/avatar.png',
open: function() {
return ((new Date()).getDay() === 0) ? "Closed" : "Open";
}.property()
});
App.ProductsController = Ember.ArrayController.extend({
sortProperties: ['title']
});
App.ContactsController = Ember.ArrayController.extend({
sortProperties: ['name']
});
App.ContactsIndexRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('contact', params.contact_id);
}
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('contact');
}
});
App.ProductsRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('product');
}
});
App.ContactsRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('contact');
}
});
App.IndexRoute = Ember.Route.extend({
model: function(){
return this.store.findAll('product');
}
});
App.ApplicationAdapter = DS.FixtureAdapter.extend();
App.Product = DS.Model.extend({
title: DS.attr('string'),
price: DS.attr('number'),
description: DS.attr('string'),
isOnSale: DS.attr('boolean'),
image: DS.attr('string'),
reviews: DS.hasMany('review', { async: true }),
crafter: DS.belongsTo('contact', { async: true })
});
App.Product.FIXTURES = [
{ id: 1,
title: 'Flint',
price: 99,
description: 'Flint is a hard, sedimentary cryptocrystalline form of the mineral quartz, categorized as a variety of chert.',
isOnSale: true,
image: 'images/products/flint.png',
reviews: [100,101],
crafter: 200
},
{
id: 2,
title: 'Kindling',
price: 249,
description: 'Easily combustible small sticks or twigs used for starting a fire.',
isOnSale: false,
image: 'images/products/kindling.png',
reviews: [],
crafter: 201
},
{
id: 3,
title: 'Matches',
price: 499,
description: 'One end is coated with a material that can be ignited by frictional heat generated by striking the match against a suitable surface.',
isOnSale: true,
reviews: [103],
image: 'images/products/matches.png',
crafter: 201
},
{
id: 4,
title: 'Bow Drill',
price: 999,
description: 'The bow drill is an ancient tool. While it was usually used to make fire, it was also used for primitive woodworking and dentistry.',
isOnSale: false,
reviews: [104],
image: 'images/products/bow-drill.png',
crafter: 200
},
{
id: 5,
title: 'Tinder',
price: 499,
description: 'Tinder is easily combustible material used to ignite fires by rudimentary methods.',
isOnSale: true,
reviews: [],
image: 'images/products/tinder.png',
crafter: 201
},
{
id: 6,
title: 'Birch Bark Shaving',
price: 999,
description: 'Fresh and easily combustable',
isOnSale: true,
reviews: [],
image: 'images/products/birch.png',
crafter: 200
}
];
App.Contact = DS.Model.extend({
name: DS.attr('string'),
about: DS.attr('string'),
avatar: DS.attr('string'),
products: DS.hasMany('product', { async: true })
});
App.Contact.FIXTURES = [
{
id: 200,
name: 'Giamia',
about: 'Although Giamia came from a humble spark of lightning, he quickly grew to be a great craftsman, providing all the warming instruments needed by those close to him.',
avatar: 'images/contacts/giamia.png',
products: [1]
},
{
id: 201,
name: 'Anostagia',
about: 'Knowing there was a need for it, Anostagia drew on her experience and spearheaded the Flint & Flame storefront. In addition to coding the site, she also creates a few products available in the store.',
avatar: 'images/contacts/anostagia.png',
products: [2]
}
];
App.Review = DS.Model.extend({
text: DS.attr('string'),
reviewedAt: DS.attr('date'),
product: DS.belongsTo('product')
});
App.Review.FIXTURES = [
{
id: 100,
text: "Started a fire in no time!"
},
{
id: 101,
text: "Not the brightest flame, but warm!"
}
];
The index.html file (if needed):
<!DOCTYPE html>
<html>
<head>
<base href='http://courseware.codeschool.com/ember/' />
<link href='bootstrap.css' rel='stylesheet' />
<link href='application.css' rel='stylesheet' />
<script src='jquery.js'></script>
<script src='handlebars.js'></script>
<script src='ember.js'></script>
<script src='ember-data.js'></script>
<script src='app.js'></script>
</head>
<body>
<script type='text/x-handlebars' data-template-name='application'>
<div class='navbar navbar-default'>
<div class='container'>
{{#link-to 'index' class='navbar-brand'}}<img src='images/logo.png' alt='logo' height='34' width='224' />{{/link-to}}
<ul class='nav navbar-nav navbar-right'>
{{#link-to 'index' tagName='li'}}Home{{/link-to}}
{{#link-to 'products' tagName='li'}}Products{{/link-to}}
{{#link-to 'contacts' tagName='li'}}Contacts{{/link-to}}
</ul>
</div>
</div>
<div class="container">
{{outlet}}
</div>
<footer class='container'>
<hr />
<p class='pull-left'>© 2013 The Flint & Flame</p>
<p class='pull-right'>{{#link-to 'credits'}}Credits{{/link-to}}</p>
</footer>
</script>
<script type='text/x-handlebars' data-template-name='index'>
<div class="jumbotron">
<h1>Welcome to The Flint & Flame!</h1>
<p class="tagline">
<img {{bind-attr src='logo'}} alt='Logo' />
Everything you need to make it through the winter.
</p>
<p>
{{#link-to 'products' class='btn btn-primary btn-lg'}}
Browse All {{productsCount}} Items »
{{/link-to}}
</p>
</div>
<p class='pull-right text-muted'>Rendered on {{time}}</p>
</script>
<script type='text/x-handlebars' data-template-name='contacts/index'>
<div class='row'>
<img {{bind-attr src='avatar'}} alt='Avatar' class='img-thumbnail col-sm-4'/>
<div class='col-sm-8'>
<h1>About The Fire Sprites</h1>
<p>Contact {{contactName}} for more info!</p>
<p>Current Status: {{open}}.</p>
</div>
</div>
</script>
<script type='text/x-handlebars' data-template-name='credits'>
<h1>Thanks for the Help!</h1>
<p>This site would not be possible without the hardworking Ember Core Team!</p>
</script>
<script type="text/x-handlebars" data-template-name='products'>
<div class='row'>
<div class='col-sm-3'>
<div class='list-group'>
{{#each}}
{{#link-to 'product' this classNames='list-group-item'}}
{{title}}
{{/link-to}}
{{/each}}
</div>
</div>
<div class='col-sm-9'>
{{outlet}}
</div>
</div>
</script>
<script type='text/x-handlebars' data-template-name='product'>
<div class='row'>
<div class='col-sm-7'>
<h2>{{title}}</h2>
<h3 class="text-success">${{price}}</h3>
<p class="text-muted">{{description}}</p>
<p>Finely crafted by {{#link-to 'contact' crafter}}{{crafter.name}}{{/link-to}}.</p>
<h3>Reviews</h3>
<ul>
{{#each reviews}}
<li><p>{{text}}</p></li>
{{else}}
<li><p class='text-muted'><em>No reviews yet. Be the first to write one!</em></p></li>
{{/each}}
</ul>
</div>
<div class='col-sm-5'>
<img {{bind-attr src='image'}} class='img-thumbnail img-rounded'/>
</div>
</div>
</script>
<script type='text/x-handlebars' data-template-name='products/index'>
<p class='text-muted'>Choose a product from those on the left!</p>
</script>
<script type="text/x-handlebars" data-template-name='contacts'>
<div class='row'>
<div class='col-sm-9'>
{{outlet}}
</div>
<div class='col-sm-3'>
<div class='list-group'>
{{#each}}
{{#link-to 'contact' this classNames='list-group-item'}}
{{name}}
{{/link-to}}
{{/each}}
</div>
</div>
</div>
</script>
<script type='text/x-handlebars' data-template-name='contact'>
<div class='row'>
<div class='col-sm-5'>
<img {{bind-attr src='avatar' alt='name'}} class='img-thumbnail img-rounded'/>
</div>
<div class='col-sm-7'>
<h2>{{name}}</h2>
<p>{{about}}</p>
<h3>Products</h3>
<ul>
{{#each products}}
<li>{{#link-to 'product' this}}{{title}}{{/link-to}}</li>
{{/each}}
</ul>
</div>
</div>
</script>
</body>
</html>
It looks like I was being too robust for this question. The answer was the following adjustment:
App.ContactsIndexRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('contact', 201);
}
});