Reputation: 833
Using:
Please have a look at this jsFiddle illustrating the described problem.
I have a list of items that are displayed in a template. The template contain a linkTo helper that let's the controller add an item to the collection and is shown as a text input on the page.
Adding the item to the collection is done by the controller:
App.TodoItem = DS.Model.extend({
title: DS.attr('string', { defaultValue: "unknown" })
});
App.Router.map(function () {
this.resource('todo_items')
});
App.TodoItemsRoute = Em.Route.extend({
model: function () {
return App.TodoItem.find();
}
});
App.TodoItemsController = Em.ArrayController.extend({
addTodoItem: function () {
App.TodoItem.createRecord();
}
});
If I want the new item to be shown is the list, I have to pass params to createRecord
, otherwise the item is not visible. The same behaviour can be reproduced by using Chrome's inspector and then the item can be made visible as follows:
// Open the jsFiddle http://jsfiddle.net/bazzel/BkFYd/ and select 'result(fiddle.jshell.net) in the inspector, then:
var item = App.TodoItem.createRecord();
// Nothing visible yet.
item.set('title', 'Whatever');
// Now the text input appear with the title as its value.
Is this expected behaviour and if so, what am I missing here?
Upvotes: 2
Views: 1273
Reputation: 833
Thank you Ken for answering my question. It indeed feels like a more proper of way of doing this in Ember. However, I still think it's difficult to get the hang of which objects are accessible from where...
Your example inspired me to do a rewrite of my code. I also made some changes to your approach:
store
instance. Instead I define a Store
class. renderTemplate in the TodoItemsNewRoute only needs the outlet key.
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="todo_items">
{{#linkTo 'todo_items.new'}}Add Todo Item{{/linkTo}}
<ul>
{{outlet "addItem"}}
{{#each controller}}
<li>
{{#unless isNew}}
{{title}}
{{/unless}}
</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="todo_items/new">
{{view Ember.TextField valueBinding="title" placeholder="Enter title"}}
window.App = Em.Application.create();
App.TodoItem = DS.Model.extend({
title: DS.attr('string', {
defaultValue: "unknown"
})
});
App.TodoItem.FIXTURES = [{
id: 1,
title: 'Lorem'
}, {
id: 2,
title: 'Ipsum'
}];
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.FixtureAdapter.create()
});
App.Router.map(function() {
this.resource('todo_items', function() {
this.route('new');
});
});
App.IndexRoute = Em.Route.extend({
redirect: function() {
this.transitionTo('todo_items');
}
});
App.TodoItemsRoute = Em.Route.extend({
model: function() {
return App.TodoItem.find();
}
});
App.TodoItemsNewRoute = Em.Route.extend({
model: function() {
return App.TodoItem.createRecord();
},
renderTemplate: function() {
this.render({
outlet: 'addItem'
});
}
});
App.TodoItemsNewView = Em.View.extend({
tagName: 'li'
});
The updated example is on jsFiddle.
Any reviews are welcome.
Upvotes: 0
Reputation: 3745
I took time to redo your example the way i feel things should be done properly with Emberjs. You should rather make sure of transaction and properly define your views and then all your issues get taken care of. So here's how i think you should do this
Define a view for the textfield to capture the value being entered or just bind it to the model property.
Listing items and adding a new item to the list should be done in two different views and should not be mixed together
<script type="text/x-handlebars"> {{outlet}} <div> {{outlet 'addItem'}} </div> </script> <script type="text/x-handlebars" data-template-name="todo_items"> {{#linkTo 'todo_items.new'}}Add Todo Item{{/linkTo}} <ul> {{#each item in controller}} <li> {{#unless item.isNew}} {{item.title}} {{/unless}} </li> {{/each}} </ul> </script>
Define different states for listing items and adding a new one
To benefit from automatic binding of your text field value to the
model property, you need to associate an ObjectController
to the TodoItemsNew
route
window.App = Em.Application.create(); App.TodoItem = DS.Model.extend({ title: DS.attr('string') }); App.TodoItem.FIXTURES = [{ id: 1, title: 'Lorem' }, { id: 2, title: 'Ipsum' }]; App.store = DS.Store.create({ revision: 11, adapter: DS.FixtureAdapter.create() }); App.Router.map(function () { this.resource('todo_items',function(){ this.route('new'); }) }); App.IndexRoute = Em.Route.extend({ redirect: function () { this.transitionTo('todo_items'); } }); App.TodoItemsRoute = Em.Route.extend({ model: function () { return App.TodoItem.find(); } }); App.TodoItemsNewRoute = Em.Route.extend({ transaction: App.store.transaction(), setupController:function(controller) { console.info(controller.toString()); controller.set('content',this.transaction.createRecord(App.TodoItem)); }, renderTemplate: function() { this.render('addItem',{ into:'application', outlet:'addItem', }) }, events: { addItem: function() { this.transaction.commit(); this.transitionTo('todo_items'); } } }); App.TodoItemsController = Em.ArrayController.extend(); App.TodoItemsNewController = Em.ObjectController.extend(); App.TextField = Ember.TextField.extend({ insertNewline: function () { this.get('controller').send('addItem') } });
Here' is a working version of the example on jsfiddle. Hopefully, i helped with this example clarify some of your issues.
Upvotes: 2