I have an asp.net mvc application. I want to perform and update operation.I want to show you my code before explain my problem sorry I am not fluent in english thats why I prefer to show my code first.Here I have an index page that shows all items in database with a table
#model IEnumerable<InOut.Models.Item>
#if (Model.Count() > 0)
{
<table class="table table-bordered table-striped" style="width: 100%; text-align: center">
<thead>
<tr>
<th>Item Name</th>
<th>Borrower</th>
<th>Lender</th>
<th >Edit</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td style="width: 25%">#item.borrower</td>
<td style="width: 25%">#item.Lender</td>
<td style="width: 25%">#item.ItemName</td>
<td style="width: 13%">
<a asp-controller="Item" asp-action="Delete" asp-route-id="#item.id" onclick="showAlert()"class="btn btn-danger" id="btn1" style="text-decoration: none; color: white">delete</a>
<a asp-controller="Item" asp-action="Update" asp-route-id="#item.id" class="btn btn-primary" id="btn1" style="text-decoration: none; color: white">Update</a>
</td>
</tr>
}
</tbody>
</table>
}
else
{
<p> No Items created </p>
}
Here is the index action for that view
public IActionResult Index()
{
IEnumerable<Item> items = _dbContext.Items;
return View(items);
}
In the foreach loop I have a button to update the object on that row it has an action named "Update" in controller class.here is the code to get the item on that row.
//Get Item
public IActionResult Update(int? id)
{
var item = _dbContext.Items.Single(o => o.id == id);
return View(item);
}
this action sends the selected item properties to the update view.here is the code of view
<form method="post" asp-action="Create">
<input asp-for="id" hidden/>
<div class="form-group">
<label >Item Name </label>
<input type="text" class="form-control" aria-describedby="emailHelp" asp-for="ItemName">
</div>
<div class="form-group">
<label >Borrower</label>
<input type="text" class="form-control" asp-for="borrower">
</div>
<div class="form-group">
<label >Lender</label>
<input type="text" class="form-control" asp-for="Lender">
</div>
<button asp-controller="Item" asp-action="Update" type="submit" class="btn btn-primary" style="margin-top: 10px">Submit</button>
submit button sends properties to the overloaded action update with the parameter of item. after update operation I want it to show me the index page. here is the overloaded update action
// Update Item
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Update(Item item)
{
_dbContext.Update(item);
_dbContext.SaveChanges();
return View("Index");
}
Problem starts here. When I run application after submitting changes I want it to show me the index action update operation working well.but when it try to show me the index page it gives me this error.
ArgumentNullException: Value cannot be null. (Parameter 'source') and it refers to the if statement in the index page it
now if I use redirectToAction return instead of View like this...
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Update(Item item)
{
_dbContext.Update(item);
_dbContext.SaveChanges();
return RedirectToAction("Index");
}
It works as I wanted. I want to know what is the problem with the first approach and is there any difference between those 2 return types?
the difference is here
public IActionResult Index()
{
IEnumerable<Item> items = _dbContext.Items; //maybe you need to add .ToList(); ??
return View(items);
}
when you are using an index action you create the list of items as a model and pass them to the view. But you don't create any model when you call Index view from an update action. If you still want to return view from an update you can do it this way
_dbContext.Update(item);
_dbContext.SaveChanges();
var items = _dbContext.Items.ToList();
return View("Index", items);
or if update and index actions are in the same controller I usually just call an action as a method
_dbContext.Update(item);
_dbContext.SaveChanges();
return Index();
Related
I have created a form that edits a list of notes. The form displays the Id of each note along with the message it contains. This works.
The problem is that after changing the messages in these notes and submitting the changes, the form is sent but I receive an empty list of models from the HttpPost action-method parameter.
I looked through many similar questions but a common problem was that the view model did not contain public properties. Mine does. I can't see where the problem is. I am a beginner to programming so I apologise if the problem is too obvious.
// My View Model
public class NoteViewModel
{
public int Id { get; set; }
public string Message { get; set; }
}
// My Post Action method
[HttpPost]
public IActionResult EditNotes(List<NoteViewModel> model)
{
foreach (var item in model)
{
// Create a note, and copy values from model
Note note = new Note
{
Id = item.Id,
Message = item.Message
};
// Update note in database.
noteRepository.Update(note);
}
return RedirectToAction("NotePage", "Home");
}
// My View EditNote.cshtml
#model List<MyWebsite.ViewModels.NoteViewModel>
<form asp-action="EditNotes" method="post">
#foreach (var note in Model)
{
<label asp-for="#note.Id">#note.Id</label>
<label asp-for="#note.Message">Message</label>
<input asp-for="#note.Message" value="#note.Message" />
}
<button type="submit" class="btn btn-success">Submit</button>
</form>
I expect to receive a list of models that contain the notes, but I receive an empty list here
public IActionResult EditNotes(List<NoteViewModel> model)
{
// model is empty
// model.Count() gives 0.
}
This:
#model List<MyWebsite.ViewModels.NoteViewModel>
<form asp-action="EditNotes" method="post">
#foreach (var note in Model)
{
<label asp-for="#day.Id">#note.Id</label>
<label asp-for="#note.Message">Message</label>
<input asp-for="#day.Message" value="#day.Message" />
}
<button type="submit" class="btn btn-success">Submit</button>
</form>
Needs to be this:
#model List<MyWebsite.ViewModels.NoteViewModel>
<form asp-action="EditNotes" method="post">
#for( Int32 i = 0; i < this.Model.Count; i++ ) {
<label>#Model[i].Id</label>
<input asp-for="#Model[i].Id" type="hidden" />
<label asp-for="#Model[i].Message">Message</label>
<input asp-for="#Model[i].Message" />
}
<button type="submit" class="btn btn-success">Submit</button>
</form>
This is because ASP.NET Core MVC's model-binding uses Expression<Func<TModel,TProperty>> for generating the name="" attributes, which means it needs a full expression to get from TModel to the property it's bound to.
The tag-helper will also generate the value="" attribute for you, you don't need to specify it manually. Ditto the <label>'s text too (especially if you use the [Display] or [DisplayName] attributes on the model properties.
You should iterate with a for block at the view side. For example see this example:
<form asp-action="EditBulk" , method="post">
<table class="table">
<thead>
<tr>
<th>
<label id="noteTextLbl">Note Text</label>
</th>
<th></th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
<input asp-for="#Model[i].NoteId" type="hidden" />
<input asp-for="#Model[i].NoteText" />
</td>
<td>
<a asp-action="Edit" asp-route-id="#Model[i].NoteId">Edit</a> |
<a asp-action="Details" asp-route-id="#Model[i].NoteId">Details</a> |
<a asp-action="Delete" asp-route-id="#Model[i].NoteId">Delete</a>
</td>
</tr>
}
</tbody>
</table>
<button type="submit">Send changes</button>
</form>
I don't know why, but this just works.
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.
I have a problem when I am trying to post IEnumerable from razor view to Controllor action method. Also result is the same if I use List.
I post my controllor action method also in comment. In my controllor action method I got list that is empty.
This is my View:
#model IEnumerable<Subject>
<form asp-action="AddNewSubjects" asp-controller="Teacher" method="post" role="form" class="form-horizontal">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Number of class</th>
<th>Level</th>
</tr>
</thead>
<tbody>
#if (Model != null)
{
var item = Model.ToList();
#for(int i=0;i<Model.Count();i++)
{
<tr>
<td>#item[i].ID</td>
<td>#item[i].Name</td>
<td>#item[i].ClassNumber</td>
<td>#item[i].Level</td>
</tr>
}
}
</tbody>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-5">
<input type="submit" class="btn btn-primary" value="Save all subjects" />
</div>
</div>
</form>
This is my Controller:
private readonly ISubjectService _subjectService;
public TeacherController(ISubjectService subjectService)
{
_subjectService= subjectService;
}
[HttpPost]
public IActionResult AddNewSubjects(IEnumerable<Subject> subjects)
{
var newSubjects= (from p in subjects
where p.State== Status.New
select p);
var result = _subjectService.SaveTeacherSubjects(newSubjects);
return View("ProfesorPages");
}
I have no idea what you're trying to do here. Your form doesn't have any input element except the submit button. Of course you're not seeing anything posted back.
#model IEnumerable<Subject>
<form>
...
<tbody>
#for(int i = 0; i < Model.Count(); i++)
{
<tr>
<td>
<input type="hidden" asp-for="Model[i].ID" />
</td>
<td>
<input type="text" asp-for="Model[i].Name" />
</td>
...
</tr>
}
</tbody>
...
</form>
Why??
Why did you convert your IEnumerable to a list named item? Why not just enumerate your subjects directly?
Why not create a different set of models called ViewModel and pass that to the View, instead of using your model from your database directly on the View?
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.
I have been trying to select multiple rows from my table of data(Generated using EF) and then pass all selected rows to the next view to perform some action. On passing the data to the next view i am getting the following error :
System.NullReferenceException: 'Object reference not set to an instance of an object.'
Temporary local of type 'int[]'> was null.
Any help on how to solve this will be appreciated.
Below is my code:
View:
<div class="row">
<div class="col-md-12">
<!-- Advanced Tables -->
<div class="panel panel-default">
<div class="panel-heading">
#using (Html.BeginForm()) {
<form action="#" method="post">
<label>Search by Company Name:</label> #Html.TextBox("SearchString")
<input type="submit" value="Go" placeholder="Search" style="background-color: #0a9dbd; color: white; border-color: #0a9dbd;">
<label>Search by Card Number:</label> #Html.TextBox("searchCard")
<input type="submit" value="Go" placeholder="Search" style="background-color: #0a9dbd; color: white; border-color: #0a9dbd;">
Export to Excel
</form>
}
</div>
<div class="panel-body">
Add Gift Card
Get Card Balance
Load Cards
<br />
<br />
<div class="table-responsive">
<table class="table table-striped table-bordered table-hover" id="dataTables-example">
<thead>
<tr>
<th>Card ID</th>
<th>Company</th>
<th>Card Number</th>
<th>Card Number 2</th>
<th>Date Created</th>
<th>Card Status</th>
<th>Discount Level ID</th>
<th>Loyalty Level ID</th>
<th>Gift Card Enabled</th>
<th>Loyalty Enabled</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td><input type="checkbox" name="ids" value="#item.CardID" /></td>
<td>#item.CardID</td>
<td>#item.Customer.CustomerCompanyName</td>
<td>#item.CardNumber</td>
<td>#item.CardNumber2</td>
<td>#item.CardDate</td>
<td>#item.CardStatus</td>
<td>#item.DiscountLevelID</td>
<td>#item.LoyaltyLevelID</td>
<td>#item.GiftCardEnabled</td>
<td>#item.LoyaltyEnabled</td>
<td>
<i class="fa fa-edit "></i> Edit <br />
</td>
</tr>
}
</tbody>
</table>
Page #(Model.PageCount
< Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount #Html.PagedListPager(Model, page=> Url.Action("Index", new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }))
</div>
</div>
</div>
<!--End Advanced Tables -->
</div>
</div>
Controller:
public ActionResult PostCards(int[]ids)
{
var myObject = new Card();
foreach(var id in ids)
{
myObject = db.Cards.Single(o => o.CardID == id);
return RedirectToAction("LoadCards", myObject);
}
return View();
}
public ActionResult LoadCards()
{
return View();
}
I need the selected data to be passed to the LoadCards view.
Let us first look at the NullReference you are getting. The problem here is that no correct index is created to bind the checkboxes to an array. Use a for loop instead of foreach. In MVC/Razor, how do I get the values of multiple checkboxes and pass them all to the controller?
To get the desired behaviour:
change the foreach to a for loop so the correct indices for sending the data will be created.
add a checkbox in each row that lets the user select the rows to submit.
your action should recieve a collection of models for each row. This model always transports the CardId and tells us whether it was selected.
public class SelectedCardModel {
public int CardId { get; set; }
public bool IsSelected {get; set;}
}
In the view:
#using (Html.BeginForm("PostCards", "CustomerView", FormMethod.Post) {
// <table> etc ...
<tbody>
#for (var i = 0; i < Model.Count; i++) {
#{ var item = Model.ElementAt(i); }
<tr>
<td>
#Html.Hidden("CardId[" + i + "]")
#Html.CheckBox("IsSelected[" + i + "]")
</td>
// display other properties of item ...
<td>#item.CardID</td>
// ...
</tr>
}
</tbody>
</table>
<button type="submit">Load Cards</button>
}
Action:
[HttpPost]
public ActionResult PostCards(SelectedCardModel[] selectedCards) {
foreach(var card in selectedCards) {
if (card.IsSelected) {
var selectedId = card.CardId;
// ...
}
}
}