MVC 3 show hide Partial Views in jquery UI tabs - asp.net-mvc

In our application we are using Tabs to display the information for example contacts, in this tab the user can switch between view contacts and created contact. When the user clicks on ‘Create’ I want the partial view changed to CreateContact within the same tab. What is the best way to show hide a Partial Views in jquery UI tabs? Should I use Ajax code to do this?
View
<div id="tabs">
<ul>
<li>Claim</li>
<li>Products</li>
<li>Contact Us</li>
</ul>
</div>
Controller
public ActionResult GetContact()
{
return PartialView();
}
public ActionResult CreateContact()
{
return PartialView();
}
Partial View
<li>#Html.ActionLink("Create", "Test", "Home")</li>
Thanks

Yes, you can use AJAX. That is in case you don't want to load all partial views at once.
If it doesn't matter, you can just render them at once:
<div id="tabs-1">
#Html.Partial("GetClaim")
</div>
<div id="tabs-2">
#Html.Partial("GetProduct")
</div>
<div id="tabs-3">
#Html.Partial("GetClaim")
</div>
jQuery hides the elements that are not currently visible and will manage the switching automatically.

You can use an Ajax.ActionLink() - http://msdn.microsoft.com/en-us/library/dd493106.aspx
This will asynchronously submit your form to your controller method. If you set the AjaxOptions.InsertionMode to be AjaxOptions.InsertionMode.Replace then it will replace the partial view content with whatever you return from the controller. Then, you just need to return the GetContacts view in the creatcontacts controller action, as below:
[HttpGet]
public ActionResult GetContact()
{
return PartialView(RetrieveListOfContacts());
}
[HttpPost]
public ActionResult CreateContact(StronglyTypedContactModel contact)
{
if (ModelState.IsValid)
{
// .. write your contact here
return PartialView("GetContact", RetrieveListOfContacts());
}
else
{
return PartialView(contact);
}
}
EDIT
Also, you can specify that it be a GET request in the HttpMethod of the AjaxOptions, so as not to require form (as I originally assumed you did).

Related

RenderAction() to make decoupled components but how to communicate with other components

I have a busy view where most of the sections on there work off an ID. I'm looking for a more component way to handle each section so I'm using RenderAction() for each section where they have their own controllers. However I have a search section/"component" and when they put in a new Id and submit on that section/"component", I need a way for that to communicate to all the other RenderActions() that new Id so they can do their thing (query DB to get more info specific to that section).
My Search section would be something like:
public class SearchController : Controller
{
[HttpGet]
public ActionResult SearchContract()
{
var vm = new SearchVM();
return PartialView(vm);
}
[HttpPost]
public ActionResult SearchContract(SearchVM Search)
{
return PartialView(Search);
}
}
#using (Html.BeginForm())
{
<div class="row">
<div class="col-md-3">Contract Id</div>
<div class="col-md-6">
#Html.TextBoxFor(m => m.Id, new { #class = "form-control" })
</div>
</div>
<input type="submit" />
}
Let's say ContractHeader is a section/"component" using RenderAction() that hits a different controller and method from the search:
public class ContractController : Controller
{
public ActionResult ContractHeader(int ContractId)
{
// query contracts
return PartialView(vm);
}
}
Again, I'm looking for a more component oriented way with this. Yes it could all be in one controller but that's not what I'm looking for here. I want a more decoupled/compartmentalized approach to these areas on my views but trying to figure out how they can communicate with each other when "events" happen.
I think I have it figured out. Basically on each search "component" (I'm calling components a separate controller and view that you use RenderAction() to get on your main view) the method that gets called when the search button is pressed will return the following code (I subclassed Controller and put tis method in)
public ActionResult RedirectWithQueryString()
{
// get the referrer url without the old query string (which will be the main view)
var uri = new Uri(Request.UrlReferrer.ToString());
var url = Request.UrlReferrer.ToString().Replace(uri.Query, "");
var allQS = System.Web.HttpUtility.ParseQueryString(uri.Query);
var currentQS = System.Web.HttpUtility.ParseQueryString(Request.Url.Query);
var combinedQS = new NameValueCollection();
// update existing values
foreach (var key in allQS.AllKeys)
{
combinedQS.Add(key, allQS[key]);
}
// add new values
foreach (var key in currentQS.AllKeys)
{
if (combinedQS.AllKeys.Contains(key))
combinedQS[key] = currentQS[key];
else
combinedQS.Add(key, currentQS[key]);
}
var finalUrl = url + combinedQS.ToQueryString();
return Redirect(finalUrl);
}
public class ContractSearchController : MyBaseController
{
// GET: ContractSearch
public ActionResult Index(ContractSearchVM model)
{
return PartialView("ContractSearch", model);
}
public ActionResult SearchContracts(ContractSearchVM model)
{
return RedirectWithQueryString();
}
}
public class StopsSearchController : MyBaseController
{
public ActionResult Index(StopsSearchVM model)
{
// query to get some search related reference data like states list for drop down
return PartialView("StopsSearch", model);
}
public ActionResult SearchStops(StopsSearchVM model)
{
return RedirectWithQueryString();
}
}
SearchContracts() and SearchStops() methods are called from their own forms in their own views using HttpGet. In those methods then we are provided with just that forms query string but we also can get the UrlReferrer query string which will have other search forms key/values in it. So RedirectWithQueryString() basically makes sure the final query string has ALL keys required to satisfy the model binding of any search components on the view and will update the given keys with the current value for the current search component that the submit button was on.
So this then causes us to refresh to the current view with all current key/values in query string for all search components which then is calling all the RenderActions() and the values can be passed.
#model FocusFridayComponents.Models.CombinedVM
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="col-md-12">
#{ Html.RenderAction("Index", "ContractSearch"); }
</div>
</div>
<div class="row">
<div class="col-md-12">
#{ Html.RenderAction("Index", "StopsSearch"); }
</div>
</div>
</div>
<div class="col-md-6">
<!-- Contract Header -->
<div class="row">
<div class="col-md-12">
#{ Html.RenderAction("Header", "ContractHeader", new { ContractId = Model.ContractSearch.Id }); }
</div>
</div>
<!-- Contract Routes -->
<div class="row">
<div class="col-md-12">
#* #{ Html.RenderAction("Index", "ContractRoutes", new { ContractId = Model.Id }); } *#
</div>
</div>
</div>
In the main view you're working on you just make a VM that combines the search VM's you're using on the view. The model binding will correctly map the query string keys that match the search VM's even when they are inside the combined VM. The catch here would be to make sure the keys/props of each search VM don't share the same names of any kind.
What's interesting is for the RenderAction() for contract and stops search I don't need to pass the model into it. The binding just does this automatically. For ContractHeader and ContractRoutes I am passing in a parameter because the idea is those are separate components and have their own input requirements and those can be named completely separate from any search models you may be using in your view so the binding wouldn't be able to map anything. This is a good thing though as it decouples your actual view components from your search components.
So you would do all of this to get components that are decoupled from each other but can still talk to each other and you can assemble your views and reuse a lot of these components by just gluing the RenderAction() parameters between them. This can help reduce giant monolithic VM's that tend to pop up on complex views you're making.

Adding a dropdown list outside or RenderBody call

I have a .NET MVC project with Razor views and I would like to implement search functionality that consists of a dropdown list, a text box and a search button.
The problem is that I would like to implement this search snippet in my _Layout.cshtml file outside of the #RenderBody() call. This means that the search functionality would be accessible on every page (it would be located at the very top right corner).
I'm trying to find out what is a good way of implementing this. I can get it to work but it would involve adding same code (do get dropdown values) to all controllers and actions.
ViewBag.States = new SelectList(db.States, "Id", "Name");
Is there a better way to implement this? It feels very repetitive to do it this way.
You can have a child action method which returns the partial view needed for your header and call this action method in your layout.
Create a view model for the properties needed.
public class AllPageVm
{
public int SelectedItem { set; get; }
public List<SelectListItem> Items { set; get; }
}
Now create an action method in any of your controller. Mark this action method with ChildActionOnly decorator.
public class HomeController : Controller
{
[ChildActionOnly]
public ActionResult HeaderSearch()
{
var vm = new AllPageVm()
{
Items = db.States
.Select(a => new SelectListItem() {Value = a.Id.ToString(),
Text = a.Name})
.ToList()
};
return PartialView(vm);
}
Now in the HeaderSearch.cshtml partial view, you can render whatever markup you want for your search header. Here is a simple example to render the dropdown. You may update this part to include whatever markup you want (Ex : a form tag which has textbox, dropdown and the button etc)
#model AllPageVm
<div>
<label>Select one state</label>
#Html.DropDownListFor(a => a.SelectedItem, Model.Items, "Select")
</div>
Now in your layout, you can call this child action method
<div class="container body-content">
#Html.Action("HeaderSearch", "Home")
#RenderBody()
<hr/>
<footer>
<p>© #DateTime.Now.Year - My ASP.NET Application</p>
</footer>
</div>
Make sure you are calling PartialView method from the HeaderSearch child action method instead of View method. If you call View method, it will recursively call the same method and you will get a StackOverflow exception

Passing html id to and mvc controller

I'm doing an game news website and I have a problem in the code. I need to the client to press a news button, choosing one of many news, and the button id, which is the news id, need to go to a controller method, then the controller will take that news and show in another view.
Here is the view code:
<div class="panel panel-default">
<div class="panel-heading">News List</div>
<table>
#foreach (var a in Model)
{
<tr>#Html.ActionLink("Read More", "Post", "Home", new { id = #a.Id }, null)</tr>
}
</table>
And here is the controller method, wich is in the HomeController:
public ActionResult Post(string id)
{
Post post = this.post.ChosePost(id);
return View(post);
}
This should call a view with the html view on the previous link but it's not, and I do change the mapRoute but isn't working at all, the value don't go to controller.

MVC Entity Framework Razor Issue

I have to following code and there's a problem and I can't figure out what's the problem. Well, I have two ideas about the place where the problem may appear but I can't find a solution.
The problem is that my layout is rendered twice.
Layout.cshtml
<div id="container">
<div id="left_side"> #RenderPage("left_side.cshtml") </div>
<div id="center_side"> #RenderBody() </div>
<div id="right_side"> #RenderPage("right_side.cshtml") </div>
</div>
left_side.cshtml
#if (ViewBag.LeftColumnVisible == true)
{
#Html.Action("GetCategories", "Products");
}
GetCategories method from Products controller
public ActionResult GetCategories()
{
List<Categories> categories = db.Categories.ToList();
...
return View();
}
GetCategories.cshtml
#foreach (System.Collections.DictionaryEntry de in ViewBag.LeftColumnContent)
{
<div> #((Ads.Models.Categories)(de.Key)).Name; </div>
}
It enters the get categories and renders the content.
The problem for rendering twice may be at this line
#Html.Action("GetCategories", "Products"); or when it calls View(). If I comment the line, my layout will be rendered only once.
I'd say there are a couple of issues with your code. First of all, if left_side.cshtml/right_side.cshtml are partial views then you want to be using
#Html.RenderPartial("view")
to render them and not #RenderPage - although #RenderPage will work it's better from a readability point of view to understand exactly what the type of view it is you are working with.
Secondly, if your GetCategories view is a partial view you want to be returning a PartialView and not a View i.e.
public ActionResult GetCategories()
{
...
return PartialView();
}

ASP.NET MVC RenderAction re-renders whole page

How do I prevent renderaction from rendering the masterpage and giving it back to me? I only want it to render 1 section, for eg.
The controller
public ActionResult PaymentOptions()
{
return View(settingService.GetPaymentBanks().ToList());
}
The PaymentOptions View:
#model IEnumerable<Econo.Domain.PaymentBank>
<h2>Payments</h2>
<!-- Stuff here -->
The View
<div class="grid_10">
</div>
<div class="grid_14">
#{Html.RenderAction("PaymentOptions", "Administrator");}
</div>
In grid_14, the header, footer and everything else gets rendered. Is there a way to prevent this?
public ActionResult PaymentOptions()
{
return PartialView(settingService.GetPaymentBanks().ToList());
}
In Razor, partial views and full views have the same extension, so you need to explicitly use the PartialViewResult result type to specify a partial view.
This:
return View(settingService.GetPaymentBanks().ToList());
Has to use the overload so you can specify a master:
return View("PaymentOptions", "", settingService.GetPaymentBanks().ToList());

Resources