I have an object that looks like this:
{
"id": "someId",
"name": "some name",
"endpoints": [
{ "scheme": "http", "host":"localhost", "port":"8080" }
]
}
I want to edit this data in a Marionette app. I've defined:
My goal is to have a form that allows N number of endpoints to be added such that when the form is submitted, I will persist the entire object (the server data and the list of endpoints).
To implement this I first overrode the parse function to instantiate the Endpoints collection like this:
parse: function(response) {
var data = response;
if (response.endpoints) {
data.endpoints = new Entities.EndpointCollection(response.endpoints, {parse: true});
}
return data;
},
Then, I created a LayoutView that holds my server related fields and contains a region for my list of endpoints. My Controller instantiates the view and passes in the Server model. My Controller also instantiates a CollectionView and passes in the Server's endpoints collection.
The form renders as expected with input controls for id, name, and repeating sets of inputs for as many endpoints as I have in my object.
However, it is not clear to me how serialization is supposed to work with this setup. If I remove an endpoint by destroying its model, the endpoint is removed when the data is persisted. However, if I change any of the endpoint data, those changes are ignored.
In my form submit handler I can iterate over the endpoints but those models do not reflect the changed form values.
submitClicked: function (e) {
e.preventDefault();
var data = Backbone.Syphon.serialize(this); // I know this is not enough
var endpoints = this.model.get("endpoints");
for (var i = 0; i < endpoints.length; i++) {
var endpoint = endpoints.models[i];
alert("got endpoint: " + endpoint.get("host"));
}
this.trigger("form:submit", data);
},
I assume there is a fundamental misunderstanding in how best to handle repeating groups within a form. What am I missing?
My thinking had become uptight. The layout view really isn't the issue. The key is that the form needs to have IDs/names that are unique. To do this, I first send the item's index to the view:
Views.Endpoint = Marionette.ItemView.extend({
template: endpointTpl,
triggers: {
"click button.btn-remove": "endpoint:remove"
},
initialize: function(options) {
this.model.set("idx", options.childIndex);
}
});
Views.Endpoints = Marionette.CollectionView.extend({
childView: Views.Endpoint,
childViewOptions: function(model, index) {
return {
childIndex: index
}
}
});
Then, in the view's template I set up the IDs and names using that index property:
<tr>
<td><label for="endpoints[<%- idx %>][scheme]" class="control-label">Scheme:</label></td>
<td><input id="endpoints[<%- idx %>][scheme]" name="endpoints[<%- idx %>][scheme]" type="text" value="<%= scheme %>"></input></td>
<td></td>
</tr>
And, finally, on submit, Syphon will create an object for my repeating group, when what I really wanted was an array, so I address that with:
submitClicked: function (e) {
e.preventDefault();
var data = Backbone.Syphon.serialize(this);
// need to convert endpoints object to array
var endpoints = data.endpoints;
delete data["endpoints"];
data.endpoints = [];
for (var idx in endpoints) {
data.endpoints.push(endpoints[idx]);
}
this.trigger("form:submit", data);
}
Now I can add/remove/edit endpoints as needed and they save back to the object appropriately.