Showing partial-view, only after search has been submitted Asp.Net MVC - c#

I am new to Asp.Net Mvc. I couldn't find a solution that worked for me here, if I am blind just redirect me.
I am trying to make a web-app where i can search through clients, without displaying the entire table of clients. Only after the user presses search, the search result should show as a partial view. I understand that using Ajax is the most popular way of handling something like this.
Any pointers on how to accomplish this?
My first thought was to just make a display: block/none script connected to the submit button but the page updates each time you search rendering this idea useless. That's why i could use some help with how to asynchronously update the web page with the search result.
HomeController:
using testForAutofill.Models;
//Search Functionality
[HttpPost]
public PartialViewResult Index(string searchTerm)
{
test_Db_Context db = test_Db_Context();
List<ViewNewOrderSum> orderSums;
if (string.IsNullOrEmpty(searchTerm))//Fix this.
{
orderSums = db.ViewNewOrderSum.ToList();
}
else
{
orderSums = db.ViewNewOrderSum.Where(x =>
x.ClientName.Equals(searchTerm)).ToList();
}
return PartialView(orderSums);
}
Index View:
#model IEnumerable<testForAutofill.Models.ViewNewOrderSum>
#using (Html.BeginForm())
{
<b>Kundenavn:</b>
#Html.TextBox("searchTerm", null, new { id = "txtSearch" })
<input type="submit" value="🔍 Search" class="btn btn-primary" id="btn-search" />
}
<div id="posts-wrapper"></div>
<div class="client-div" runat="server" style="max-width: 20rem;">
<div class="card-header">Header</div>
<div class="card-body" id="client-Card">
<h4 class="card-title">Client info</h4>
<table id="client-table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ClientName)
</th>
</tr>
#foreach (var item in Model)
{
#Html.Partial("_OrderSum", item)
}
</table>
</div>
</div>
Partial View:
#model testForAutofill.Models.ViewNewOrderSum
<tr>
<td>
#Html.DisplayFor(modelItem => Model.ClientName)
</td>
</tr>

No need of using Ajax. You can submit search text in Form Post. Fetch your data and filter based on your searchTerm retun to View with model. If your model is not null or empty show table else do not display table.
Checkout the below code :
View :
#model List<testForAutofill.Models.ViewNewOrderSum>
#using (Html.BeginForm()) {
<b>Kundenavn:</b>
#Html.TextBox("searchTerm", null, new { id = "txtSearch" })
<input type="submit" value="🔍 Search" class="btn btn-primary" id="btn-search" />
}
#if (Model != null && Model.Count() > 0) {
<div class="client-div" runat="server" style="max-width: 20rem;">
<div class="card-header">Header</div>
<div class="card-body" id="client-Card">
<h4 class="card-title">Client info</h4>
<table id="client-table">
<tr>
<th>
ClientName
</th>
</tr>
#foreach (var item in Model) {
#Html.Partial("_OrderSum", item)
}
</table>
</div>
</div>
}
Controller :
public ActionResult Index()
{
//if you want to load all the clients by default
test_Db_Context db = test_Db_Context();
List<ViewNewOrderSum> orderSums;
orderSums = db.ViewNewOrderSum.ToList();
return View(orderSums);
}
[HttpPost]
public ActionResult Index(string searchTerm) {
test_Db_Context db = test_Db_Context();
List<ViewNewOrderSum> orderSums;
if (!string.IsNullOrEmpty(searchTerm))
{
orderSums = db.ViewNewOrderSum.Where(x =>
x.ClientName.Equals(searchTerm)).ToList();
}
return View(result);
}

My first thought was to just make a display: block/none script
connected to the submit button but the page updates each time you
search rendering this idea useless.
You can prevent the page from updating using something like the following (using jQuery):
<script type="text/javascript">
$('form').submit(function (evt) {
evt.preventDefault();
... your code
});
</script>
Then you can make your ajax POST call, get the data, unhide table headers and append the html results from your partial view.

Related

Why is my model and its variables not passing from View to Method inside of my Controller?

My application searches for the files that contains a string the user is looking for. So far it does that perfectly. The last thing I need to do is to export it to an excel file so I added a method inside of my controller that is called by the Result page after I press a button.
The results are stored in a List of type Result which is a class with four variables.
The method ExportToExcel currently returns string so that I can test if the list of results is null. Every single time it has come out as "No Data", therefore it is null. It perfectly prints out a table with the information in the Results page but does not have the information when i want to export it.
Why is my model not passing from view to method?
At first I wanted to pass my model so that I can access the information in the List but now I am wondering if it would be better to save the List data in the controller so that I can directly pass it to my method.
Either way would be fine and I am open to any other ways to do this.
Model
namespace Final.Models
{
public class InputModel:Result
{
public List<Result> Results { get; set; }
}
}
Controller
This controller is just showing how I am passing the InputModel between the views and method. Maybe I am doing something wrong here?
public ActionResult Index()
{
var input = new InputModel();
return View(input);
}
[HttpPost]
public ActionResult Index(InputModel input)
{
//Finds files that contain string.
//send model to Result
return View("Result", input);
}
public ActionResult Result(InputModel input)
{
return View(input);
}
[HttpPost]
public string Result(InputModel input,string export)
{
return ExportToExcel(input);
}
public string ExportToExcel(InputModel input)
{
if (input.Results!=null)
{
//Run excel code here
return "Excel Exported";
}
else
{
return "No Data";
}
}
View for Result
This is part of the view, not the whole thing. I didn't think the full view was necessary but I posted it in the bottom just in case.
#foreach(var result in Model.Results)
{
<tr>
//Return graph of information received
</tr>
}
</table>
<form action="Find/Result" method="POST">
<input type="submit" value="Export" name="export" class="btn btn-default">
</form>
Output
Occurs after pressing the "Export" Button
"No Data"
This is my first MVC applications so once again please let me know if there is any other area I can improve in.
Full View For Result
Changed the form to enclose the entire view as suggested by Wubbly but I get the same output.
#model Final.Models.InputModel
#{
ViewBag.Title = "Result";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<br />
<h4>Result</h4>
<hr />
#using (Html.BeginForm("Result", "Find", FormMethod.Post))
{
<p>The <b>#Model.SelectedText</b> files that contain <b>"#Model.Find"</b> are: </p>
<div>
<table class="table table-bordered table-responsive table-hover">
<tr>
//HEADERS
</tr>
#foreach (var result in Model.Results)
{
// int i = 1;
<tr>
<td>#result.SelectedText</td>
<td>#result.FileName</td>
<td>#result.Line</td>
<td>#result.LineCode</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 ">
<input type="submit" value="Export" name="export" class="btn btn-default">
</div>
</div>
</div>
}
<p>
#Html.ActionLink("Back to List", "Index")
</p>
Use for loop with hidden property which takes your property value to model.
#using (Html.BeginForm("Result", "Find", FormMethod.Post))
{
<p>The <b>#Model.SelectedText</b> files that contain <b>"#Model.Find"</b> are: </p>
<div>
<table class="table table-bordered table-responsive table-hover">
<tr>
//HEADERS
</tr>
#for (int i = 0; i < Model.Results.Count; i++)
{
// int i = 1;
<tr>
<td>
#Html.HiddenFor(r => r.Model.Results[i].SelectedText)
#Html.HiddenFor(r => r.Model.Results[i].FileName)
#Html.HiddenFor(r => r.Model.Results[i].Line)
#Html.HiddenFor(r => r.Model.Results[i].LineCode)
#Html.DisplayFor(r => r.Model.Results[i].SelectedText)
</td>
<td>#Html.DisplayFor(r => r.Model.Results[i].FileName)</td>
<td>#Html.DisplayFor(r => r.Model.Results[i].Line)</td>
<td>#Html.DisplayFor(r => r.Model.Results[i].LineCode)</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 ">
<input type="submit" value="Export" name="export" class="btn btn-default">
</div>
</div>
</div>
}
<p>
#Html.ActionLink("Back to List", "Index")
</p>
To anyone who might need an answer and is in a similar situation I figured my problem out. Many of you might not think it is the right way to fix it but it is what worked for me. Either way any feedback would be appreciated to improve my abilities.
First, I did change my foreach to a for loop as recommended by StephenMuecke and ShailendraKumar.
The way I transferred the data from my HTTPGet to my HTTPPostis with TempData. I used it to store my model with the user's input inside my HTTPPost Index and called it in my HTTPPost Result.
Here's how I changed my controller.
public ActionResult Index()
{
var input = new InputModel();
input.Type = input.FillType(input.Type);
return View(input);
}
[HttpPost]
public ActionResult Index(InputModel input)
{
input.FileType = input.ValueConvert();
input.FileFind();
TempData["model"] = input
return View("Result", input);
}
public ActionResult Result(InputModel input)
{
return View(input);
}
[HttpPost]
public void Result()
{
InputModel model = new InputModel();
model = (InputModel)TempData["model"];
model.ExportToExcel();
}

Selecting one object from IEnumerable by clicking on a button

I'm creating a tool for editing/disabling Active Directory user accounts. I will search for a username and it will come up with all matches ("jsm" will come up with a table with "John Smith", "James Smoth", and "Jack Smuth"). Beside the user names are buttons "Edit" and "Disable". When the I click "Edit" or "Disable", a bootstrap modal will appear with textboxes containing the account properties such as SamAccountName and DisplayName inside of form inputs.
Because there could be multiple results, I'm passing in an IEnumerable into the view, then foreach through the #Model to put the data into the table. My next step is trying to pass in just that specific User object into the modal. So if the I click on the Edit button beside John Smith, I want his User object to populate the modal.
My first thought is doing a variable of User I can pass the User object from the foreach loop into then use that object in the modal, but I'm not sure how to go about doing that. Can/should I keep this inside the razor view or should I be passing this data into the controller then back out?
Controller:
namespace ADM.Controllers
{
public class ManagementController : Controller
{
public new IActionResult User()
{
var user = new List<User>();
return View(user);
}
[HttpPost]
public new IActionResult User(string username)
{
var user = new User();
var result = user.Get(username);
return View(result);
}
public IActionResult Group()
{
return View();
}
}
}
User.cshtml:
#model IEnumerable<User>
#{
ViewData["Title"] = "User Management";
var count = 1;
}
<div class="container col-6">
<div class="card card-square">
<div class="card-body">
<form asp-controller="Management" asp-action="User">
<div class="form-row form-inline">
<input type="text" class="form-control col-10" name="username" placeholder="Username" />
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
</div>
<br />
#if (Model.Any())
{
<div class="small">
<table class="table table-hover table-sm">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Display Name</th>
<th scope="col">Description</th>
<th scope="col">SamAccountName</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
#foreach (var user in Model)
{
<tr>
<th scope="row">#(count++)</th>
<td>#user.DisplayName</td>
<td>#user.Description</td>
<td>#user.SamAccountName</td>
<td>
<button id="userEditBtn" class="btn btn-sm btn-info" data-toggle="modal" data-target="#userEditModal">Edit</button>
<button class="btn btn-sm btn-danger" data-toggle="modal" data-target="#userDisableModal">Disable</button>
</td>
</tr>
}
</tbody>
</table>
</div>
}
There's more than one way to do this.
If there isn't a lot of User fields, you can store the properties in the DOM for each User button
#foreach(var user in Model)
{
<button class="btnEdit" data-username="#user.Username">Edit</button>
}
In your JavaScript button handler you extract the data to populate a form submission or make an AJAX request.
$(".btnEdit").on("click", function(e) {
var form = $("#myform");
var username = $(this).data("username");
// populate a form
$("input[name='username']").val(username);
form.submit();
});
Another way is to make a AJAX call for the user you want to edit to obtain more data than what you first loaded with the initial collection. Then use the second call to populate the edit form. One quick way is to just load a html partial.
$(".btnEdit").on("click", function(e) {
var username = $(this).data("username");
var action = $(this).data("action"); // EditDetailsForm
$.ajax({
url: action,
method: "get",
data: { username = username }
})
.then(function(partialView) {
$("#editForm").html(partialView);
});
});
And the action
[HttpGet]
public ActionResult EditDetailsForm(string username)
{
var user = GetUser(username);
return PartialView("_editForm", user);
}
Returns a partial view _editForm.cshtml
#Model User
<form ...>
...
</form>
Which you just drop into your modal.

MVC model post with partial shows list as null

I'm going round in circles now so if anyone can identify the issue here I would greatly appreciate it.
I have a partial I'm using to list out items and that works fine.
The post back to the controller works if the items passed back is just set as an ienumerable of the items but I need to pass back a model as it contains more information than just the list.
On doing this the list is empty each time and I cannot see why.
The partial:
#model RequestModel
#section Scripts {
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet">
<style>
.btn span.glyphicon {
opacity: 0;
}
.btn.active span.glyphicon {
opacity: 1;
}
</style>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
}
#for (var i = 0; i < Model.Requests.Count(); i++)
{
<div #(Model.Requests.Count == 3 ? "class=col-md-4" : Model.Requests.Count() == 2 ? "class=col-md-6" : "class=col-md-12")>
#Html.HiddenFor(m => Model.Requests[i].RequestID)
<table class="table table-responsive img-rounded">
<thead>
<tr class="alert-cascade">
<th colspan="2">
<div class="btn-group btn-group-sm pull-right" role="group" data-toggle="buttons">
<button class="btn btn-success" data-toggle="tooltip" title="Accept" id="acceptradio">
#Html.RadioButtonFor(m => Model.Requests[i].AcceptChecked, Model.Requests[i].AcceptChecked, new { #id = Model.Requests[i].RequestID, #style = "display:none" })
<span>Accept </span>
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
</button>
<button class="btn btn-warning" data-toggle="tooltip" title="Reject" id="rejectradio">
#Html.RadioButtonFor(m => Model.Requests[i].RejectChecked, Model.Requests[i].RejectChecked, new { #id = Model.Requests[i].RequestID, #style = "display:none" })
<span>Reject </span>
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
</button>
</div>
#Model.Requests[i].EmployeeDescription
</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Request Type</strong></td>
<td class="text-right">#Model.Requests[i].RequestType</td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td class="text-right">#Model.Requests[i].DurationDescription</td>
</tr>
<tr>
<td><strong>Dates</strong></td>
<td class="text-right">#Model.Requests[i].DatesDescription</td>
</tr>
</tbody>
</table>
</div>
}
The view:
#model RequestPageModel
#{
ViewData["Title"] = "Requests";
ViewData["SubTitle"] = "Welcome to Cascade Mobile";
}
#{Layout = "~/Views/Shared/_MainLayout.cshtml";}
#section Scripts {
<script type="text/javascript">
//Submit count setter
$(document).ready(function () {
var accepted = 0;
var rejected = 0;
$("#acceptradio").click(function () {
console.log("ready 2!");
$("#acceptCount").text("4");
});
});
</script>
}
<div class="container">
<h3>#ViewBag.Warning</h3>
#*Existing requests*#
<h4><strong>Requests</strong> <span class="badge alert-cascade">#Model.Pager.TotalRecords</span></h4><br />
#using (Html.BeginForm("Index", "Request", new { #id = "requestsform" }))
{
<div class="row">
#Html.Partial("_MultiSelectPartial", Model.RequestModel)
</div>
<div>
<textarea class="span6" rows="3" placeholder="Comments.." required></textarea>
</div>
<input type="submit" value="submit"/>
}
<button id="submitbtn" class="btn btn-primary pull-right" type="button">
Accept
<span class="badge" id="acceptCount">0</span>
Reject
<span class="badge" id="rejectCount">0</span>
</button>
#Html.Partial("_Pager", Model.Pager)
</div>
The controller action:
[HttpPost]
public IActionResult Index(RequestModel requests)
{
ViewBag.Warning = "We have: ";
foreach (var request in requests.Requests)
{
ViewBag.Warning += request.RequestID + " : ** : ";
}
var requestModel = GetRequestPageModel(3,1);
//requestModel.Requests[0].AcceptChecked = true;
return View("~/Views/User/Requests.cshtml", requestModel);
}
The model:
using System;
using System.Collections.Generic;
namespace Mobile.Models
{
/// <summary>
/// Request data model for the requests page
/// </summary>
public class RequestModel
{
public List<Request> Requests;
public RequestModel(List<Request> requests)
{
Requests = requests;
}
public RequestModel()
{
Requests = new List<Request>();
}
}
}
Again, if the post method takes just a list of request items its fine, but I will need to pass more information and cannot get it to post the list as part of the model. Can anyone see whats wrong here?
The model in your partial is RequestModel and your loop is generating controls with
name="Requests[0].RequestID"
name="Requests[1].RequestID"
etc, but the model in your POST method should be RequestPageModel so the correct name attributes would need to be
name="RequestModel.Requests[0].RequestID"
name="RequestModel.Requests[1].RequestID"
which will post back to
[HttpPost]
public IActionResult Index(RequestPageModel model)
You need to change the model in the partial to #model RequestPageModel (and adjust the HtmlHelper methods accordingly) and in the main view, use #Html.Partial("_MultiSelectPartial", Model).
In addition, change the name of the parameter to (say) RequestModel model so there is no conflict with the equivalent property name (refer this answer for an explanation).
I would however recommend that you use custom EditorTemplate's for your types rather than a partial, so that you controls are correctly named (refer the 2nd part of this answer for an example)
Side notes:
Your radio buttons do not makes sense, since they have different
names so you can select both (and once selected, you cannot
un-select them)
Your have a <textarea> without a name attribute so it will not
submit a value.

How do I Pass Selected items from one page to another page in asp.net mvc

In my index page, i have multiple checkboxes that can be selected:
#foreach (var item in Model) {
<tr>
<td class ="text-nowrap">
<input class="chkSelect" type="checkbox" name="Selected" value="#item.Id">
</td>
<td>
#Html.ValueFor(modelItem => item.trandate, "{0:dd/MM/yyyy}")
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id })
</td>
</tr>
}
I need to get the selected items and pass them to another form/page /action where i want to use the ids of the selected item to perform other actions.
How do i pass the selected items to the controller action of the other page ?
so , now i created public ActionResult SendSMS(string[] msisdn)
{
return View();
}
and
#using (Html.BeginForm("SendSMS", "Subscribers", FormMethod.Post))
{
<div class="form-group">
<span class="btn btn-primary"> <i class="fa fa-envelope"></i> Send SMS</span>
</div>
}
So, will Request["Selected"] hold all the selected items? it appears null when debugging.
You could POST it to the controller and do a redirect with those values. The values would then do in a query strings to the other action. Like:
return RedirectToAction("Index", "Home", new { value1 = value1, value2 = value 2});
I was able to get this done by simply using a submit button. and
and in the controller action SendSMS i have :
if (Request["Selected"] != null)
{
model.msisdn = string.Empty;
model.MessageId = string.Empty;
foreach (var selection in Request["Selected"].Split(','))
{
}
}

Ajax form not submitting and updating a partialview

I'm going crazy on this... I've been trying to figure out how to use ajax to update a partial view for quite some time... I've got to be missing something simple and easy.
I'm in VS2012, MVC4.
First, I do have these bundles loaded:
#Styles.Render("~/css")
#Styles.Render("~/Content/themes/base/css")
#Scripts.Render("~/js" )
#Scripts.Render("~/bundles/jqueryui")
#Scripts.Render("~/bundles/jqueryval")
I have also tried to specify the unobtrusive script manually as well...
Then I have this in my view:
#using (Ajax.BeginForm("Index_AddGroup", new AjaxOptions { UpdateTargetId = "AddGroupList" }))
{
<div>
#Html.LabelFor(model => Model.NewGroups.GroupName)
</div>
<div>
#Html.DropDownListFor(model => Model.AllGroups.nSelectGroupID, Model.AllGroups.GroupList, "Select Group to Add")
<input type="submit" value="Add Group" />
</div>
}
Then I load a partial view:
<div id="AddGroupList">
#if(Model.Groups != null)
{
#Html.Partial("_AddGroups", Model.Groups);
}
</div>
In that partial view I do the following:
#model IEnumerable<MyApp.ViewModels.Group>
#{
ViewBag.Title = "Added Groups";
}
<h2>Groups to be Added</h2>
<table>
<tr>
<th>Group Name</th>
<th>Added until</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(model=>item.GroupName)</td>
<td>#Html.DisplayFor(model=>item.EndDate)</td>
</tr>
}
Controller:
[HttpPost]
public ActionResult Index_AddGroup(SearchedUser viewModel)
{
AddGroupsContext db = new AddGroupsContext();
Group NewGroup = new Group();
NewGroup.GroupName = "test";//viewModel.
db.Groups.Add(NewGroup);
db.SaveChanges();
return PartialView("_AddGroups", db.Groups);
}
I loaded up fiddler and clicked on the button but no request was being sent. Why isn't that javascript/ajax code running?
I think you need a submit button. try:
<input type="submit" value="Add Group" />
Also i think you will need to add a reference to as it doesn't look like it is included anywhere:
jquery.unobtrusive-ajax.js
I think you can get it through nuget if you dont have it already.
otherwise here is a script tag that includes a CDN link:
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/mvc/3.0/jquery.unobtrusive-ajax.js"></script>

Categories

Resources