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.
Related
I'm trying to use flow-router to render templates within a template based on nested routes. Rereading that confused me so I'll give an example.
Main Container:
<body>
<header>
<div id="headerRight">
<span class="headerButton" id="teachButton">Teach</span>
</div>
</header>
<div class="container" style="padding: 50px;">
{{> mainLayout}}
</div>
</body>
<template name="mainLayout">
<main>
{{>Template.dynamic template=main}}
</main>
</template>
Main Container JS:
FlowRouter.route('/', {
action: function() {
BlazeLayout.render("mainLayout", {main: "base"});
}
});
FlowRouter.route('/createCourse', {
action: function() {
BlazeLayout.render("mainLayout", {main: "courseChooseCont"});
}
});
Nested Routes:
<template name="courseChooseCont">
<header>
<i class="fa fa-times" aria-hidden="true"></i>
</header>
<ul>
<li>Category</li>
<li>Availability</li>
<li>Simple Availability</li>
</ul>
{{> courseLayout}}
<div name="courseMain"></div>
<div class="bottomBar">
<div class="back">Back</div>
<div class="next">Next</div>
</div>
</template>
<template name="courseLayout">
<main>
{{>Template.dynamic template=content}}
</main>
</template>
Nested Routes JS:
var createCourse = FlowRouter.group({
prefix: '/createCourse'
});
createCourse.route('/', {
action: function() {
BlazeLayout.render("courseLayout", {content: "base"});
}
});
createCourse.route('/availabilityCheck', {
action: function() {
BlazeLayout.render("courseLayout", {content: "availabilityCheck"});
}
});
createCourse.route('/simpleAvailability', {
action: function() {
BlazeLayout.render("courseLayout", {content: "simpleAvailability"});
}
});
When I render the nested routes it seems to overwrite the "courseChooseCont" template that should be housing the nested template and is simply displaying the nested template inside the 'Main Container'.
I'm assuming I'm using BlazeLayout wrong but I can't seem to find a solution.
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'm trying to do something very simple here and I couldn't make it work so far. It's probably me doing silly things.
You click in the circle to turn on and off a post. You can see that the alert message is coming right, the parameter is being updated, however I can't make the className change.
Here's the JS Bin: http://jsbin.com/tusimozo/1/edit
<script type="text/x-handlebars" data-template-name="index">
{{ group-list posts=model }}
</script>
<script type="text/x-handlebars" id="components/group-list">
<div class="row">
<table width="100%">
<thead>
<tr>
<th width="90">Status</th>
<th align="left">Categories</th>
</tr>
</thead>
<tbody>
{{# each item in posts}}
{{list-item published=item.published title=item.title pubDate=item.pub_date}}
{{/each}}
</tbody>
</table>
</div>
</script>
<script type="text/x-handlebars" id="components/list-item">
<tr>
<td>
<a href="#" {{bind-attr class=":post-status published:on:off"}} {{action "publish"}}></a>
</td>
<td align="left">{{title}}</td>
</tr>
</script>
app.js
App = Ember.Application.create();
posts = [{
title: "Items",
published: true
}, {
title: "Boards",
published: false
}];
App.IndexRoute = Ember.Route.extend({
model: function() {
return posts;
}
});
App.ListItemComponent = Ember.Component.extend({
actions: {
publish: function() {
var publishStatus = this.get('published') ? false : true;
this.set('published', publishStatus);
alert(this.get('published') ? 'publish' : 'unpublish');
}
}
});
What am I missing here?
Cheers!
So basically you should use classbindings on the component.
App.ListItemComponent = Ember.Component.extend({
tagName:'tr',
classNameBindings: ['isPublished:on:off'],
isPublished: function() {
return this.get('published');
}.property('published'),
actions: {
publish: function() {
this.toggleProperty('published');
}
}
});
JSBIN:
http://jsbin.com/mixeniheze/1/edit
You can add more than one class by using classNames as well.
classNames: ['class-name-1'],
classNameBindings: ['isSomethingTrue:class-name-2:class-name-3']
Further references here: https://guides.emberjs.com/v1.10.0/components/customizing-a-components-element/
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 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