TodoMVC-based Getting Started Guide

M2M relationships in Ember data are asked about quite frequently on Stack Overflow. Offering guidance on setting up those relationships would be a good idea. We had that problem ourselves and ended up solving it by using an adapter by @toranb.

For TasteJS: we’re planning on hopefully hammering out a spec for a complex, GitHub API driven application sometime soon. We want it to capture auth, session handling and a number of other features a simple Todo app just doesn’t capture.

@trek.glowacki we’ll be inviting you to take part into the conversations around this in the (hopefully) near future :slight_smile:

4 Likes

Update on this. I tried them. Features beyond Step19 stopped working for me. My stack:

DEBUG: ------------------------------- 
DEBUG: Ember.VERSION : 1.0.0-rc.1
DEBUG: Handlebars.VERSION : 1.0.0-rc.3
DEBUG: jQuery.VERSION : 1.9.1
DEBUG: -------------------------------

I’d need more information to make this actionable. Could you provide a little more context or maybe archive your version of the code and share it with me?

Archive file - http://ge.tt/api/1/files/6qisueb/0/blob?download

Awesome. I can have a look tonight.

it looks like you’re missing

Todos.TodosRoute = Ember.Route.extend({
  model: function () {
    return Todos.Todo.find();
  }
});

from your route at this step. Did you accidentally remove it?

Huh! Yeah, I replaced it with TodosIndexRoute in step18. Its working now.

There are now a few steps with text content https://github.com/emberjs/website/pull/399/files

hey Trek, Any chance you could upload to the “quick-start” code sample on github, the actually libraries you use for dependency in that specific example? I am having a ton of issues trying to get a version of ember to work with the right version of handlebars, getting Assertion errors, and not defined errors all around. It would be great if you could add a libs folder in that project folder for the currently working combination of ember.js handlebars.js and ember-data.js The code i’m refferring to is linked below: https://github.com/emberjs/quickstart-code-sample

Thank you :slight_smile:

For me, everything worked fine until I added the code that’s displayed at the Displaying Model Data step in the Getting Started Guide.

Routing doesn’t seem to work at all, when I add:

Todos.TodosRoute = Ember.Route.extend({});
    model: function () {
        return Todos.Todo.find();
    }
});

The entire UI disappears when I reload the application. Why is this happening?

HELP???


index.html

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Ember.js • TodoMVC</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <!-- template -->
    <script type="text/x-handlebars"  data-template-name="todos">
        <section id="todoapp">
                <!-- Header-->
                <header id="header">
                  <h1>todos</h1>
                  <input type="text" id="new-todo" placeholder="What needs to be done?" />
                </header>

          
                <!-- Main -->
                
                <section id="main">
                  <ul id="todo-list">
                    
                     {{#each controller}}
                      <li>
                        <input type="checkbox" class="toggle">
                        <label>{{title}}</label><button class="destroy"></button>
                      </li>
                    {{/each}}
                     
                  </ul>
                  
                  <input type="checkbox" id="toggle-all">

                </section>
                

                <!-- Footer -->
                <footer id="footer">
                  <span id="todo-count">
                    <strong>2</strong> todos left
                  </span>
                  <ul id="filters">
                    <li>
                      <a href="all" class="selected">All</a>
                    </li>
                    <li>
                      <a href="active">Active</a>
                    </li>
                    <li>
                      <a href="completed">Completed</a>
                    </li>
                  </ul>

                  <button id="clear-completed">
                    Clear completed (1)
                  </button>
                </footer>

        </section>

        <footer id="info">
          <p>Double-click to edit a todo</p>
        </footer>

    </script>     

    <!-- Js scripts -->
    <script src="js/libs/jquery-1.10.2.min.js"></script>
    <script src="js/libs/handlebars.js"></script>
    <script src="js/libs/ember-1.0.0-rc.6.js"></script>
    <script src="js/libs/ember-data.prod.js"></script>

    <!-- application & routes -->
    <script src="js/application.js"></script>
    <script src="js/router.js"></script>
    
    <!-- models -->
    <script src="js/models/store.js"></script>
    <script src="js/models/todo.js"></script>
    <!-- end scripts -->
  </body>
</html>

js/route.js

// Default route using the Router class
Todos.Router.map(function () {
  this.resource('todos', { path: '/' });
});


Todos.TodosRoute = Ember.Route.extend({
  model: function () {
    return Todos.Todo.find();
  }
});

js/application.js

// Create an instance of the application
window.Todos = Ember.Application.create();

js/models/todo.js

// Todo class
Todos.Todo = DS.Model.extend({
  title: DS.attr('string'),
  isCompleted: DS.attr('boolean')
});

// ... additional lines truncated for brevity ...
Todos.Todo.FIXTURES = [
 {
   id: 1,
   title: 'Learn Ember.js today!',
   isCompleted: true
 },
 {
   id: 2,
   title: '...',
   isCompleted: false
 },
 {
   id: 3,
   title: 'Profit!',
   isCompleted: false
 }
];

js/models/store.js

   Todos.Store = DS.Store.extend({
     revision: 12,
     adapter: 'DS.FixtureAdapter'
   });

style.css

html,
body {
	margin: 0;
	padding: 0;
}

button {
	margin: 0;
	padding: 0;
	border: 0;
	background: none;
	font-size: 100%;
	vertical-align: baseline;
	font-family: inherit;
	color: inherit;
	-webkit-appearance: none;
	/*-moz-appearance: none;*/
	-ms-appearance: none;
	-o-appearance: none;
	appearance: none;
}

body {
	font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
	line-height: 1.4em;
	/* background: #eaeaea url('bg.png'); */
	background: url('bg.png');
	color: #4d4d4d;
	width: 550px;
	margin: 0 auto;
	-webkit-font-smoothing: antialiased;
	-moz-font-smoothing: antialiased;
	-ms-font-smoothing: antialiased;
	-o-font-smoothing: antialiased;
	font-smoothing: antialiased;
}

#todoapp {
	background: #fff;
	background: rgba(255, 255, 255, 0.9);
	margin: 130px 0 40px 0;
	border: 1px solid #ccc;
	position: relative;
	border-top-left-radius: 2px;
	border-top-right-radius: 2px;
	box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
				0 25px 50px 0 rgba(0, 0, 0, 0.15);
}

#todoapp:before {
	content: '';
	border-left: 1px solid #f5d6d6;
	border-right: 1px solid #f5d6d6;
	width: 2px;
	position: absolute;
	top: 0;
	left: 40px;
	height: 100%;
}

#todoapp input::-webkit-input-placeholder {
	font-style: italic;
}

#todoapp input:-moz-placeholder {
	font-style: italic;
	color: #a9a9a9;
}

#todoapp h1 {
	position: absolute;
	top: -120px;
	width: 100%;
	font-size: 70px;
	font-weight: bold;
	text-align: center;
	color: #b3b3b3;
	color: rgba(255, 255, 255, 0.3);
	text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
	-webkit-text-rendering: optimizeLegibility;
	-moz-text-rendering: optimizeLegibility;
	-ms-text-rendering: optimizeLegibility;
	-o-text-rendering: optimizeLegibility;
	text-rendering: optimizeLegibility;
}

#header {
	padding-top: 15px;
	border-radius: inherit;
}

#header:before {
	content: '';
	position: absolute;
	top: 0;
	right: 0;
	left: 0;
	height: 15px;
	z-index: 2;
	border-bottom: 1px solid #6c615c;
	background: #8d7d77;
	background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
	background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
	background: -moz-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
	background: -o-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
	background: -ms-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
	background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
	filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
	border-top-left-radius: 1px;
	border-top-right-radius: 1px;
}

#new-todo,
.edit {
	position: relative;
	margin: 0;
	width: 100%;
	font-size: 24px;
	font-family: inherit;
	line-height: 1.4em;
	border: 0;
	outline: none;
	color: inherit;
	padding: 6px;
	border: 1px solid #999;
	box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	-ms-box-sizing: border-box;
	-o-box-sizing: border-box;
	box-sizing: border-box;
	-webkit-font-smoothing: antialiased;
	-moz-font-smoothing: antialiased;
	-ms-font-smoothing: antialiased;
	-o-font-smoothing: antialiased;
	font-smoothing: antialiased;
}

#new-todo {
	padding: 16px 16px 16px 60px;
	border: none;
	background: rgba(0, 0, 0, 0.02);
	z-index: 2;
	box-shadow: none;
}

#main {
	position: relative;
	z-index: 2;
	border-top: 1px dotted #adadad;
}

label[for='toggle-all'] {
	display: none;
}

#toggle-all {
	position: absolute;
	top: -42px;
	left: -4px;
	width: 40px;
	text-align: center;
	border: none; /* Mobile Safari */
}

#toggle-all:before {
	content: '»';
	font-size: 28px;
	color: #d9d9d9;
	padding: 0 25px 7px;
}

#toggle-all:checked:before {
	color: #737373;
}

#todo-list {
	margin: 0;
	padding: 0;
	list-style: none;
}

#todo-list li {
	position: relative;
	font-size: 24px;
	border-bottom: 1px dotted #ccc;
}

#todo-list li:last-child {
	border-bottom: none;
}

#todo-list li.editing {
	border-bottom: none;
	padding: 0;
}

#todo-list li.editing .edit {
	display: block;
	width: 506px;
	padding: 13px 17px 12px 17px;
	margin: 0 0 0 43px;
}

#todo-list li.editing .view {
	display: none;
}

#todo-list li .toggle {
	text-align: center;
	width: 40px;
	/* auto, since non-WebKit browsers doesn't support input styling */
	height: auto;
	position: absolute;
	top: 0;
	bottom: 0;
	margin: auto 0;
	border: none; /* Mobile Safari */
	-webkit-appearance: none;
	/*-moz-appearance: none;*/
	-ms-appearance: none;
	-o-appearance: none;
	appearance: none;
}

#todo-list li .toggle:after {
	content: '✔';
	line-height: 43px; /* 40 + a couple of pixels visual adjustment */
	font-size: 20px;
	color: #d9d9d9;
	text-shadow: 0 -1px 0 #bfbfbf;
}

#todo-list li .toggle:checked:after {
	color: #85ada7;
	text-shadow: 0 1px 0 #669991;
	bottom: 1px;
	position: relative;
}

#todo-list li label {
	word-break: break-word;
	padding: 15px;
	margin-left: 45px;
	display: block;
	line-height: 1.2;
	-webkit-transition: color 0.4s;
	-moz-transition: color 0.4s;
	-ms-transition: color 0.4s;
	-o-transition: color 0.4s;
	transition: color 0.4s;
}

#todo-list li.completed label {
	color: #a9a9a9;
	text-decoration: line-through;
}

#todo-list li .destroy {
	display: none;
	position: absolute;
	top: 0;
	right: 10px;
	bottom: 0;
	width: 40px;
	height: 40px;
	margin: auto 0;
	font-size: 22px;
	color: #a88a8a;
	-webkit-transition: all 0.2s;
	-moz-transition: all 0.2s;
	-ms-transition: all 0.2s;
	-o-transition: all 0.2s;
	transition: all 0.2s;
}

#todo-list li .destroy:hover {
	text-shadow: 0 0 1px #000,
				 0 0 10px rgba(199, 107, 107, 0.8);
	-webkit-transform: scale(1.3);
	-moz-transform: scale(1.3);
	-ms-transform: scale(1.3);
	-o-transform: scale(1.3);
	transform: scale(1.3);
}

#todo-list li .destroy:after {
	content: '✖';
}

#todo-list li:hover .destroy {
	display: block;
}

#todo-list li .edit {
	display: none;
}

#todo-list li.editing:last-child {
	margin-bottom: -1px;
}

#footer {
	color: #777;
	padding: 0 15px;
	position: absolute;
	right: 0;
	bottom: -31px;
	left: 0;
	height: 20px;
	z-index: 1;
	text-align: center;
}

#footer:before {
	content: '';
	position: absolute;
	right: 0;
	bottom: 31px;
	left: 0;
	height: 50px;
	z-index: -1;
	box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
				0 6px 0 -3px rgba(255, 255, 255, 0.8),
				0 7px 1px -3px rgba(0, 0, 0, 0.3),
				0 43px 0 -6px rgba(255, 255, 255, 0.8),
				0 44px 2px -6px rgba(0, 0, 0, 0.2);
}

#todo-count {
	float: left;
	text-align: left;
}

#filters {
	margin: 0;
	padding: 0;
	list-style: none;
	position: absolute;
	right: 0;
	left: 0;
}

#filters li {
	display: inline;
}

#filters li a {
	color: #83756f;
	margin: 2px;
	text-decoration: none;
}

#filters li a.selected {
	font-weight: bold;
}

#clear-completed {
	float: right;
	position: relative;
	line-height: 20px;
	text-decoration: none;
	background: rgba(0, 0, 0, 0.1);
	font-size: 11px;
	padding: 0 10px;
	border-radius: 3px;
	box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
}

#clear-completed:hover {
	background: rgba(0, 0, 0, 0.15);
	box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
}

#info {
	margin: 65px auto 0;
	color: #a6a6a6;
	font-size: 12px;
	text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
	text-align: center;
}

#info a {
	color: inherit;
}

/*
	Hack to remove background from Mobile Safari.
	Can't use it globally since it destroys checkboxes in Firefox and Opera
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
	#toggle-all,
	#todo-list li .toggle {
		background: none;
	}

	#todo-list li .toggle {
		height: 40px;
	}

	#toggle-all {
		top: -56px;
		left: -15px;
		width: 65px;
		height: 41px;
		-webkit-transform: rotate(90deg);
		transform: rotate(90deg);
		-webkit-appearance: none;
		appearance: none;
	}
}

.hidden{
	display:none;
}

Sorry about that. I fixed my issue by downloading and including handlebars-1.0.0-rc.4 from emberjs.com

This Ember noob needs a bit of help. I have built a few basic Ember.js apps. Now I am trying to make the todoMVC app work.

I am using a 64-bit Windows 7 PC and Notepad++ and testing each step using Firefox, Google, and IE. I’m also using the latest Ember Starter Kit (with RC6) and a recent version of Ember-data. But I’ve hit a problem. Each time I reach the step titled “Displaying the Number of Incomplete Todos,” ( http://emberjs.com/guides/getting-started/displaying-the-number-of-incomplete-todos/ ), the counter in the lower left corner (it says “2 items left” in the illustration at page bottom) does NOT update as it is supposed to.

It stays stuck at “2 items left” and shows no sign of life when I click on items in the “ToDo” list. Other aspects of the app are connected to that counter. So the fact that it does not update kills the app’s operation.

I have checked and re-checked the code until my head hurts and have tried twice to build new versions of the app from scratch, very carefully following each step. I have also tried running it on two other Windows machines, with the same discouraging results. Everything works, step by step, until I hit “Displaying the Number of Incomplete Todos,” and there, the counter refuses to budge when I click on items.

Can somebody take a look at the code in that step and in an earlier step or two and see if there might be something obvious that might cause a problem? For example, I’ve seen some vague references on the web to problems involving bindAttr in other apps. And that one is used in the step prior to the step that has stopped me cold. But I’m just guessing. Otherwise, I’m clueless. Has anyone else had this frozen counter problem?

Thanks for any help. I wasted a good holiday “playing” with this.

Sorry that you had issues. Right now the included JSBin on that step isn’t running properly. I’m trying to discover what’s wrong with it. Thanks for brining this to our attention.

It looks like someone updated the version we recommended (RC3) to RC6 but did not update the recommended SHA for EmberData and didn’t test that the application continued to work.

My guess is that the build of ember-data has a breaking change.

The JSBin included with that stop does seem to work properly. (Apparently there was a temporary JSBin bug that prevented it from loading when I last checked.) Have you compared your code to the code provided in the JSBin? Are there any errors in your console locally?

this solve my problem, thank you

I find that I have to use latest ember-data to make this guide work without error in console

I find that I can’t get this guide to work at all. JSbin example suffers the same problems - Uncaught Errors. One day I’m hoping Ember will just work when you follow it’s conventions. Sadly, today is not the day.

I have successfully made the tods page just following the guide step by step . Then I added more than 80 todos by randomly type something in the input text box. Then I click the checkbox to toggle all todos between completed and incompleted. But the it takes 2 minutes to do the rendering. The performance is too bad! It stops my idea about applying emberjs into other projects. Can somebody tell me why the performance is so bad?

The version of js:

DEBUG: Ember.VERSION : 1.0.0-rc.6 DEBUG: Handlebars.VERSION : 1.0.0-rc.4 DEBUG: jQuery.VERSION : 1.9.1

My browser is chrome 25.0.1323.1. My laptop is LATITUDE E6330, with intel core i5, 2.6GHz, memory 4G.