5

I'm very new to MVC and I'm trying to figure out if there is a better way to do this. I have a textbox for the user to put in their search, then based on that search I am displaying some results below said search box. I am trying to avoid having so much code logic in my view and would like to know if there is a better way of handling this. Here is my existing code, where based on what the value of "Model.Results" is it will return one of 3 partial views or a button if the rest of my logic passes:

@section CustomerPrefixInfo
{
    @if (Model.Results == PrefixSearch.SearchResults.CustomerFound)
    {
        @Html.Partial("_CustomerPrefixInfo")
    }
    @if (Model.Results == PrefixSearch.SearchResults.PrefixResultsFound)
    {
        @Html.Partial("_PrefixResults")
    }
    @if (Model.Results == PrefixSearch.SearchResults.AnimalsFound)
    {
        @Html.Partial("_AnimalSearchResults")
    }
    @if (Model.Results == PrefixSearch.SearchResults.ValidNewPrefix)
    {
        using (Html.BeginForm("Index", "PrefixManagement", new { prefix = Model.AnimalPrefix.Prefix, dbPrefix = Model.AnimalPrefix.DbPrefix }))
        {
            <fieldset>
                <input id="btnReservePrefix" type="submit" value="Reserve Prefix" />
            </fieldset>
        }
    }
}

I would like to put this inside a controller so that it just returns the view that is to be displayed, then just display that view on the page. Aftering doing some rearch I thought using Ajax.BeginForm with the InsertionMode set to InsertAfter would do the trick:

@using (Ajax.BeginForm("GenericSearch", "Home", FormMethod.Post, new AjaxOptions { InsertionMode = InsertionMode.InsertAfter, UpdateTargetId = "searchResults" }))
{
    <fieldset>
        <input id="btnPrefixSearch" type="submit" value="Prefix Search/Validate"/>
        @Html.EditorFor(model => model.Input)
    </fieldset>
    <div id="searchResults">

    </div>
}

My GenericSearch Action then uses a switch to decide which partial view to return:

public ActionResult GenericSearch(PrefixSearch prefixSearch)
{
    //some database logic here to get the results
    switch (prefixSearch.Results)
    {
        case PrefixSearch.SearchResults.CustomerFound:
            return PartialView("_CustomerPrefixInfo", prefixSearch);
        case PrefixSearch.SearchResults.PrefixResultsFound:
            return PartialView("_PrefixResults", prefixSearch);
        case PrefixSearch.SearchResults.AnimalsFound:
            return PartialView("_AnimalSearchResults", prefixSearch);
        default:
            return null;
     }
}

But when I tried this it puts the partial view on a new page.

here is one of my partial views (they are all 3 mostly identical to this)

@model MVC_Test_Project.Models.PrefixSearch

@{
    ViewBag.Title = "PrefixResults";
 }

@{
    Layout = null;
 }
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.PrefixResults[0].Prefix)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.PrefixResults[0].CustomerCount)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.PrefixResults[0].Link)
        </th>
    </tr>

@foreach (var item in Model.PrefixResults)
{
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Prefix)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.CustomerCount)
        </td>
        <td>
            <a href="@item.Link">edit</a>               
        </td>
    </tr>
}
 </table>

Any help would be appreciated!

Edit Just a helpful hint in case anybody makes the same stupid mistake I did, make sure your bundles are called before your scripts.

@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryval")
@Scripts.Render("~/bundles/bootstrap")
<script src="/Scripts/jquery.unobtrusive-ajax.js"></script>
<script src="/Scripts/jquery.unobtrusive-ajax.min.js"></script>

I had added in those last 2 lines when it was mentioned to use those, but they were above my bundles....and thus the ajax didn't work because of it. Thanks for everybodies help, all is well now! --Joseph

jpaugh78
  • 99
  • 2
  • 10
  • can you show your partial view code, it should have on top ``@{ Layout=null;}`` which is used to tell, do not use any master page for it – Ehsan Sajjad May 11 '16 at 19:34
  • I just added the @{ Layout=null; } to my partial views and it didn't seem to change the result. I updated my post to show one of my views. – jpaugh78 May 11 '16 at 19:47
  • Are you looking to just return the results inside one table on the page? Is the partial view you're returning just appending to the current results? If this is the case, you want the `InsertionMode.Replace`, which just re-renders the `searchResults` div – Geoff James May 11 '16 at 19:54
  • Ensure that you have the `[HttpPost]` attribute above the Action method in your controller. Also, if you are only "searching" for results, and not actually "posting" anything, I would recommend a `GET` request (no need for an attribute above the Action method), and change the `FormMethod` to `Get`, or try settings the `HttpMethod` in your `AjaxOptions` to `"GET"` – Geoff James May 11 '16 at 20:05
  • Just to be clear what is happening when i try the Ajax: It gets the results and puts them on a blank page, rather than adding it to the existing page. I tried Geoff's suggestions and got the same result. I think I'm just not understanding how to return data into a section of my already existing page. – jpaugh78 May 11 '16 at 20:12
  • Possible duplicate of [How to use Simple Ajax Beginform in Asp.net MVC 4?](http://stackoverflow.com/questions/17095443/how-to-use-simple-ajax-beginform-in-asp-net-mvc-4) --> make sure you load the unobtrusive js files... – Ziv Weissman May 11 '16 at 21:10
  • I've added an answer that solves your Ajax issue. Please see the answer below - not entirely sure _why_ it's been downvoted, though -.- – Geoff James May 11 '16 at 22:40
  • 1
    @jpaugh78 why use ajax for this. It doesn't seem to solve your specific problem. It's cool technology and I do what your attempting to do very often, but for your specific needs it appears to be overkill. – Erik Philips May 11 '16 at 22:44
  • @erik phillips I'm not married to using ajax. I'm just trying to figure out a good way to push content into my current view into a particular div – jpaugh78 May 11 '16 at 22:50
  • @jpaugh78 Do you want it to be dynamic without reloading of the page or does the whole page have to reload each time? – Erik Philips May 11 '16 at 22:51
  • @eric phillips for this particular use it would be dynamic without reloading the rest of the page. – jpaugh78 May 11 '16 at 22:53
  • @Eric phillips Thanks for the help! I hadn't thought of just saving the partial view to a variable. Not 100% sure what you were doing with the Enums though, but I'll bookmark that link and try to figure out later. Thanks again! – jpaugh78 May 12 '16 at 00:30

2 Answers2

2

Make sure you are including jQuery and jquery.unobtrusive-ajax.js (or jquery.unobtrusive-ajax.min.js) at the top of your page you want to perform the Ajax on.

Like this:

<script src="~/scripts/jquery-1.x.x.js" />
<script src="~/scripts/jquery.unobtrusive-ajax.js" />

...
The rest of your page down here
...

This allows the Partial's postback to be rendered in-page, rather than re-directing you each time.

If you haven't got the jQuery files, or are unsure how to include them; have a read here: http://www.c-sharpcorner.com/UploadFile/4fcb5a/update-a-div-and-partial-view-using-ajax-beginform-on-form-s/ - it's very useful.

Ensure that Unobtrusive JS is enabled, in your web.config file, also. They look like this in the <appsettings> node:

<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>

If you are using validation - which I doubt you are for a search; but stay with me - you may also want to include jquery.validate.js and jquery.validate.unobtrusive.js, too. This can allow client-side validation (where possible) making the UX a lot smoother.

NOTE: There are other ways of doing the Ajax request; such as creating it in your own JS script - this example is the easiest using an already-available and much-loved library :)

Hope this helps!

EDIT
The switch on which Partial view to return can then be done solely from inside your Controller. You can remove the logic from within the page view.

The Ajax will then take care of replacing the content of the div's ID you gave to the Ajax.BeginForm helper.

You could either replace the search form entirely - by using its ID, or, better still - place an empty <div> container within the page, ready to display the results in:

<div id="searchResults"></div>

The Ajax will then return the Partial view and put the content inside your searchResults div.

EDIT 2

I noticed in your Ajax.BeginForm's AjaxOptions object you are missing HttpMethod = "POST".

The AjaxOptions should look something like this:

new AjaxOptions {
            HttpMethod = "POST",
            UpdateTargetId = "searchResults",
            InsertionMode = InsertionMode.Replace
}
Geoff James
  • 3,122
  • 1
  • 17
  • 36
  • 1
    This doesn't answer the question about how to switch the logic. Regardless of how the html is rendered, he still wants the logic out of the view. – Erik Philips May 11 '16 at 22:40
  • If you read the post correctly, he has put the logic in the Action's code and that decides which partial view to return... – Geoff James May 11 '16 at 22:41
  • I've edited my answer to clarify where the logic can be and how to then display this on the page from returning the partial view – Geoff James May 11 '16 at 22:55
  • I did some tinkering and almost got the Ajax to work, but for some reason even though I'm getting data back (I can see data is returned while debugging), it's not showing up in my div. I don't see any errors when I inspect the web page, so I'm not 100% sure what is going on. I did get Erik Philips's version to work, but I would really like to figure out how to use the Ajax because it seems it could really come in handy. – jpaugh78 May 12 '16 at 00:25
  • 1
    Good to hear you're getting somewhere :) There's one thing that I always use within the AjaxOptions object properties which you don't have; that's `HttpMethod = "POST" `. Try adding this and let me know how you get on - _don't confuse this with the already existing `FormMethod.Post`; you can leave that in..._ – Geoff James May 12 '16 at 00:34
  • I think it may have been the order I had my bundles and scripts. I had the scripts calling the unobtrusive-ajax above the bundles, so the ajax wasn't loaded before the scripts were called. Ugh. – jpaugh78 May 12 '16 at 00:52
  • 1
    No way! Easy mistake to make. Anywho, I've expanded on my last comment in an edit of my answer just for clarity. Best of luck! :) – Geoff James May 12 '16 at 00:56
2

I would like to put this inside a controller so that it just returns the view

Seems pretty straight forward:

ViewModel:

public class MyModel
{
  public string PageToRender { get; set; }
}

Controller Action

public ActionResult DoLogic()
{
  //var Results = ?? as The ENum

  switch(Results)
  {
    PrefixSearch.SearchResults.CustomerFound:
      model.PageToRender = "_CustomerPrefixInfo";
    // etc etc
  }    

  return View(model);
}

View:

 @{if (!string.IsNullOrEmpty(model.PageToRender))
   Html.RenderPartial(model.PageToRender);}

Although I would probably decorate the Enum:

public enum SearchResults
{
  [Display(Name="_CustomerPrefixInfo")]
  CustomerFound
}

And then there is no switch statement, you just grab the enum value display attribute's name value.

Community
  • 1
  • 1
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
  • I explained my downvote on [your answer with a comment](http://stackoverflow.com/questions/37171541/mvc-partial-views-issue/37174403#comment61883535_37174226). I would assume my answers downvote from you seems to be hypocritical based on [your comment](http://stackoverflow.com/questions/37171541/mvc-partial-views-issue/37174403#comment61883504_37174403). – Erik Philips May 11 '16 at 22:46
  • I've edited my answer to include where the logic can be - he's already mentioned that he'd put the logic within the Controller; so I assumed that this was the way he was going forward, and needed to know how to parse the results into a div on the page - sorry if you got a little offended – Geoff James May 11 '16 at 22:54