0

What I want to do

I am very new to MVC. I'm trying to create a page that allows users to perform the following actions on the same page:

  • View the list (table)
  • Add a new item (Filling the form and clicking the Add button should update the table)
  • Delete an item from the list (Clicking the Delete button in a row should update the table)

A simple example looks like this but I actually have two lists on one page (Fees and Costs): enter image description here

Question

What would be the best way to achieve this? Should I go with Dylan Beattie's method posted here which would look something like this?

public ActionResult MyAction(string submitButton, MyViewModel form)
{
    switch (submitButton)
    {
        case "AddFee":
            return (AddFee(form));
        case "AddCost":
            return (AddCost(form));
        case "RemoveFee":
            return (RemoveFee(form));
        case "RemoveCost":
            return (RemoveCost(form));
    }
}

public ActionResult AddFee(MyViewModel form)
{
    Fee newFee = ....; // Get entered data from `form`
    _repository.InsertFee(newFee);
    return View("Create"); //Back to the original page
}

Or is there any other recommended methods to handle this such as using JavaScript?

Community
  • 1
  • 1
kabichan
  • 1,063
  • 4
  • 19
  • 49
  • Using ajax to add and delete items will give you the best performance, and avoid having to submit and then redirect back to the same page. However if you do not want to use ajax, then you should have separate forms for adding and deleting that post back to separate actions (not one common method). –  Jun 06 '15 at 03:10

2 Answers2

1

You could create the table as a partial view and re render this via ajax.

Wrap the partial view in a div and Wrap the form in @using (Ajax.BeginForm(.... and target the wrapper div. Your controller action that is targeted by the ajax request will need to return a partial view.

Here is a simple example

public class HomeController : Controller
{
    public ActionResult Index()
    {
        MYvm vm = new MYvm() { id = 1, name = "This is my View Model" };

        return View(vm);
    }

    public ActionResult DA(MYvm vm)
    {
        vm.name = "CHANGED";
        return PartialView("Part", vm);
    }

View:

@model MvcApplication1.Controllers.HomeController.MYvm
@{
    ViewBag.Title = "Home Page";
}

@using (Ajax.BeginForm("DA", "Home", new AjaxOptions() { UpdateTargetId =  "cont", HttpMethod = "Get"  }))
{

<div>
    Id: @Html.EditorFor(model => model.id)
</div>
<div>
    Name: @Html.EditorFor(model => model.name)
</div>

<input type="submit" value="SubmitForm" />
}

<div id="cont">

    @{Html.RenderPartial("part", Model);} 
</div>

Partial View

@model MvcApplication1.Controllers.HomeController.MYvm

@{
     ViewBag.Title = "part";
}

<h2>part</h2>

@Model.name
Brad Black
  • 46
  • 1
  • Sorry, I'm really new to this so.. is the `Delete` action handled in the Partial View and is the `Add` action handled in DA? – kabichan Jun 08 '15 at 15:53
1

Should I go with [previous SO answer]

No. That answer was for a different scenario where the question had a form with two submit buttons that wanted to do two different actions (and wasn't even the accepted answer to that question).

Your sample screenshot indicates that some javascript/jquery and ajax would solve the issue cleanly.

As you're new to MVC, try to keep it relatively simple. Break up the page into separate parts:

  • the containing page
  • the edit form
  • the list with remove

the edit/list work independently and should be written in a way that they could be put on any other page - the page is just there to contain them and doesn't do much else (obviously your real page will contain more, but add those parts as separate components as well).

1 Create actions for your list and edit forms that return partialviews - just the parts that are needed for that view (self-contained)

controller:

[HttpGet]
public ActionResult AddCost()
{
    var model = new Cost();
    return PartialView(model);
}

[HttpPost]
public void AddCost(Cost model)
{
    if (ModelState.IsValid) {
        db.SaveCost(model);...
    }
}

form Views/Home/AddCost.cshtml:

@using (Ajax.BeginForm(... 
{
    <div class='editor-label'>@Html.LabelFor(model=>model.Description)</div>
    ...etc...
}

I'll leave you to set the Ajax.BeginForm properties. But make sure the on-success calls reloadCostList() (see below)

controller

public ActionResult CostList() 
{
    var model = db.loadCosts(); ...
    return PartialView(model);
} 

list, Views/Home/CostList.cshtml

@model IEnumerable<ViewModels.Cost>
<table>
    <thead>
       <tr>
           <th>Cost Description</th>
           ...
    <tbody>
        @foreach (var cost in Model.Costs) 
        {
            <tr data-id='@cost.Id'>
                <td>@Html.DisplayFor(x=>cost.Description)</td>
                ...
                <td><a href='#' class='remove-button'>Remove</a></td>
        }
        ...

2 Create an action + view for the main page with placeholder for the form and calls the list partial-action, eg:

<div id="body">
    <div id="formWrapper">
        @Html.Action("AddCost")
    </div>
    <div id="listWrapper">
        @Html.Action("ListView")
    </div>
</div>

if you already load the data for the page, you can pass it directly to the partial, but there's no need:

@Html.Partial("ListView", Model.Costs)

this allows you to refresh the list via an ajax call, something like:

function reloadCostList() {
    $(".listWrapper").load("Home/CostList");
}

(ideally, $.ajax and add some fancy UI to indicate loading)

3 Add a remove action to your controller

[HttpPost]
public void RemoveCost(int id)
{
}

4 Wire up the Remove link

$(function() {
    $(".remove-button").click(function() {
        var id = $(this).closest("tr").attr("id");
        $.post("/Home/RemoveCost/" + id, null, function() {
            $(".listWrapper").load("Home/CostList");
            // or reloadCostList(); from above
            // or:
            //$(".listWrapper tr[id=" + id + "]").hide();
        });
    });
} 

rather than re-load the entire list, you could just remove the row (add some fancy UI like fade-out...)

freedomn-m
  • 27,664
  • 8
  • 35
  • 57
  • Thank you for your detailed explanation! I will try this! – kabichan Jun 09 '15 at 15:55
  • For `@Html.Action("Edit")`, the `Edit` is a method in the controller, right? Should it return a partial view? If so, how do I set it so that it knows which partial view to use? – kabichan Jun 09 '15 at 18:48
  • Sorry - typo - `@Html.Action("AddCost")`. When you use `return PartialView(model)` it will find a view (.cshtml) with the same name as the action method. You can specify a different name with `return PartialView("viewname", model)`. – freedomn-m Jun 10 '15 at 08:17