Reputation: 3991
GOAL:
Have a hierarchy of nested elements with unlimited depth.
PROBLEM:
I have a class A, which includes an instance of a class B.
Class B has a list of Comment objects. Each Comment may have a list of "child" Comments.
Therefore, we have a tree-like structure with unlimited-depth nesting (e.g. A.B.Comments[1].Comments[0].Comments[5]....).
Class A Editor View calls:
@Html.EditorFor(m=>m.B)
Class B's Editor Template has (simplified):
<table id="B-comments">
@for (int i = 0; i < Model.Comment.ToList().Count(); i++)
{
@Html.EditorFor(m => m.Comment.ToList()[i])
}
</table>
<a id="addCommentToB" href="#">Add Comment</a>
And each Editor Template for the Comment class consists of (simplified):
<tr>
@using (Html.BeginCollectionItem("B.Comment"))
{
<td>
(... STUF ...)
</td>
(...STUF...)
<td>
<table class ="commentReplies">
@for (int i = 0; i < Model.Comment1.ToList().Count(); i++)
{
@Html.EditorFor(m => m.Comment1.ToList()[i])
}
</table>
<a id="addReply" href="#">Add Reply</a>
</td>
}
The <a ...>
links are used to get entry rows via Ajax and append them to the corresponding tables.
When submitting, the binding only works for the first nesting level (A.B.Comments, but not for Comments made on objects in A.B.Comments). The reason, is likely to be due to the @using (Html.BeginCollectionItem("B.Comment"))
line, which is generating the indexes only for 1 level of depth.
I'd be very thankful if anyone could give me a suggestion on how to allow unlimited nesting given these circumstances. Is there a way to dynamically modify the argument in Html.BeginCollectionItem(ARG)?
Cheers
EDIT: The simplified ViewModels, as requested
public partial class A
{
(...)
public int BID{ get; set; }
public virtual B B{ get; set; }
(...)
}
public partial class B
{
(...)
public virtual ICollection<Comment> Comment { get; set; }
}
public partial class Comment
{
(...)
public Nullable<int> ParentID { get; set; }
public virtual ICollection<Comment> Comment1 { get; set; }
public virtual Comment Comment2 { get; set; }
public virtual B B { get; set; }
public Nullable<int> BID { get; set; }
}
ADDENDUM:
I ended up creating a different BeginCollectionItem() Html helper that doesn't keep adding prefixes to the generated html's names and id's.
E.g.:
The way I had, if I had a comment inside a comment, the Html helper would generate tags like this: name="*B.Comment[521cfe57-23aa-42d4-9b63-b7fcdd8799c3].*B.Comment.index"
My new method just generates name="Entity.Comment.index"
which allows the binding to work correctly.
Alberto León gave me the right hint by reminding me that I don't have to actually do an in-depth binding (for instance, bind a comment's comment to its parent and so forth) as capturing the relationship between the comment and the B objects is enough for the binding to work as I wish.
Upvotes: 1
Views: 825
Reputation: 486
You can use ViewData.TemplateInfo.GetFullHtmlFieldName() or ViewData.TemplateInfo.HtmlFieldPrefix to retrieve full path to the current model
Upvotes: 0
Reputation: 2921
You are doing this in the complicated way. Few years ago I designed the Bside social engine that sadly died because no founds.
I found the same problem and it has a very simple solution.
You need to assign a guid to each content and assume each comment as a new content, so you can always search for the guid content. Then you only need to create the interface ICommentable with the property Comments and the methods Add, Delete, Edit
Then you need the A class inherited ICommentable and you need to write the implementation. The same for B.
Then in the business layer you get A and B from the database or your storage way. And search the database for A and B comments. When you get it, you need to search the comments for the comments.
So in the storage way, comments are always comments. Isn't sense about the content item related to the comment.
About the User Interface with ajax, I used jQuery. So don't try to bind please. Treat each comment as independent object. The each add button to add comment, and save, can be reference only the comment. You only need to send to the MVC method the form with the new comment. Don't forget the guid of the parent content item (class A, or B or any Comment object)
Upvotes: 1