views:

35

answers:

2

I have a view where I want to perform different actions on the items in each row in a table, similar to this (in, say, ~/Views/Thing/Manage.aspx):

<table>
  <% foreach (thing in Model) { %>
    <tr>
      <td><%: thing.x %></td>
      <td>
        <% using (Html.BeginForm("SetEnabled", "Thing")) { %> 
          <%: Html.Hidden("x", thing.x) %>
          <%: Html.Hidden("enable", !thing.Enabled) %>
          <input type="submit"  
                 value="<%: thing.Enabled ? "Disable" : "Enable" %>" />
        <% } %>
      </td>    
      <!-- more tds with similar action forms here, a few per table row -->     
   </tr>
  <% } %>

In my ThingController, I have functions similar to the following:

public ActionResult Manage() {
  return View(ThingService.GetThings());
}

[HttpPost]
public ActionResult SetEnabled(string x, bool enable) {
  try {
    ThingService.SetEnabled(x, enable);
  } catch (Exception ex) {
    ModelState.AddModelError("", ex.Message); // I know this is wrong...
  }
  return RedirectToAction("Manage");
}

In the most part, this is working fine. The problem is that if ThingService.SetEnabled throws an error, I want to be able to display the error at the top of the table. I've tried a few things with Html.ValidationSummary() in the page but I can't get it to work.

Note that I don't want to send the user to a separate page to do this, and I'm trying to do it without using any javascript.

Am I going about displaying my table in the best way? How do I get the errors displayed in the way I want them to? I will end up with perhaps 40 small forms on the page. This approach comes largely from this article, but it doesn't handle the errors in the way I need to.

Any takers?


Solved thanks to @Shaharyar:

public ActionResult Manage() {
  if (TempData["Error"] != null)
    ModelState.AddModelError("", TempData["Error"] as string);
  return View(ThingService.GetThings());
}

[HttpPost]
public ActionResult SetEnabled(string x, bool enable) {
  try {
    ThingService.SetEnabled(x, enable);
  } catch (Exception ex) {
    TempData["Error"] = ex.Message;
  }
  return RedirectToAction("Manage");
}

Then just a small form for the ValidationSummary at the top of my table.

<% using (Html.BeginForm()) { %>
  <%: Html.ValidationSummary(false) %>
<% } %>

Thanks!

A: 

Hey,

Try doing:

 try {
    ThingService.SetEnabled(x, enable);
  } catch (Exception ex) {
    ModelState.AddModelError("", ex.Message); // I know this is wrong...
    return View(); //return to the view to display the error
  }

If you return the same view that's in error, it reloads the view; there are some data items you may need to reload, but returning the view in error the framework should then extract those errors from ModelState and display them.

The most efficient way is to use JQuery to submit the form to the server, so you aren't always reloading the page, and display a message on the client.

HTH.

Brian
There is no SetEnabled view, it is used just for the post. I want to display the Manage view with the error after posting to SetEnabled. Also, I want to do it without javascript.
enashnash
Ok, try returning View("Manage"); as long as there are errors in the model state, it should reload the state of the view...
Brian
A: 

Let's try...

There is a TempData dictionary available for you to do this kind of things.

And you might have to rely on another page to handle the errors for you.

Because an exception will be thrown as soon as the ViewModel can't be passed to the View.

But if the model has some problems, you could do the following (just pass an empty model to the view):

public SetEnabled(string x, bool enable) {
  try {
    ThingService.SetEnabled(x, enable);
    return View(viewModel);
  } catch {
    TempData["GetThingsError"] = "Oops! Some error happened while processing your request!"
    return View(); 
    /*
     * Note that you can also pass the initial model
     * back to the view -> this will do the model validation 
     * (specified in your model) 
     */
  }
  return RedirectToAction("Manage");
}

The TempData message is only available for the current request and will be gone after a refresh.

It might need some further tweaking but that would be the direction to do that kind of error reporting to the user/customer.

Shaharyar
Thank you, that solved it. Ideal that it's just available for the current request. I've added the code that I used to a separate reply.
enashnash
Glad it helped you!You could have also edited your original question and added a new section with the solution. (preferred behavior on StackOverflow) ;-)
Shaharyar