I have the below which displays a list of products. When I click on a product I would like to see just the product information.
The only way I've managed to get the product info to appear is to use the {{outlet}} or {{render}} on the parent products template which is not what I want.
var App = Ember.Application.create();
App.ApplicationController = Ember.Controller.extend({
});
App.ProductsController = Ember.ArrayController.extend({
sortProperties: ['id']
});
App.Router.map(function () {
this.resource('products', function () {
this.resource('product', { path: ':product_id' });
});
});
App.ProductsRoute = Ember.Route.extend({
model: function () {
return App.Products.find();
}
});
// Models
App.Store = DS.Store.extend({
revision: 11,
adapter: 'DS.FixtureAdapter'
});
App.Products = DS.Model.extend({
title: DS.attr('string'),
artist: DS.attr('string'),
image: DS.attr('string'),
price: DS.attr('number'),
url: DS.attr('string')
});
App.Products.FIXTURES = [
{
id: 1,
title: 'The Door',
artist: 'Religious Knives',
image: 'http://ecx.images-amazon.com/images/I/51og8BkN8jL._SS250_.jpg',
large_image: 'http://ecx.images-amazon.com/images/I/51og8BkN8jL._SS500_.jpg',
price: 9.98,
url: 'http://www.amazon.com/Door-Religious-Knives/dp/B001FGW0UQ/?tag=quirkey-20'
},
//etc etc
];
-------------MARKUP------------
<script type="text/x-handlebars" data-template-name="products">
<span>{{ controller.model.length }} item(s) in stock</span>
<div>
{{#each product in controller.model}}
<div class="item">
<div class="item-image">
{{#linkTo "product" product}}
<img {{bindAttr src="product.image" alt="product.title"}}>
{{/linkTo}}
</div>
<div class="item-artist">{{product.artist}}</div>
<div class="item-title">{{product.title}}</div>
<div class="item-price">${{product.price}}</div>
</div>
</div>
{{/each}}
{{render product}}
</script>
<script type="text/x-handlebars" data-template-name="product">
<div class="item-detail">
<div class="item-image">
<img {{bindAttr src="large_image" alt="title"}} />
</div>
<div class="item-info">
<div class="item-artist">{{artist}}</div>
<div class="item-title">{{title}}</div>
<div class="item-price">${{price}}</div>
<div class="item-form">
<p>
<label>Quantity:</label>
<input type="text" size="2" data-bind="value: quantity" />
</p>
<p>
<button data-bind="click: $parent.addItem">Add to Cart</button>
</p>
</div>
<div class="item-link"><a {{bindAttr href="url"}}">Buy this item on Amazon</a></div>
<div class="back-link">« Back to Items</div>
</div>
</div>
</script>
UPDATE: This kind of works:
The issue is when going from a product back to products the products are no longer listed
App.ProductsRoute = Ember.Route.extend({
model: function () {
return App.Products.find();
}
});
App.ProductRoute = Ember.Route.extend({
model: function (params) {
return App.Products.find(params.product_id);
},
renderTemplate: function () {
this.render('product', { // the template to render
into: 'application', // the template to render into
outlet: 'main', // the name of the outlet in that template
});
}
});
You could change change your router mappings to something like this:
App.Router.map(function () {
this.route('products');
this.route('product', { path: 'product/:product_id' });
});
Also consider using an index route which transfers you to a specific location when the page loads:
App.IndexRoute = Em.Route.extend({
redirect: function() {
this.transitionTo('products');
}
});
Fiddle: http://jsfiddle.net/schawaska/d2FtF/
Run: http://jsfiddle.net/schawaska/d2FtF/show
Related
I've created a simple app that should iist each item from a model in a list, created using a javascrit template.
Fiddle
Html:
<div id="tagsList" class="box">
<div class="box-head">
<h2 class="left">Tags</h2>
</div>
<div class="box-content">
<input type="text" placeholder="Add New Tag" />
<button>+ Add</button>
<div data-bind="template: 'tagsTempl'"></div>
</div>
</div>
<script id="tagsTempl" type="text/html">
<ul>
{{each tags}}
<li class="tagItem">
<span>${Name}</span>
<div>
Edit
Delete
</div>
</li>
{{/each}}
</ul>
</script>
Javascript:
$(function () {
//$("#tagDialog").hide();
var data = [
{ Id: 1, Name: "Ball Handling" },
{ Id: 2, Name: "Passing" },
{ Id: 3, Name: "Shooting" },
{ Id: 4, Name: "Rebounding" },
{ Id: 5, Name: "Transition" },
{ Id: 6, Name: "Defense" },
{ Id: 7, Name: "Team Offense" },
{ Id: 8, Name: "Team Defense" }
];
var viewModel = {
tags: ko.observableArray(data),
tagToAdd: ko.observable(""),
addTag: function() {
this.tags.push({ Name: this.tagToAdd() });
}
}
ko.applyBindings(viewModel)
});
Output of list:
{{each tags}}
${Name}
Edit Delete
{{/each}}
The scripts file is accessible through viewing source. I'm not sure where my error is. Any help?
I updated your fiddle. Now it is working like you want it to: The list of tags is being rendered using the knockout standard method as described in the docs.
HTML
<ul data-bind="template: {name: 'tagsTempl', foreach: tags}"></ul>
Template
<script id="tagsTempl" type="text/html">
<li class="tagItem">
<span data-bind="text: Name"></span>
<div>
Edit
Delete
</div>
</li>
</script>
Also I connected the viewmodel to the view.
For example:
<button data-bind="click: addTag">+ Add</button>
You simply forgot most of it. I suggest you follow the interactive tutorials on how to do this.
Based on the code below, is there any reason why I am not getting the list of Categories populated, even though the browser console shows 6 when I type Categories.find().count()?
What did I do incorrectly? Here is my repository on GitHub.
categories.js:
Categories = new Mongo.Collection('categories');
if (Meteor.isServer) {
Categories.allow({
insert: function(userId, doc) {
return true;
},
update: function(userId, doc, fieldNames, modifier) {
return true;
},
remove: function(userId, doc) {
return true;
}
});
}
home_controller.js:
HomeController = RouteController.extend({
subscriptions: function() {
this.subscribe('categories');
this.subscribe('photos');
},
data: function () {
console.log(this.params.name);
Session.set('category', this.params.name);
},
action: function () {
this.render('MasterLayout', {});
}
}
list_categories.html:
<template name="ListCategories">
<ul>
{{#each Categories}}
<li id="categories"><a class="btn btn-default" href="/{{name}}">{{name}}</a></li>
{{/each}}
</ul>
</template>
list_categories.js:
Template.ListCategories.helpers({
Categories: function() {
return Categories.find();
}
});
list_photos.html:
<template name="ListPhotos">
<div id='photos'>
<div class='page-header'>
<h1>
<small>
{{#if catnotselected}}
Photos
{{else}}
{{category}} Photos
{{/if}}
</small>
</h1>
</div>
<div id='mainContent'>
{{#each photolist}} {{>photo}} {{else}} {{#if catnotselected}}
<div class='page-header'>
<h1><small>Select a category.</small></h1></div>
{{else}}
<div class='page-header'>
<h1><small>No photos in this category.</small></h1></div>
{{/if}} {{/each}}
</div>
</div>
</template>
list_photos.js:
Template.ListPhotos.helpers({
catnotselected: function() {
return Session.equals('category', null);
},
category: function() {
return Session.get('category');
}
});
master_layout.html:
<template name="MasterLayout">
<div class="navbar">
<div class="container">
<div class="navbar-inner">
<a class="brand btn" href="/"><img style="height: 40px; width: 135px;" src="img/logo.png" />
<p>Photos</p>
</a>
<div id="login-button" class="btn btn-default pull-right">
{{> loginButtons}}
</div>
</div>
</div>
</div>
<div class='container-fluid'>
<div class='row'>
<div class='col-xs-12 text-center'>
{{> yield 'categories'}}
</div>
<div class='col-xs-10 col-xs-offset-1 text-center'>
{{> yield 'photos'}}
</div>
</div>
</div>
</template>
master_layout.js:
Template.MasterLayout.onCreated(function() {
Session.set('category', null);
});
photos.js:
Photos = new Mongo.Collection('photos');
if (Meteor.isServer) {
Photos.allow({
insert: function(userId, doc) {
return true;
},
update: function(userId, doc, fieldNames, modifier) {
return true;
},
remove: function(userId, doc) {
return true;
}
});
}
routes.js:
Router.configure({
layoutTemplate: 'MasterLayout',
loadingTemplate: 'Loading',
notFoundTemplate: 'NotFound',
yieldTemplates: {
'photos': {
to: 'ListPhotos'
},
'categories': {
to: 'ListCategories'
}
}
});
Router.route('/', {
name: 'home',
controller: 'HomeController',
action: 'action',
where: 'client'
});
Router.route('/:name', {
name: 'photos',
controller: 'HomeController',
action: 'action',
where: 'client'
});
As expected, rendering the ListPhotos and ListCategories template fails. That's why there are no corresponding elements in your DOM, even though you can access documents of your Photos and Categories collection via the console.
Apparently, using yieldRegions in the global router config will fail, due to an iron-router issue, which requires to call this.render() in the route's action.
routes.js:
Router.configure({
layoutTemplate: 'MasterLayout',
loadingTemplate: 'Loading',
notFoundTemplate: 'NotFound',
yieldRegions: {
//each yield going to different templates
'ListPhotos': {
to: 'photos'
},
'ListCategories': {
to: 'categories'
}
}
});
Router.route('/', {
name: 'home',
controller: 'HomeController',
action: function () {
this.render();
},
where: 'client'
});
Router.route('/:name', {
name: 'photos',
controller: 'HomeController',
action: function () {
this.render();
},
where: 'client'
});
I'm doing some intro Meteor stuff, and I'm having trouble getting the correct values from my Collection. I'm trying to track ownership of the central site, as claimed by a button. The program is currently just an extension of the Try Meteor task tutorial. Anyways, here's the HTML and JS:
--------------HTML------------
<head>
<title>Todo List</title>
</head>
<body>
<div class="container">
<header>
<h1>Todo List</h1>
<form class="new-task">
<input type="text" name="text" placeholder="Type to add new tasks" />
</form>
{{> ownerclaim}}
<h4>Current owner: {{ownerget}}</h4>
</header>
<u1>
{{#each tasks}}
{{> task}}
{{/each}}
</u1>
{{> loginButtons}}
</div>
</body>
<template name="task">
<li class="{{#if checked}}checked{{/if}}">
<button class="delete">×</button>
<input type="checkbox" checked="{{checked}}" class="toggle-checked" />
<span class="text">{{text}}</span>
</li>
</template>
<template name="ownerclaim">
<button class="claim">Claim website</button>
</template>
<template name="ownership">
<span class="text">{{text}}</span>
</template>
-----------JS---------------
OwnerC = new Mongo.Collection("owner");
Tasks = new Mongo.Collection("tasks");
if (Meteor.isClient) {
Meteor.subscribe("own");
console.log(OwnerC.findOne({}));
Session.set("currentOwner", OwnerC.findOne({}));
Template.body.helpers({
tasks: function () {
return Tasks.find({}, {sort: {createdAt: -1}});
},
ownerget: function () {
console.log("JOHNNN");
console.log(Session.get("currentOwner"));
return Session.get("currentOwner");
}
});
Template.body.events({
"submit .new-task": function (event) {
var text = event.target.text.value;
Tasks.insert({
text: text,
createdAt: new Date(),
owner: Meteor.userId(),
username: Meteor.user().username
});
event.target.text.value = "";
return false;
}
});
Template.task.events({
"click .toggle-checked": function() {
Tasks.update(this._id, {$set: {checked: ! this.checked}});
},
"click .delete": function() {
Tasks.remove(this._id);
}
});
Template.ownerclaim.events({
"click .claim": function() {
OwnerC.update({}, {$set: {text: Meteor.user._id}});
}
})
Accounts.ui.config({
passwordSignupFields: "USERNAME_ONLY"
});
}
if(Meteor.isServer) {
console.log("YOYOYOYOYOYO");
Meteor.startup( function(){
console.log("YOYO");
OwnerC.insert({
text: "none",
createdAt: new Date(),
owner: "0",
username: "0"
});
console.log(OwnerC.findOne({}));
Meteor.publish("own", function() {
return OwnerC.find({});
});
});
}
For some reason, my server logs are showing OwnerC to contain the object, but the client logs are showing it as an empty Collection, and findOne in the client is returning undefined. Anyone know where I'm going wrong?
I can get the "Help Topics" title to render, but none of the FIXTURES that I've defined. Nothing in the {{#each model}} will render. This is my first time working with Ember, so anything (literally anything) will help as I'm not receiving any error messages in the browser debugger.
App.js
App.Router.map(function () {
this.resource('home', { path: "/Index" });
this.resource('agents', { path: "/Agents" });
this.resource('topics', function() {
this.resource('topic', {path: '/topic/:topic_id'})
});
this.resource('contacts', { path: "/Contacts" });
});
App.TopicRoute = Ember.Route.extend({
model: function () {
return App.Topic.find();
}
})
App.Topic = DS.Model.extend({
title: DS.attr('string'),
info: DS.attr('string')
});
App.Topic.FIXTURES = [{
id: 1,
title: "Periscope",
info: "Periscope is a read-only application pulling information from D3."
}, {
id: 2,
title: "Second post",
info: "ASP.NET MVC 4 is a framework for building scalable, standards-based web applications using well-established design patterns and the power of ASP.NET and the .NET Framework."
}, {
id: 3,
title: "Ember.js",
info: "Ember.js is designed to help developers build ambitiously large web applications that are competitive with native apps."
}];
View
<script type="text/x-handlebars">
<div class="navbar">
<div class="navbar-inner">
<ul id="menu">
<li>{{#linkTo 'home'}}Home{{/linkTo}}</li>
<li>{{#linkTo 'agents'}}Agents{{/linkTo}}</li>
<li>{{#linkTo 'topics'}}About{{/linkTo}}</li>
<li>{{#linkTo 'contacts'}}Contact{{/linkTo}}</li>
</ul>
</div>
</div>
{{outlet}}
</script>
<script type="text/x-handlebars" id="topics">
<div class="container-fluid">
<div class="row-fluid">
<div class="span3">
<table class='table'>
<thead>
<tr><th>Help Topics</th></tr>
</thead>
{{#each model}}
<tr><td>
{{#linkTo 'topic' this}}{{title}} {{/linkTo}}
</td></tr>
{{/each}}
</table>
</div>
<div class="span9">
{{outlet}}
</div>
</div>
</div>
</script>
<script type="text/x-handlebars" id="topic">
<h1>{{title}}</h1>
<div class="intro">
{{info}}
</div>
</script>
Try to change your route like this:
App.TopicsRoute = Ember.Route.extend({
model: function () {
return App.Topic.find();
});
Hope it helps.
I've followed the tutorial of one of the creators of Emberjs: http://www.youtube.com/watch?v=Ga99hMi7wfY
The goal of tutorial is to present basics of emberjs on an example, where there's a list of posts and if you edit one, the content gets mapped to the corresponding textarea or textfield. Then, the changes are visible in real time.
This, however, doesn't work. And I've been following the official tutorial (obviously, emberjs has changed a lot since this yt video).
Anyways, what am I missing?
Here's my code (HTML):
<script type="text/x-handlebars">
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="#">Bloggr</a>
<ul class="nav">
<li>{{#linkTo 'posts'}}Posts{{/linkTo}}</li>
<li>{{#linkTo 'about'}}About{{/linkTo}}</li>
</ul>
</div>
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" id="about">
<div class="about">
random text about
</div>
</script>
<script type="text/x-handlebars" id="posts">
{{#if isEditing}}
{{partial 'post/edit'}}
<button {{action 'save'}}>Done</button>
{{else}}
<button {{action 'edit'}}>Edit</button>
{{/if}}
<div class="about">
<table>
<tr>
<th>Id</th>
<th>Title</th>
<th>Author</th>
<th>Date</th>
<th>Utilities</th>
</tr>
{{#each model}}
<tr>
<td>{{id}}</td>
<td>{{title}}</td>
<td>{{author}}</td>
<td>{{publishedAt}}</td>
<td>{{#linkTo "post" this}}Details{{/linkTo}}</td>
</tr>
{{/each}}
</table>
</div>
<div>
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" id="post/_edit">
<p>{{view Ember.TextArea valueBinding="title" placeholder="test.."}}</p>
<p>{{view Ember.TextField valueBinding="author"}}</p>
</script>
<script type="text/x-handlebars" id="post">
<h1>{{title}}</h1>
<h2>by {{author}}</h2>
<small>{{date publishedAt}}</small>
</script>
This is the emberjs part:
App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
});
App.Router.map(function(){
this.resource("posts", function(){
this.resource("post", { path: ':post_id'});
});
this.resource("about");
});
App.PostsRoute = Ember.Route.extend({
model: function(){
return App.Post.find();
}
});
App.PostsController = Ember.ObjectController.extend({
isEditing: false,
edit: function(){
this.set("isEditing", true);
},
save: function() {
this.set("isEditing", false);
}
});
App.Post = DS.Model.extend({
title: DS.attr('string'),
author: DS.attr('string'),
publishedAt: DS.attr('date')
});
App.Post.FIXTURES = [
{
id:1,
title: "This is my title",
author: "John Doe",
publishedAt: new Date('12-27-2012')
},
{
id:2,
title: "This is another title",
author: "Jane Doe",
publishedAt: new Date('02-03-2013')
}
];
Ember.Handlebars.registerBoundHelper('date', function(date){
return date.getFullYear();
});
Name of controller should be PostController instead of PostsController. They are responsible for handling different routes.
#wedens is right you have a wrongly named controller for the single post, you should also check out this github repo which contains the (correct) source code for that example app build in the video.