sproutcore / guides

The guides source. Build and push to https://github.com/sproutcore-guides/sproutcore-guides.github.com.
http://guides.sproutcore.com/
110 stars 65 forks source link

Getting Started with HTML based apps bug #64

Closed robert-stuttaford closed 13 years ago

robert-stuttaford commented 13 years ago

http://guides.sproutcore.com/html_based.html

The todoController is given a createTodo method in section 3, but upon setting the contentBinding and testing it, the following error occurs:

Uncaught TypeError: Object SC.ArrayController:sc144 has no method 'create'

I fixed it by renaming the createTodo method on the todoController to 'create'.

Please advise or if this fix is adequate, update the guide so no one else struggles!

gmoeck commented 13 years ago

It looks like this error is likely on your side. Are you sure that you copied exactly the code in the guide? In any case, you definitely don't want to overwrite the create method on the array controller. Could you double check your code and get back to us?

robert-stuttaford commented 13 years ago

I'm pretty sure about it. I triple checked before I submitted the bug report.

The tut says to put a 'createTodo' method in the controller, but it doesn't work as described. When I rename this method to 'create', it works as expected.

On 2 May 2011 17:18, gmoeck < reply@reply.github.com>wrote:

It looks like this error is likely on your side. Are you sure that you copied exactly the code in the guide? In any case, you definitely don't want to overwrite the create method on the array controller. Could you double check your code and get back to us?

Reply to this email directly or view it on GitHub: https://github.com/sproutcore/sproutguides/issues/64#comment_1088363

gmoeck commented 13 years ago

I'll give it a run through and let you know what I find then.

gmoeck commented 13 years ago

When I'm following the guide, I don't run into the same problem. Could you maybe create a repo, and push your code for when your getting the error so I can try it directly?

wagenet commented 13 years ago

It seems possible that part of our description about where to put code is unclear and this may be causing problems.

robert-stuttaford commented 13 years ago

The code below is what I ended up with after following the instructions of the guide - that is to say, I typed all this code by reading it from the guide.

I have sproutcore 1.5.0 as well as 1.4.5 installed. 1.5.0 is the active gem.

I doubt this warning has anything to do with it, but perhaps you'd be served well by seeing it even so:

$ sc-server
/Library/Ruby/Gems/1.8/gems/sproutcore-1.5.0/lib/sproutcore/models/target.rb:659: warning: don't put space before argument parentheses
SproutCore v1.5.0 Development Server
Starting server at http://0.0.0.0:4020 in debug mode
To quit sc-server, press Control-C
>> Thin web server (v1.2.8 codename Black Keys)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:4020, CTRL+C to stop

When I run this example in Chrome/Mac and enter a todo and press enter, I get the following error in the console:

Uncaught TypeError: Object SC.ArrayController:sc145 has no method 'create'
Todos.CreateTodoView.SC.TemplateView.extend.insertNewlinetodos.js:40
SC.Object.tryToPerformjavascript.js:8734
SC.TextFieldSupport.keyUpjavascript.js:3327
SC.Object.tryToPerformjavascript.js:8734
SC.Pane.SC.View.extend.sendEventjavascript.js:6847
(anonymous function)javascript.js:9236
SC.runjavascript.js:14042
SC.RootResponder.SC.Object.extend.sendEventjavascript.js:9230
SC.RootResponder.SC.Object.extend.keyupjavascript.js:10338
SC.mixin.handlejavascript.js:4284
listenerjavascript.js:4441

When I rename the 'createTodo' method to 'create', the error goes away and the expected behaviour occurs.

todos/apps/todos/todos.js:

// ==========================================================================
// Project:   Todos
// Copyright: ©2011 My Company, Inc.
// ==========================================================================
/*globals Todos */

Todos = SC.Application.create();

Todos.Todo = SC.Object.extend({
  title: null,
  isDone: false
});

Todos.todoListController = SC.ArrayController.create({
  content: [],
  createTodo: function ( title ) {
    var todo = Todos.Todo.create({ title: title });
    this.pushObject( todo );
  },
  remaining: function () {
    return this.filterProperty('isDone', false).get('length');
  }.property('@each.isDone'),
  clearCompletedTodos: function () {
    this.filterProperty('isDone', true).forEach(this.removeObject, this);
  },
  allAreDone: function ( key, value ) {
    if (value !== undefined) {
      this.setEach('isDone', value);
      return value;
    } else {
      return this.get('length') && this.everyProperty('isDone', true);
    }
  }.property('@each.isDone')
});

Todos.CreateTodoView = SC.TemplateView.extend(SC.TextFieldSupport, {
  insertNewline: function () {
    var value = this.get('value');
    if ( value ) {
      Todos.todoListController.create( value );
      this.set('value','');
    }
  }
});

Todos.CheckboxView = SC.TemplateView.extend(SC.CheckboxSupport, {
  valueBinding: '.parentView.content.isDone'
});

Todos.TodoListView = SC.TemplateCollectionView.extend({
  contentBinding: 'Todos.todoListController'
});

Todos.StatsView = SC.TemplateView.extend({
  remainingBinding: 'Todos.todoListController.remaining',
  displayRemaining: function() {
    var remaining = this.get('remaining');
    return remaining + (remaining === 1 ? " item" : " items");
  }.property('remaining').cacheable()
});

Todos.ClearCompletedView = SC.TemplateView.extend({
  mouseUp: function () {
    Todos.todoListController.clearCompletedTodos();
  }
});

Todos.MarkAllDoneView = SC.TemplateView.extend(SC.CheckboxSupport, {
  valueBinding: 'Todos.todoListController.allAreDone'
});

SC.ready(function() {
  Todos.mainPane = SC.TemplatePane.append({
    layerId: 'todos',
    templateName: 'todos'
  });
});

todos/apps/todos/resources/templates/todos.handlebars:

<h1>Todos</h1>

{{#view Todos.CreateTodoView}}
    <input id="new-todo" type="text" placeholder="What needs to be done?" />
{{/view}}

{{#view Todos.StatsView id="stats"}}
    {{#view Todos.ClearCompletedView}}
        <button>Clear Completed Todos</button>
    {{/view}}
    {{displayRemaining}} remaining.
{{/view}}

{{#collection Todos.TodoListView itemClassBinding="content.isDone"}}
    <label>
        {{#view Todos.CheckboxView}}
            <input type="checkbox" class="check" />
        {{/view}}
        {{content.title}}
    </label>
{{/collection}}

{{#view Todos.MarkAllDoneView}}
  <label>
    <input class="mark-all-done" type="checkbox" /> Mark All as Done
  </label>
{{/view}}
gmoeck commented 13 years ago

The issue is that in your CreateTodoView your calling Todos.todoListController.create instead of Todos.todoListController.createTodo. This is then trying to create another SC.ArrayController, but you acutally want to send the createTodo message to the instantiated controller. You likely just had a typo, or misread the guide :)

I'm going to close the issue, but if you still have problems let us know and I can reopen it.

robert-stuttaford commented 13 years ago

Gosh, now I feel like a fool! For some reason I had this idea that the issue was somewhere inside some auto generated code. I should slow down and sleep more.

Thanks for your patience and attention, and sorry for the idiocy!

wagenet commented 13 years ago

No worries, happens to the best of us :)

gmoeck commented 13 years ago

It's happened to me plenty of times, no worries :)