0

EDIT reformed my question based on @Stephen Muecke answer ..

it took me months trying to resolve such problem, but failed to.

my entities are:

public partial class Book
{
    public int bookID { get; set; }
    public string name { get; set; }
    public string chapter { get; set; }
    public virtual ICollection<Root> roots { get; set; }
}

.

public class Root
{
    public int rootID { get; set; }
    public string rooting { get; set; }
    public virtual ICollection<Book> Book{ get; set; }
}

BooksController:

public class BooksController : Controller
{
    private Context db = new Context();

    // GET: Books
    public ActionResult Index()
    {
        var boo = db.books.Include(x => x.roots).ToList();
        List<RootyVM> model = new List<RootyVM>();
        return View(boo);
    }
....
....
// GET: Books/Create
    public ActionResult Create()
    {
        return View();
    }
    // POST: Books/Create
 [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(IEnumerable<RootyVM> rootings, Book book)
    {
        if (ModelState.IsValid)
        {
            db.books.Add(book);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(book);
    }

my view model RootyVM:

public class RootyVM
{
    public int? rootID { get; set; }

    [Required(ErrorMessage = "Please enter the name of the root!!")]
    public string rooting { get; set; }

}

and my partial view _Rooting.cshtml

@model project.ViewModels.RootyVM
<div class="rooting">
@using (Html.BeginCollectionItem("rooting"))
{
    @Html.HiddenFor(m => m.rootID, new { @class = "rootID" })
    @Html.LabelFor(m => m.rooting)
    @Html.TextBoxFor(m => m.rooting)

    <button type="button" class="delete">Delete</button>
}
</div>

and my Razor view (Create.cshtml) as follows:

@model project.Models.Book

@{
ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm()) 
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
    <h4>Book</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    <div class="form-group">
        @Html.LabelFor(model => model.name, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.name, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.name, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.chapter, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.chapter, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.chapter, "", new { @class = "text-danger" })
        </div>
    </div>
    <div class="form-group">
        @using (Html.BeginForm())
        {
            <div id="rootings">
                foreach(var rooting in Model)
                {
                @Html.Partial("_Rooting", rooting)
                }
            </div>
            <button id="add" type="button">Add</button>
            <input type="submit" value="Save" />
        }
    </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>


<div>
@Html.ActionLink("Back to List", "Index")
</div>

<script type="text/javascript">
var url = '@Url.Action("Rooting")';
var form = $('form');
var rootings = $('#rootings');
$('#add').click(function() {
    $.get(url, function(response) {
        rootings.append(response);
        // Reparse the validator for client side validation
        form.data('validator', null);
        $.validator.unobtrusive.parse(form);
    });
});

$('.delete').click(function() {
    var container = $(this).closest('.rooting');
    var id = container.find('.id').val();
    if (id) {
        // make ajax post to delete item
        $.post(yourDeleteUrl, { id: id }, function(result) {
            container.remove();
        }.fail(function (result) {
            // Oops, something went wrong (display error message?)
        }
    } else {
        // It never existed, so just remove the container
        container.remove();
    }
    });
</script>
}

HOWEVER, there is a mistake where i cannot find. Will appreciate your patience and help

ORIGINAL MAIN REQUESTS

however, I'm struggling in Create and Edit method. What I need to do is that:

  • Create a new book record and assign a root or more in the same view.
  • using dropdownlist is preferable.
  • if root doesn't exist, i want to add/create it on the fly, I mean immediately in the same create view.
  • I would appreciate if the Create view for relationship property i.e. Root is based on java where token are used (Select2).

I hope I made it clear for you to help me.

M.g.
  • 1
  • 3
  • `Roots` is a complex object, so if your want to edit your collection of existing `Root`, the you need to create an `EditorTemplate` for it. But if your wanting to dynamically add and remove items from your collection, refer [this answer](http://stackoverflow.com/questions/40539321/a-partial-view-passing-a-collection-using-the-html-begincollectionitem-helper/40541892#40541892) –  Mar 30 '17 at 22:21
  • if i opt to go with dynamically add and remove collection, how to tailor that to my model? – M.g. Mar 30 '17 at 22:54
  • Read the link I gave you. –  Mar 30 '17 at 22:55
  • sir, would you please revisit @StephenMuecke – M.g. Mar 31 '17 at 23:44
  • Sorry, your edits make no sense and are not like the link I gave you. For a start you have nested forms which is invalid and not supported. Then you have `foreach(var rooting in Model)` which makes no sense since the model is a single `Book` (I assume you mean `@foreach(var rooting in Model.roots) { ...`). Then your collection property is named `roots` not `rooting` so it needs to be `@using (Html.BeginCollectionItem("roots"))` and you POST method needs to be just `public ActionResult Create(Book book)` since `Book` already contains a collection of `Root`. –  Mar 31 '17 at 23:54
  • thank you for your continuance support. i changed all codes in your last comment, but received {System.NullReferenceException: 'Object reference not set to an instance of an object.' System.Web.Mvc.WebViewPage.Model.get returned null.} after `@foreach(var rooting in Model.roots)` – M.g. Apr 01 '17 at 00:06
  • Because you not passing a model to the view in the `Create()` GET method. Initialise your model (but you should be using a view model for `BookVM` as per the link I gave you) and then initialize its collection of `roots` and pass that model to the view so that its not `null` –  Apr 01 '17 at 00:08
  • @StephenMuecke sorry to bother you a lot. i'm little bit confuse. i want to add to the mapping table the relation between `Book` & `Root` every time i add a new `Root`. now you are tilling me to add `BookVM`? i did it and came out with error The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[project.ViewModels.BookyVM]', but this dictionary requires a model item of type 'project.Models.Book'. // GET: Books/Create public ActionResult Create() { List model = new List(); return View(model);} – M.g. Apr 01 '17 at 00:40
  • You need to go back and carefully study the code in the link I gave you. There is no point taking a working solution and then completely changing it and expect that ot will still work. You should have a view model (say) `BookVM` that contains a property `RootVM` and you pass `BookVM` to the view which will have `@model BookVM`. And the error message is telling you exactly what your doing wrong - your passing a collection of `BookyVM` (not clear why since you only want to edit one) to a view which has `@model Book` - they are not the same thing –  Apr 01 '17 at 00:56
  • thanks again. i'll do that – M.g. Apr 01 '17 at 01:05
  • @StephenMuecke i'm still confused. please treat me as new to BeginCollectionItem. how to make 'BookVM' contains 'RootVM'? – M.g. Apr 20 '17 at 18:45
  • Create a class named `BookVM` (which will contain a property `IEnumerable Rootings` along with other properties of `Book` that you want to edit. Then the model in your view is `BookVM` and the loop is `foreach(var rooting in Model.Rootings) { ...` and in the POST method its ` public ActionResult Create(BookVM model). But you have invalid html anyway - remove the `@using (Html.BeginForm())` in your loop - nested forms are invalid and not supported. **AGAIN - Read the link I gave you.** –  Apr 24 '17 at 07:29

0 Answers0