backbone-forms icon indicating copy to clipboard operation
backbone-forms copied to clipboard

Use collections for lists of nested models.

Open mitchellrj opened this issue 12 years ago • 28 comments

Also prevents modal boxes from popping up on form load. Should have been a separate commit, sorry.

mitchellrj avatar Mar 04 '13 16:03 mitchellrj

Could you add tests for both changes?

philfreo avatar Mar 04 '13 16:03 philfreo

Do you have any instructions for running and writing tests? I would have done otherwise. No idea what I'm doing with this "sinon" though.

mitchellrj avatar Mar 04 '13 17:03 mitchellrj

Just run test/index.html in your browser and add qunit tests to the appropriate files (e.g., test/editors/list.js)

It's just QUnit tests - sinon is a little add-on to help with some things. Reading through some of the existing ones should be helpful.

http://qunitjs.com/ http://sinonjs.org/

philfreo avatar Mar 04 '13 17:03 philfreo

Done.

mitchellrj avatar Mar 06 '13 08:03 mitchellrj

Thanks for adding the tests @mitchellrj - just looking into merging but wanted to confirm what the use case is?

I can see why you would pass an instance of a collection in to populate the list (a little like how the Select editor works) but what are the advantages of passing in a collection definition?

powmedia avatar Mar 06 '13 10:03 powmedia

In my particular case, I'm using backbone-relational and have a custom collection type to help integrate with the API on the server side. One might have different collection types depending on the model contained, particularly if using Backbone.Collection.model, or even in particular contexts.

mitchellrj avatar Mar 06 '13 14:03 mitchellrj

Refactored implementation added so that we don't depend on editor type names for behaviour.

mitchellrj avatar Mar 08 '13 09:03 mitchellrj

@mitchellrj I tried a merge earlier today - there was a missing opening curly brace (have added a line comment) which was breaking the script. That wasn't too much of a problem, I fixed that; the main thing was that List.NestedModel was no longer returning a object literal value, so when you do Form.getValue() or Form.commit() it's not receiving the full form value.

There would need to be a way of keeping the original value being returned as before.

Refactoring the code into separate List.Object and List.NestedModel editors was good, and I've done this. I also added a whole bunch of tests for List.Modal to master so if you continue work on this best to rebase off master and use those tests, maybe adding where appropriate.

powmedia avatar Mar 08 '13 17:03 powmedia

Thanks for that. I'm not sure I follow what you mean when you say

List.NestedModel was no longer returning a object literal value, so when you do Form.getValue() or Form.commit() it's not receiving the full form value.

I think I should have made my refactoring clearer, in that the classes are distinguished in the following ways:

  • List.Modal: an abstract base class of List.Object and List.NestedModel.
  • List.Object: an editor which accepts and returns Javascript hashes and requires a schema to be provided.
  • List.NestedModel: an editor which accepts and returns Backbone models and requires a model to be provided.

The updated tests I've just pushed should help clarify the situation.

mitchellrj avatar Mar 08 '13 18:03 mitchellrj

What I mean is that every editor needs to return a simple object from the getValue() method, because when you do form.commit() or form.getValue() it's just going through each editor and combining them.

In your pull request, List.NestedModel#getValue() returns a model, and if the List#getValue() returns a collection when the itemType is NestedModel.

This breaks the way form.getValue() works - you get an empty object ({}) for the list field, instead of an array of objects.

powmedia avatar Mar 08 '13 19:03 powmedia

So the way it's intended to work and indeed does work in my use-case at least is as follows:

I have a model Parent which, using Backbone-relational has an attribute as part of its schema, children which contains a Backbone.Collection of child Backbone.RelationalModel objects.

Feeding this model to a form using a Backbone.Form.List with item editor Backbone.Form.List.NestedModel sets the form values appropriately, and indeed getValue returns and updates the model appropriately, too.

When it comes to saving the model as JSON, the default handlers for Backbone.Collection.toJSON and Backbone.Model.toJSON do all the hard work and give you back the right thing anyway.

I think the case you're talking about requiring an array of Javascript hashes to be input and returned should use the Backbone.Form.List.Object editor. For me, it makes sense that setValue and getValue should always be of the same type. Inputting a Backbone.Model and getting back a hash isn't desirable IMO.

mitchellrj avatar Mar 11 '13 12:03 mitchellrj

Sounds like @mitchellrj is describing what I was in https://github.com/powmedia/backbone-forms/issues/81#issuecomment-6665415

philfreo avatar Mar 11 '13 17:03 philfreo

Ok yeah that all makes sense. I suppose that internally the form should check if the editor is for a nested model or a list of them (collection) and handle them differently.

But I think this is a bigger job than just this pull request as we'll need to work on some of the form internals too.

The form should probably use each child model's save() method rather pulling out the values and sending them through as part of the main model.

Is everyone here using backbone-relational? I tend to just put nested models and collections on the parent directly so will need to look into what backbone-relational actually does. Seems odd that collections contain instances of backbone-relational models as mitchellr points out.

On 11 Ma 2013, at 17:14, Phil Freo [email protected] wrote:

Sounds like @mitchellrj is describing what I was in #81

— Reply to this email directly or view it on GitHub.

powmedia avatar Mar 11 '13 18:03 powmedia

Is everyone here using backbone-relational? I tend to just put nested models and collections on the parent directly so will need to look into what backbone-relational actually does.

I use backbone-relational a lot, though other times I just setup the relations manually. In either case a model attribute is an instance of a Backbone.Model.

Seems odd that collections contain instances of backbone-relational models as mitchellr points out.

Why is this odd?

philfreo avatar Mar 11 '13 18:03 philfreo

The form should probably use each child model's save() method rather pulling out the values and sending them through as part of the main model.

yes this makes sense (in form.commit())

philfreo avatar Mar 11 '13 18:03 philfreo

Seems odd that collections contain instances of backbone-relational models as mitchellr points out. Why is this odd?

  • Only because I'd expect an instance of your app model to be returned from the collection. How does that work - do the backbone-relational models wrap them?

powmedia avatar Mar 11 '13 22:03 powmedia

yes, like this:

var Foo = Backbone.RelationalModel.extend(),
    foo = new Foo();

ok(foo instanceof Foo);
ok(foo instanceof Backbone.RelationalModel);
ok(foo instanceof Backbone.Model);

philfreo avatar Mar 11 '13 22:03 philfreo

@philfreo Ah OK that makes sense

powmedia avatar Mar 12 '13 10:03 powmedia

@mitchellrj All tests are passing for me now but have you tried editing an existing item in a List.NestedModel? I get Object has no method 'get'.

This seems to be because the editors.Base is looking up this.model (for the parent model) but this has been overwritten with the data from the List.NestedModel instance.

powmedia avatar Mar 13 '13 11:03 powmedia

I'm also thinking that when this is all sorted it might make sense to rename the List.NestedModel editor to something like List.RelationalModel to make it clearer that it depends on backbone.relational-model.

I'm working on a different editor for working with collections directly without backbone.relational-model though it will have to be separate from the List editor.

powmedia avatar Mar 13 '13 11:03 powmedia

@powmedia It doesn't actually depend on backbone-relational, it should work with any model where nested models are held in collections. I'm working on implementing some changes to how commit works, as suggested and then I'll add some more tests.

mitchellrj avatar Mar 13 '13 11:03 mitchellrj

Can you outline what changes you're implementing?

powmedia avatar Mar 13 '13 11:03 powmedia

I'm not sure how I'm going to go about it yet. I was going to make Form.commit call the commit methods on each field, which in turn call the commit methods of their respective editors, and for the List editor, make it call the commit for each modal form and so on.

The problem with this approach is that the change event is triggered for each individual attribute which has changed, rather than just once per model. I don't want to change the behaviour or signature of commit, so another approach might be to add a new method called prepareCommit or something similar which returns the attributes & values to change for each model and in the case of the list editor, calls the commit method of each nested form.

I don't particularly like either of these solutions. I need to think about it some more.

mitchellrj avatar Mar 13 '13 12:03 mitchellrj

I think it's because the List editor isn't right for this use case.

I've started some work on a Collection editor which adds, edits and removes items from a collection atomically. I.e. when you edit an item in a collection, it modifies that model directly; when you remove it, it removes it from the collection etc. In a way this separates the collection from the parent model but I think it makes sense to use the collection to it's full potential like this.

powmedia avatar Mar 13 '13 13:03 powmedia

That does sound better.. and so when the modal is closed or in some other way submitted, the data is then committed to those individual items?

Are you working on this completely separately from this pull's branch, or are you basing some of it on what exists here?

mitchellrj avatar Mar 13 '13 14:03 mitchellrj

I've started some work on a Collection editor which adds, edits and removes items from a collection atomically. I.e. when you edit an item in a collection, it modifies that model directly; when you remove it, it removes it from the collection etc. In a way this separates the collection from the parent model but I think it makes sense to use the collection to it's full potential like this.

I do something similar to this already and it would be great to see it standardized. Just need to make sure it works properly in a setting where you might want to "cancel" your changes made to the list.

philfreo avatar Mar 13 '13 14:03 philfreo

I am using backbone relational as well and would find the items mitchellrj and philfreo are introducing to be useful. If not this, is there a preferred way to handle a sub collection of nested models?

ksteigerwald avatar Jul 15 '13 22:07 ksteigerwald

@powmedia Is there any update on your Collection editor?

reustle avatar Feb 15 '14 00:02 reustle