I'm using .NetCore and Entity Framework to design a site.
On the site, I have a cshtml page that displays a table of data like this:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.CharacterId)
</td>
<td>
#Html.DisplayFor(modelItem => item.CharacterDisplayName)
</td>
<td>
#Html.DisplayFor(modelItem => item.TimePathYear)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.CharacterId">Edit</a> |
</td>
</tr>
}
Where, when the user clicks on the "Edit" link, it will send them to this controller:
public async Task<IActionResult> Edit(int? id) { }
However, I now want to pass in a user-selectable date range so that the controller would look like this:
public async Task<IActionResult> Edit(int? id, DateTime? start, DateTime? end) { }
So I added these two date controls to the cshtml page:
<div class="datePickers">
<div class="startDate">
Start Date
<input id="inputStartDate" type="date" asp-format="{0:yyyy-MM-dd}"/>
</div>
<div class="endDate">
End Date
<input id="inputEndDate" type="date" asp-format="{0:yyyy-MM-dd}"/>
<button formaction="/Characters/Index/">Go</button>
</div>
</div>
But how would I add the date values they select to the asp-action?
Thanks!
Not sure where you put the startDate and startDate , i assume you put the two date select input on top of your table :
<div class="datePickers">
<div class="startDate">
Start Date
<input id="inputStartDate" type="date" asp-format="{0:yyyy-MM-dd}"/>
</div>
<div class="endDate">
End Date
<input id="inputEndDate" type="date" asp-format="{0:yyyy-MM-dd}"/>
<button formaction="/Characters/Index/">Go</button>
</div>
</div>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.CharacterId)
</td>
<td>
#Html.DisplayFor(modelItem => item.CharacterDisplayName)
</td>
<td>
#Html.DisplayFor(modelItem => item.TimePathYear)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.CharacterId">Edit</a> |
</td>
</tr>
}
Then you can use Jquery to dynamically append the query string by :
#section Scripts{
<script>
$(function () {
$('#inputStartDate').on('change', function () {
var querystring = 'inputStartDate=' + $("#inputStartDate").val() + '&inputEndDate=' + $("#inputEndDate").val();
$("a").each(function () {
var href = $(this).attr('href').split('?')[0];
if (href) {
href += (href.match(/\?/) ? '&' : '?') + querystring;
$(this).attr('href', href);
}
});
});
})
And when user click the edit link , you could get parameters from server side :
public async Task<IActionResult> Edit(int? id, DateTime? inputStartDate, DateTime? inputEndDate)
{
}
Related
Hey I have a RoadRecord table. I have an SQL database for it. I display it on Index.html. In the RoadRecords there are date,LicencePlate, Type, Fuel, Mileage etc.., User can edit these RoadRecords. But here is the problem, how can I implement that user only can edit the newest record for each Licenceplate.
For example:
Date
LicencePlate
08.19
AAA-111
08.17
AAA-111
08.16
CCC-222
In this situation I should be able to edit only the 08.19 AAA-111, and the 08.16 CCC-222.
There is any way to implement it in code or in SQL?
Here is my table in .html
<tbody>
#foreach (var item in Model)
{
<tr class="click box-shadow" onclick="location.href = '#(Url.Action("Details", "RoadRecords", new { id = item.Id }))'">
<td>
#Html.DisplayFor(modelItem => item.Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.LicencePlateNavigation.LicencePlate)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location)
</td>
<td>
#Html.DisplayFor(modelItem => item.Mileage)
</td>
<td>
#Html.DisplayFor(modelItem => item.Fueling)
</td>
<td>
#Html.DisplayFor(modelItem => item.Note)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.Id">Szerkesztés</a> |
<!--<a asp-action="Details" asp-route-id="#item.Id">Részletek</a> | -->
<a asp-action="Delete" asp-route-id="#item.Id">Törlés</a>
</td>
</tr>
}
</tbody>
EDIT 1 - Did it in SQL Manager Studio
I got it in SQL Manager Studio, now, how can I implement it in EntityFramework, or in code?
SELECT * FROM [RoadRecord] WHERE [Date] IN ( SELECT MAX([Date]) FROM [RoadRecord] GROUP BY [LicencePlate]);
You can use this linq to select the newest datatime goupby LicencePlate
var result = _dbContext.RoadRecords.GroupBy(x => x.LicencePlate).Select(x=>x.Max(z=>z.Date)).ToList();
I write a simple demo here.
public class HomeController : Controller
{
private readonly ProductDbContext _dbContext;
public HomeController(ProductDbContext dbContext)
{
_dbContext = dbContext;
}
//...other code....
[HttpPost]
public IActionResult Index(RoadRecord model)
{
//select the newest datatime from database
var result = _dbContext.RoadRecords.GroupBy(x => x.LicencePlate).Select(x=>x.Max(z=>z.Date)).ToList();
if (!result.Contains(model.Date))
{
//if the record is not the newest one, The controller will not change the data and response the error message.
ModelState.AddModelError("","you can only change the newest record");
var listmodel = _dbContext.RoadRecords.ToList();
return View(listmodel);
}
else
{
//if the record is the newest one,the controller will update the data in db.
//.......
}
}
}
Then add
<div asp-validation-summary="All" class="text-danger"></div>
In your View, Once user update the uncorrest record, The view will show the error message.
Or you can use FluentValidation to custom your validation rules, But i think it will be more complex.
I am writing a movie app that allows you to rent movies (similar to Redbox). I have a CheckOut cart view containing a table. Each table row has a remove button which uses AJAX to delete element in the view and also update the SQL database that the app works with. After removing any items from the cart, the user should be able to click 'purchase' and process the items that were left in the cart, all without needing to refresh the page.
I have an Order model containing a list of OrderDetails. Each OrderDetails item has information about a movie. It is data from OrderDetails list that the table is populated with.
The issue comes in when I remove items from cart and try to post the form with the values in the table. My CheckOut HttpPost controller method receives the model, but the OrderDetail list still has the item count it originally had before I removed items from cart. Logically, there is no data bound to the properties since I deleted the hidden tags I had in each record.
Because the list contains elements I don't need, processing the list results in garbage data going into the database.
I tried to simply remove the garbage elements within my CheckOut HttpPost method before it begins processing the list. This worked great but I don't want to have to remove anything in the CheckOut method after posting the form. I'm expecting the list to not contain the elements.
CheckOut POST method:
[HttpPost]
public IActionResult CheckOut(Order order)
{
if (ModelState.IsValid == false)
{
return View("CheckOut", order);
}
foreach (var orderDetailObj in order.OrderDetailsList)
{
_checkOutService.StoreMoviesInOrder(GetConnectionString(), order.OrderId, orderDetailObj);
}
return RedirectToAction("PurchaseSummary", new { Id = order.OrderId });
}
CheckOut.cshtml view:
#model MovieContract.Order
...
#for (int i = 0; i < Model.OrderDetailsList.Count; i++)
{
<tr>
<td>
<input type="button" name="btnRemove" class="removeButton" value="Remove" onclick="Remove(this, '#Model.CartId', #Model.OrderDetailsList[i].Movie.FilmId)" />
</td>
<td hidden>
<input asp-for="#Model.OrderDetailsList[i].Movie.AddedToCart" value="#Model.OrderDetailsList[i].Movie.AddedToCart" hidden />
</td>
<td hidden>
<input asp-for="#Model.OrderDetailsList[i].Movie.FilmId" value="#Model.OrderDetailsList[i].Movie.FilmId" hidden />
</td>
<td>
<input asp-for="#Model.OrderDetailsList[i].Movie.FilmName" value="#Model.OrderDetailsList[i].Movie.FilmName" hidden />
#Model.OrderDetailsList[i].Movie.FilmName
</td>
<td>
<input asp-for="#Model.OrderDetailsList[i].Movie.GenreName" value="#Model.OrderDetailsList[i].Movie.GenreName" hidden />
#Model.OrderDetailsList[i].Movie.GenreName
</td>
<td>
<input asp-for="#Model.OrderDetailsList[i].Movie.PricePerDay" value="#Model.OrderDetailsList[i].Movie.PricePerDay" class="pricePerDay" hidden />
#Html.DisplayFor(modelItem => #Model.OrderDetailsList[i].Movie.PricePerDay)
</td>
<td hidden>
<input asp-for="#Model.OrderDetailsList[i].Movie.AmountOnHand" value="#Model.OrderDetailsList[i].Movie.AmountOnHand" hidden />
</td>
</tr>
}
As for AJAX, I simply have an AJAX function that calls a post controller method. The method deletes the appropriate item from the database and returns NoContent();. Upon success, AJAX deletes the desired row from the view.
I expect that by the time I reach the CheckOut HttpPost method, the parameter object's list property will contain less elements if I had decided to remove any from the cart. I don't want to have to refresh the whole page to rebuild my model each time I remove an item from the cart.
Here is a working demo :
View
#model AjaxDeleteItem.Models.Order
<div>
<form method="post" asp-action="CheckOut">
<table class="table" id="table">
<thead>
<tr>
<th></th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.OrderDetailsList.Count; i++)
{
<tr class="count">
<td>
<input type="button" name="btnRemove" class="removeButton" value="Remove" onclick="Remove(this, #Model.OrderDetailsList[i].Id)" />
</td>
<td >
<input class="FilmId" asp-for="#Model.OrderDetailsList[i].Movie.FilmId" value="#Model.OrderDetailsList[i].Movie.FilmId" />
</td>
<td >
<input class="FilmName" asp-for="#Model.OrderDetailsList[i].Movie.FilmName" value="#Model.OrderDetailsList[i].Movie.FilmName" />
</td >
<td>
<input class="GenreName" asp-for="#Model.OrderDetailsList[i].Movie.GenreName" value="#Model.OrderDetailsList[i].Movie.GenreName" />
</td>
<td>
<input class="PricePerDay" asp-for="#Model.OrderDetailsList[i].Movie.PricePerDay" value="#Model.OrderDetailsList[i].Movie.PricePerDay" />
</td>
</tr>
}
</tbody>
</table>
<input type="submit" value="Submit"/>
</form>
</div>
#section Scripts
{
<script>
function Remove(obj,id) {
$.ajax({
type: "post",
url: "/orders/deleteorderitem?id="+id,
success: function () {
$(obj).closest('tr').remove();
var count = $(" tbody tr").length;
var i = 0;
$("tbody tr").each(function () {
var row = $(this);
if (i < count)
{
row.find("input[class=FilmId]").attr("name", "OrderDetailsList[" + i + "].Movie.FilmId");
row.find("input[class=FilmName]").attr("name", "OrderDetailsList[" + i + "].Movie.FilmName");
row.find("input[class=GenreName]").attr("name", "OrderDetailsList[" + i + "].Movie.GenreName");
row.find("input[class=PricePerDay]").attr("name", "OrderDetailsList[" + i + "].Movie.PricePerDay");
i++;
}
});
},
error: function () {
alert("Fail to delete");
}
});
}
</script>
}
2.Controller:
[HttpPost]
public async Task<IActionResult> DeleteOrderItem(int id)
{
var orderdetail = await _context.OrderDetails.FindAsync(id);
_context.OrderDetails.Remove(orderdetail);
await _context.SaveChangesAsync();
return NoContent();
}
[HttpPost]
public IActionResult CheckOut(Order order)
{
if (ModelState.IsValid == false)
{
return View("Details", order.CartId);
}
//the stuff you want
}
3. Result :
I have a view that contains a table of items. In each row of the table, you have the option to click on a link to edit the item which takes you to a different page. I'm trying to make it so that once you finish editing the item and save your changes, it takes you back to the original view but at the same spot.
So, for example, if you had 300 rows in this table and were working with row 247. I click Edit, make some changes, and when I save I'd like to go back to row 247. Instead, I'm brought back to the top of the page after saving. I've seen some stuff for jquery, but isn't there something simple that can be done in razor with anchor tags?
My Code looks like this:
View
Index.cshtml
...
...
...
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.AID)
</th>
<th>
#Html.DisplayNameFor(model => model.Comment)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.AID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Comment)
</td>
<td colspan="5" align="right">
<button type="button" class="btn btn-primary" onclick="location.href='#Url.Action("Edit", new {id = item.AID })'">Action Event</button>
</td>
</tr>
}
</table>
Controller
DatabaseController.cs
public ActionResult Index()
{
return View(db.ActionableEvents.ToList());
}
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ActionableEvents actionableEvents = db.ActionableEvents.Find(id);
if (actionableEvents == null)
{
return HttpNotFound();
}
return View(actionableEvents);
}
[HttpPost]
public ActionResult Edit([Bind(Include = "AID,Comment")] ActionableEvents actionableEvents)
{
if (ModelState.IsValid)
{
db.Entry(actionableEvents).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(actionableEvents);
}
Ideally, it would be nice in my HttpPost for the Edit to do something like:
return RedirectToAction("Index",idtag);
where idtag could be a route value that loads the page at the specific anchor tag. Something to the effect of "https://www.mywebsite.com/Home/Index#247".
The question is, how can I append the model.AID value to the anchor tag (or maybe just do something in my tag) in my for loop and then reference this in the controller?
Add an id attribute matching the item id to each row:
#foreach (var item in Model)
{
<tr id="#item.AID"> #* assuming AID is the item id (e.g. 247)*#
<td>
#Html.DisplayFor(modelItem => item.AID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Comment)
</td>
<td colspan="5" align="right">
<button type="button" class="btn btn-primary" onclick="location.href='#Url.Action("Edit", new {id = item.AID })'">Action Event</button>
</td>
</tr>
}
Then in your controller set the anchor to the html row's id which should be the same as the item id:
return Redirect(Url.Action("Index") + "#" + idtag); // assuming idtag is the item id (e.g. 247)
The idtag anchor must match the table row (tr) id attribute to make the page jump to the row.
i added PagedList in my project, i have a search view, which basically calls a function to get values from database, my Pagedlist is working fine, but i am not sure if this gets whole data from database or only based on pageSize. For example i have total 30 records, and i added pagesize as 10, so view will show 10 records at a time and 3 pages. Now this PagedList get all 30 values, or only get 10 first time, then if user click on page 2 it gets next 10 records.
Because lets say i have thousand records, so i dont want system to load all of them, i just want to load it according to pagesize , and when user click on second page it gets next records, hope my point is clear, below is my code:
View:
#model PagedList.IPagedList<BackendIntegration.Models.VendorAllTransactions>
#using PagedList.Mvc;
#if (Model != null)
{
<hr />
<div class="form-horizontal">
<table class="table">
<tr class="bg-primary">
<th>
#Html.DisplayName("Date")
</th>
<th>
#Html.DisplayName("Type")
</th>
<th>
#Html.DisplayName("Amount")
</th>
<th>
#Html.DisplayName("User Name")
</th>
<th>
#Html.DisplayName("Vendor Name")
</th>
<th>
#Html.DisplayName("POS ID")
</th>
</tr>
#foreach (var trans in Model)
{
<tr>
<td>
#Html.DisplayFor(model => trans.Date)
</td>
<td>
#Html.DisplayFor(model => trans.TypeDesc)
</td>
<td>
#Html.DisplayFor(model => trans.Amount)
</td>
<td>
#Html.DisplayFor(model => trans.UserName)
</td>
<td>
#Html.DisplayFor(model => trans.VendorName)
</td>
<td>
#Html.DisplayFor(model => trans.POS_ID)
</td>
</tr>
}
</table>
<br />
<div id='Paging' style="text-align: center">
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber)
of #Model.PageCount
#Html.PagedListPager(Model, page => Url.Action("VendorAllTransactions", new { page }))
</div>
</div>
}
Controller:
public ActionResult VendorAllTransactions(VendorAllTransactionView vw, int? page)
{
int pageSize = 10;
int pageIndex = 1;
pageIndex = page.HasValue ? Convert.ToInt32(page) : 1;
IPagedList<VendorAllTransactions> model = null;
try
{
model = sales.VendorAllTransactions(vw).ToPagedList(pageIndex, pageSize);
}
catch (Exception exp)
{
ModelState.AddModelError("", exp.Message);
}
return View(model);
}
sales.VendorAllTransactions(vw) this call a fucntion with query, which will return records. But this clearly returning all the records. Is Pagedlist get only required number of rows(pagesize) or it gets everything.???
Please advise.
Thanks
I'm trying to show a drop down list of options to filter with, but the select helper tag is not showing the bounded list.
This is the Home Controller:
public IActionResult Index(string SelectedClinic)
{
GetReservationsAsync();
HomeIndexvmIns.Clinics = new SelectList(_clinicsLookup, "Id", "ClinicName");
return View(HomeIndexvmIns);
}
This is the HomeIndexViewModel:
public HomeIndexViewModel()
{
Reservations = new List<ReservationViewModel>();
}
public List<ReservationViewModel> Reservations { get; set; }
public SelectList Clinics { get; set; }
public string SelectedClinic { get; set; }
And this is the Index View:
#model schedule.ViewModels.HomeIndexViewModel
<form asp-controller="Home" asp-action="Index" method="post">
<p>
<select asp-for="SelectedClinic" asp-items="Model.Clinics"></select>
</p>
</form>
<table class="table">
<tbody>
<thead>
<tr>
<th>
Reservation Id
</th>
<th>
Start Date/Time
</th>
<th>
End Date/Time
</th>
<th>
Status
</th>
<th>
Clinic
</th>
<th></th>
</tr>
</thead>
#foreach (var item in Model.Reservations)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.StartDateTime)
</td>
<td>
#Html.DisplayFor(modelItem => item.EndDateTime)
</td>
<td>
#Html.DisplayFor(modelItem => item.Status)
</td>
<td>
#Html.DisplayFor(modelItem => item.Clinic)
</td>
</tr>
}
</tbody>
</table>
I tried changing the SelectList to an IEnumerable of Clinics or a List of SelectListItems but still no options showed up on the drop down.
EDIT
I just changed the Select Tag with
#Html.DropDownListFor(m => m.SelectedClinic, Model.Clinics)
And it rendered correctly, i still wanna know why the Select Tag didn't work.