ASP.NET MVC Render Partial on Dropdown List Value Change - c#

I'm trying to render a partial view dynamically when the user selects a value from the dropdown list. I've gotten quite far but can't figure out this last bit.
Here's the View/Quiz/AddQuestion.cshtml file
#model quizAppCodeFirst.ViewModels.QuestionTypes
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div>
Type #Html.DropDownList("QuestionTypes", ViewData["QuestionTypes"] as SelectList, new { #class = "form-control" })
<div id="partialPlaceHolder"> </div>
</div>
}
<script type="text/javascript" src="~/Scripts/jquery-3.3.1.min.js"></script>
<script type="Text/javascript">
$(function(){
$("#QuestionTypes").change(function(){
var stateId=$(this).val();
$("#partialPlaceHolder").load("#Url.Action("MyAction", "Quiz")/MCQ");
});
});
</script>
In the QuizController.cs, this is the MyAction method that renders the partials
public ActionResult MyAction(string id)
{
Type t = Type.GetType("quizAppCodeFirst.Quiz" + id);
return PartialView(viewName, t);
}
Till now, I've succeeded in rendering the partial view, but when I click the button inside the partial, it reloads the javascript / MyAction method.
Edit: To make it clear, I have main view which is View/Quiz/AddQuestions.cshtml and within it I am calling a partial view which is View/MCQ/AddMCQ.cshtml when the user selects MCQ from the dropdown list. The partial view (AddMCQ) loads perfectly fine and displays a form to add an MCQ with a Create button, but when I click the Create button, instead of calling the MCQController.cs/AddMCQ function, it calls the QuizController.cs/MyAction function. I can add the code for AddMCQ.cshtml but I feel like it's unnecessary since it works fine on its own.

Related

Nested Partial View

On our website the client can add different items into the footer columns, ie a text box. We wanted to be able to keep the code for these different items in one partial view, as the items are used in other areas of the site and want to keep code duplication to a minimum.
I was wondering if it possible to nest a Partial View within a foreach statement and then pass an if statement based on the parent foreach.
I have the following code in a partial view called Three_Column_Footer:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
var root = Model.Content.Site();
var thefooter = root.FirstChild("footer").Children();
}
<footer class="footer color-main">
<div class="container">
<div class="row">
#foreach(var foot in thefooter){
var childcol = foot.Children;
<div class="col-6">
#foreach(var item in childcol){
#Html.Partial("Footers/FooterStubs", item)
}
</div>
}
</div>
</div>
</footer>
I need someway of getting the above foreach to be recognised in the Partial called FooterStubs. Can this be done with an #inherits Umbraco.Web.Mvc.UmbracoViewPage<IPublishedContent> or a Model.GetPropertyValue<IEnumerable<IPublishedContent>> - if so how?
Thanks!
The Partial method has an overload that accepts additionalViewData. You can utilize this to provide any additional context you need the partial to have:
#Html.Partial("Footers/FooterStubs", item, new ViewDataDictionary(ViewData)
{
{ "foot": foot }
});
Then in your partial:
#{
var foot = ViewData["foot"] as YourFootClass;
}
I'm not sure what info you actually need in the partial, but you can essentially add whatever you like in this way. Just bear in mind that ViewData is a dynamic dictionary, so you'll have to type whatever you get out of it.

mvc paging inside an html form

I'm trying to create paging inside html form.
How do I make each page button change the model's CurrentPage property?
I'm new to MVC, so bear with me please.
I have a list of reports that I need to display in a view named Search. I made a model for that (ReportViewModel), and another model that holds a list of those, named SearchViewModel. In SearchViewModel I have some other variables like SearchName, SearchDate, CurrentPage, PageCount, PageSize, etc. The goal is to do paging and searching in one action named Search in ReportsController.
Now, I would like to keep that action simple by giving that function only SearchViewModel instance as a parameter, so that inside I use all variables of that model. Doing that, I won't have to type parameter names twice (once in SearchViewModel and once as parameters in Search action). I will not need that search function elsewhere, so it's ok for it to not have any parameters other than SearchViewModel instance.
So, very loosely, it looks something like this:
ReportsController.cs
public class ReportsController : Controller
{
public ActionResult Search(SearchViewModel model)
{
if (!ModelState.IsValid)
return View(model);
// do search magic here
return View(model)
}
}
Search.cshtml
#model ISFinReports.Models.SearchViewModel
#{
ViewBag.Title = "List";
Layout = "~/Views/_masterLayout.cshtml";
}
<link href="~/CSS/Search.css" rel="stylesheet" />
<script src="~/Scripts/Search.js"></script>
#using (Html.BeginForm("Search", "Reports", FormMethod.Get))
{
<div id="divSearchBar">
// add #Html.DropDownListFor, #Html.TextBoxFor and submit button here
</div>
<div id="divReportsTable">
<table id="tblReports">
#if (Model.Reports != null)
{
foreach (var r in Model.Reports)
{
// add tr, td of all reports in report list
}
}
</table>
</div>
<div style="text-align:center">
#for (int i = 0; i < Model.PageCount; i++)
{
#Html.ActionLink(i.ToString(), "Search", "Reports", new { CurrentPage = i });
// or
// <input type="submit" value="#i" onClick="meybe something like this?" />
}
Current Page #Html.EditorFor(q => q.CurrentPage), of total #Model.PageCount pages
</div>
}
I know that I can simply type twice all the data I need to flow between controller and view, once in model and once as Search action parameters, but that's extactly what I'm trying not to do. Currently I have a textbox for editing the current page, and it works, because it's created via #Html.EditorFor, but I'd like to have multiple buttons for each page.
I could maybe create a hidden field in that form, and create a javascript function that'll find that hidden field by id, and change it's value, and then call whatever the submit button onClick function is. BUT, is there a better/shorter/more-MVC way to implement that?

Render part of page on dropdown selection

I have a chart on my main view which I would like to update partially when a dropdown selects different values.
The page renders correctly the first time, but when I select a new value in the dropdown, then "-PnlChart.cshtml" replace the entire page. I want just the chart portion to be updated.
How to I ensure that?
_PnlChart.cshtml
<img src="#Url.Action("CreateTraderPnlChart3")" width="600" height="600" align="middle" vspace="50" />
My mainview Index.cshtml:
<div class="w3-half">
#{ Html.RenderAction("PeriodSelection");}
#Html.DropDownList("PeriodSelection",
new SelectList((string[])Session["Periods"]),
(string)Session["Period"],
new
{
onchange = "document.location.href = '/Employee/PeriodSelection?dropdownlistReturnValue=' + this.options[this.selectedIndex].value;"
})
</div>
My controller:
public ActionResult PeriodSelection(string dropdownlistReturnValue) // dont know what dropdownlistReturnValue is doing?
{
Session["Period"] = dropdownlistReturnValue;
return PartialView("~/Views/Employee/_PnlChart.cshtml");
}

Implement back button in ASP.NET MVC

I am facing issue implementing a back button feature in a ASP.NET MVC application.
This is my Home page View, for simplicity let us consider it has a div which has img element and div that renders PartialView2. PartialView2 is a partial view which generates list of tiles based on the data from the Model.
View1:
<div> remainig page content </div>
<div>
<img src="/path/to/img_back" id="img" />
<div id="1">
Html.RenderAction("PartialView2", "Home");
</div>
</div>
<div> remainig page content </div>
PartialView2:
#model ICollection<Model>
#{
foreach(item in Model) {
<div class="tile">
<!-- tiles data generated using model data that I get from controller -->
</div>
}
}
HomeController:
//field
private Stack<ICollection<Model>> myStack = new Stack<ICollection<Model>>();
public PartialViewResult GetTiles(string id) {
var nodes = new List<Model>();
//make a call to database and generating the list of child nodes of type Model.
// get the rows from the db which have parentid as the passed value.
//for back button: myStack.push(nodes);
return PartialView("PartialView2",nodes);
}
//Method to call from jQuery on click of #img
public PartialViewResult BackButton() {
return PartialView("PartialView2",myStack.Pop());
}
When I click on a tile, I make a call to GetTiles method in the home controller through Jquery Ajax and populate the $("#1").html() with the result returned from the ajax call which is the same partial view.
When I click on #img I call BackButton() method from jQuery ajax to to load html of #1 div in the page.
Model:
string icon;
string title;
string parentid;
string id;
bool hasChildren;
With this design I only have the options to traverse the children and represent them as tiles but I need to implement a back button so as to traverse the parents. I have created a stack in the home controller and pushed the elements to the stack for each call to the GetTiles() method. But for every call to the controller, a new stack is instantiated. So my idea to have collection of models in the stack and on each call to back button pop the collection of Model and return it to the view is not working.
Can you guys please help me solve this issue? Any in-memory data structures to use to hold the parent models?
Please let me know if I need to provide further information.

Need help to finish async form post and screen update using jquery

I'm pretty much a jquery newb...I've almost got this working I think, let me know if i can clarify anything.
I have a screen displaying a list..let's call them affiliates. To create a new affiliate, a modal style pop up dialogue is used.
When the dialogue "Create" button is clicked, the form data must be saved (creating a new affiliate), the dialogue disappears, and the affiliate list updates without reloading the page
The jquery file at the bottom shows how I'm trying to do it now: trying to detect a click on the "confirm" button, get the form to submit using the data target property, and using the form's target property to know what container to update.
What is happening is: nothing. The cancel button works, create button does absolutely nothing.
Also note that the "Create" button, which will act as the form's submit, is not located within the <form> tags.
I'm pretty sure I'm doing modals wrong but let's ignore that for the moment and focus on the async post and list update. I've included my relevant code below to support my post.
--AffiliateListPartial
#model IPagedList<Acme.Business.DomainModel.Affiliates.Affiliate>
<div class="items-list" id="affiliate-list-view">
#foreach (var item in Model)
{
<a href="#Url.Action("AffiliateDetails", "Vendor", new { id = item.AffiliateId })">
//basic spans and razor display list in here..nothing notable
</a>
}
</div>
The above partial view is contained within a full view, lets call it AffiliateList. Nothing particularly relevant in there except that it is controlled by the VendorController/Affiliatelist method.
The VendorController.AffiliateList looks like:
public ActionResult AffiliateList(string searchTerm = null, int page = 1)
{
var userId = WebSecurity.GetUserId(User.Identity.Name);
var model = (from a in db.Affiliates.ToList()
where a.VendorId == userId
select a).ToPagedList(page, 15);
if(Request.IsAjaxRequest()) return PartialView("_AffiliateListPartial", model);
return View(model);
}
The modal style dialoque for creating a new affiliate (I'll just include the lines that I think are relevant):
_Modal.AffiliateCreate.Partial
<form id="affiliate-create-form" class="form" method="post" action="#Url.Action("AffiliateCreate")" data-acme-ajax="true" data-acme-target="#affiliate-list-view">
// multiple input elements
</form>
<div class="modal-footer">
<button name="close_modal"><span>Cancel</span></button>
<button name="confirm" data-acme-target="#affiliate-create-form"><span>Create</span></button>
</div>
And the VendorController.AffiliateCreate method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AffiliateCreate(Affiliate affiliate)
{
if (!ModelState.IsValid) return View(affiliate);
db.Affiliates.Add(affiliate);
db.SaveChanges();
return RedirectToAction("AffiliateList");
}
And the .js file's relevant parts:
$("button[name='confirm']").on("click", function() {
var $form = $(this).attr("data-acme-target");
var options = {
url: $form.attr("action"),
type: $form.attr("type"),
data: $form.serialize()
};
$.ajax(options).done(function (data) {
var $target = $($form.attr("data-acme-target"));
var $newHtml = $(data);
$target.replaceWith(data);
$newHtml.effect("highlight");
});
$(".modal_overlay").css("opacity", "0");
$(".modal_container").css("display", "none");
return false;
});
$("button[name='close_modal']").on("click", function(event) {
$(".modal_overlay").css("opacity", "0");
$(".modal_container").css("display", "none");
return false;
});
var $form = $(this).attr("data-acme-target"); is getting the attribute named 'data-acme-target' of the button, rather than the form it's associated with. So then when you're using $form.attr('action'), you aren't getting anything back.
Since data-acme-target is an ID to another control that is the form you want to submit, use $($(this).attr("data-acme-target")); to get it.

Categories

Resources