mvc paging inside an html form - c#

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?

Related

How to post only selected items from list in MVC

We have a list bound to view as
#model List<DataModels.UseCase>
This view contains html form as
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Count(); i++)
{
#Html.CheckBoxFor(m => Model[i].IsSelected)
//few other controls as
}
<input type="submit" value="Submit Selection" >
}
And In Controller, POST method is like below
[HttpPost]
public ActionResult payment([Bind(Include = "Id,IsSelected// few other properties")] List<UseCase> useCases)
{
// Few business logic
return View();
}
Please note- Just for example I have shown only check box control on form, there are other few controls as well.
Now, in this case for example view contains 10 records but out of 10 only 2 are selected then we need to pass only 2 selected records to POST method and not all 10. This is to reduce overload on POST method.
Can we achieve this type of scenario in any way?
Good question, I might implement this on my projects as well.
I could only think of one way-- using javascript, when form is submitted, delete the other form input fields first then resubmit the form.
First is we need to put the input fields inside a parent div with class input-container, so we could quickly delete all the fields by just deleting the entire div. I also added a class targetCheckbox to your input field so we could attach an event to it;
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Count(); i++)
{
<div class="input-group">
#Html.CheckBoxFor(m => Model[i].IsSelected, new { #class="targetCheckbox" })
//few other controls as
<div class="input-group">
}
<input type="submit" value="Submit Selection" >
}
We'll need to bind an event to to your form. On form submit, we need to identify which targetCheckbox are not checked, then delete the div that contains them. We also need to replace the indexes of the input fields because ASP.NET MVC model binding must start with 0 and should not skip. After all that resubmit the form;
<script>
$(document).ready(function(){
$("form").submit(function(e){
e.preventDefault();
var index = 0;
// loop through all the checkbox
$(".targetCheckbox").each(function(){
if($(this).is(":checked")){
// get the parent
var parent = $(this).closest(".input-container");
// loop through all the input fields inside the parent
var inputFieldsInsideParent = $(parent).find(":input");
// change the index inside the name attribute
$(inputFieldsInsideParent).each(function(){
var name = $(this).attr("name");
var firstBracket = name.IndexOf("[");
var secondBracket = name.IndexOf("]");
if(firstBracket != null && secondBracket != null){
// check if this is a valid input field to replace
var newName = name.substring(0,firstBracket)+index+name.substring(secondBracket);
// result should be IntputFieldName[newIndex].Property
// assign the new name
$(this).attr("name",newName);
}
});
index++;
}else{
// empty the parent
$(this).closest(".input-container").html("");
}
});
// submit the form
$(this).submit();
});
});
</script>

ASP.NET MVC Render Partial on Dropdown List Value Change

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.

POST Form which has a list of objects as a viewModel property (without ajax)

I have a view model which gets displayed in a cshtml view like this:
#model MyViewModel
<form action="...">
#foreach (var item in this.Model.MyList)
{
<input type="checkbox" name="item.Name"/>
}
</form>
Then I have a controller method on the backend:
[HttpPost]
public ActionResult SaveMyViewModel(MyViewModel viewModel)
{
...
When I inspect the viewModel in the controller method while POSTing, it has all null properties. I would expect it to have values in MyList and in there, bools for each item in MyList.
Is this possible without Ajax? I cannot use ajax here.
What must be done to the form in order to properly return an accurate representation of the viewModel back to the server?
I am assuming your button to submit the form is within the <form> element in HTML.
When you are POSTing a List (Array) of an Object from HTML to Controller, you need to add index of each item in the List to the HTML name property.
Additionally, the name property should also have same name as of the Property in the C# code.
Your code should look like below to work properly.
<form action="...">
#{
var i = 0;
}
#foreach (var item in this.Model.MyList)
{ var nameChecked = string.Empty;
if(item.Name != null) nameChecked = “checked”;
<input type="checkbox" name="viewModel[i].Name" #nameChecked />
i++;
}
</form>
NOTE: This is a basic code sample. You can always enhance to work more efficiently.
Hope this helps!

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.

Initiate update from list

I've got a partial view that lists details about my entities. Details include a photo, caption, date, and name. There is an ActionLink for Delete that removes the item from the list.
I would like to make the Caption field an editable textbox to allow the user to edit the contents. Then I wold like to add an 'Update' link that will write this data to the DB. This is the only attribute that can be edited.
Basically I want to avoid the user having to open an 'Edit' form to change this field. I would like to be able to do it from within the list. Not sure how to create the 'Update' ActionLink, send it to the controller, and have the user-entered text available to the controller method.
The model that you pass to the view is of a certain type(of item in db). So, if You change the label with name "Caption" to a textbox of the same name, then the binding would remain the same - the output would be a textbox with the caption taken from the model passed to the View in Controller Action.
As for the update link:
I would create an Action of the same name as the View with [HttpPost] attribute and the model as parameter. The action would be invoked by a submit button in View (so all the labels and the textbox should be enclosed in a form). Inside that action perform the db update.
So:
The View should be something like this:
#model db.Foo
#using (Html.BeginForm()) {
<fieldset>
#Html.LabelFor(model=>model.Name)
(...)
#Html.TextBoxFor(model=>model.Caption)
<input type="submit" value="Update Caption"/>
</fieldset>
}
and the controller actions:
//The action that passes the model to the View
public ActionResult Details()
{
//
//get foo here
//
return View(foo); //Where foo is the item You use for your model
}
[HttpPost]
public ActionResult Details(foo model)
{
//
//Update model here with model of type foo
//
}
Hope this helps
Since it seems to be an extremely basic edit, I would just put the Caption in a Div then using CSS/jQuery display an edit button (glyphicon glyphicon-pencil) on mouseover. On click set the Div to Editable (use styling to show it's in edit mode, and only exit edit mode with a save button). On save, do a jQuery Ajax call to update the value in the database.
Please be extra cautious to not have any SQL Injection Attacks as editable content could contain anything from a user.
CSS might look like:
.editor editor-display button
{
display: none;
}
.editor editor-display:hover .edit-button
{
display: inline-block;
}
.editor editor-edit .edit-button
{
display: none;
}
.editor editor-edit .save-button
{
display: inline-block;
}
Html might look like:
<div class="my-entity editor editor-display" data-id="5" >
<div class="edit-content">#model.Caption</div>
<button id="edit-caption-button" class="edit-button" />
<button id="save-caption-button" class="save-button" />
<div>
Javascript might look like:
$(document).ready(function()
{
$("#save-caption-button").on("click", function()
{
$(this).parent("editor").removeClass("editor-display").addClass("editor-edit");
});
$("#save-caption-button").on("click", function()
{
$.ajax({
url: "/MyEditity/UpdateCaption",
data: { id: $(this).parent(".my-entity").data("id"),
caption: $(this).parent(".my-entity").find(".edit-content").first().text() };
});
$(this).parent("editor").removeClass("editor-edit").addClass("editor-display");
});
});
Controller:
public ActionResult UpdateCaption(int id, string caption)
{
// lookup entity by id
// change caption value
// save
return new EmptyResult();
}
This is a super basic (UNTESTED!) example, with no exception handling.

Categories

Resources