Actionlink rowindex stored to a C# get set - javascript

I have an ActionLink that is deleting a row from a html table and should be updating the controller. The problem however is that the information of the row that the button is on isn't being shared with the controller once the button is clicked.
<table id="LansingData" class="table">
<thead>
<tr>
<th>Action</th>
<th>Row ID</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Records)
{
<tr id="#item.RowIndex">
<td>#Html.ActionLink("Delete", "Delete", new { id = item.RowIndex }, new { onclick = "return confirm('Are you sure you want to delete this user?');", #class = "delete-button" })</td>
<td>#item.RowIndex</td>
</tr>
}
</tbody>
</table>
This is my controller that is performing the delete.
public ActionResult Delete(int? rowID)
{
if (rowID == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
LansingMileage lansing = db.LansingMileages.Find(rowID);
if (lansing == null)
{
return HttpNotFound();
}
return View(lansing);
}
//POST:
[HttpPost, ActionName("Index")]
[OnAction(ButtonName = "Delete")]
//[ValidateAntiForgeryToken]
public ActionResult Delete(int rowID)
{
LansingMileage lansing = db.LansingMileages.Find(rowID);
db.LansingMileages.Remove(lansing);
db.SaveChanges();
return RedirectToAction("Index");
}
The desired result will be for the link to post to the controller with the rowID of the whatever link was selected.

Looks like a naming issue. The parameter is called id in the view and (most likely) in route definition, but the controller expects rowID. Just rename this last one to be id too:
public ActionResult Delete(int? id)
Model binding in MVC is done by name, so make sure posted parameters always match those expected on the server side.

Related

Bind json to html table in view

i am trying to bind my data to a html table in my view, how do i go about this
public ActionResult FlugTopAir()
{
DataModel db = new DataModel();
var test = db.Database.SqlQuery<FlugTopAirData>("exec sp_FlugTopAir").ToList();
return Json(test, JsonRequestBehavior.AllowGet);
}
public class FlugTopAirData
{
public string Airline { get; set; }
public double Spend { get; set; }
public double TA { get; set; }
}
That will depend on the client side framework that you use.
If you are looking for the native js here is the link:
https://www.w3schools.com/js/js_json_html.asp
You could use jQuery Ajax to call that controller action. Like below
<table class="table">
<thead>
<tr>
<td>Airline</td>
<td>Spend</td>
<td>TA</td>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
#section scripts{
<script type="text/javascript">
$.ajax({
url: '#Url.Action("FlugTopAir")',
type: 'GET',
cache: false,
success: function (result) {
var rows = result.map(function (record) {
var row = $("<tr></tr>");
var airline = $("<td></td>").html(record.Airline);
var spend = $("<td></td>").html(record.Spend);
var ta = $("<td></td>").html(record.TA);
row.append(airline, spend, ta);
return row;
});
$("#tableBody").append(rows);
}
});
</script>
}
You should probably consider using some template engine like JSRender for this to be honest.
Or the easiest way would be to return the view with model so you can use Razor syntax to iterate through Model from the view
View (Index.cshtml)
#using MVCTestApp.Models
<table class="table">
<thead>
<tr>
<td>Airline
<td>Spend</td>
<td>TA</td>
</tr>
</thead>
#foreach (TestModel testModel in Model)
{
<tr>
<td>#testModel.Airline</td>
<td>#testModel.Spend</td>
<td>#testModel.TA</td>
</tr>
}
</table>
In controller, instead of returning Json, return View
return View(test);

How do I retrieve a viewmodel from another viewmodel?

I have this ViewModel which incorporates 3 other viewmodels and a list:
public class GroupPageViewModel{
public string GroupName { get; set; }
public GroupSelectViewModel _groupSelectVM {get; set;}
public List<User> _users { get; set; }
public ViewModelStudent _studentVM { get; set; }
public ViewModelGroupMembers _groupMembersVM { get; set; }
}
In the view I can access each of these sub-ViewModels by Model._groupSelectVM, each of the sub-ViewModels are associated with a partial view. The problem arises when I need to refresh just one or two partial views, I'm not sure how to access the inner ViewModels returned in an Ajax success, and as I'm relatively new to MVC and asp.net in general. And I literally know next to nothing about JavaScript, jquery or Ajax.
How would I go about getting a specific ViewModel from the main ViewModel in an Ajax success?
This is just one example for the clarification requested all the others are pretty much the same (although some of them might need to update mutliple partial views -
From the controller:
[HttpPost]
public ActionResult Index(string groupChoice = "0", string newGroup = "")
{
string groupName = "";
if (groupChoice == "0" && newGroup != "")
{
if (ModelState.IsValid)
{
Group group = new Group
{
GroupName = newGroup,
Active = true
};
db.Groups.Add(group);
db.SaveChanges();
PopulateLists();
}
}
else if (groupList == null)
{
groupList = (List<SelectListItem>)Session["groupList"];
Session["groupName"] = groupName = groupList.Where(m => m.Value == groupChoice).FirstOrDefault().Text;
MembersInSpecificGroup(groupName, groupMembers, groupMembersList);
groupPageVM._groupMembersVM = groupMembers;
}
return View("GroupSelection", groupPageVM);
}
The script:
$(document).ready(function () {
$('#selectedGroup').change(function () {
var data = {
groupChoice: $('#selectedGroup').val()
};
var groupChoice = $('#selectedGroup').val();
$.ajax({
url: '/Group/Index/',
type: 'POST',
data: { groupChoice: groupChoice },
success: function (data) {
setTimeout(function () {
delayGroupSuccess(data);
}, delay);
}
});
})
});
function delayGroupSuccess(data) {
$("#groupSelect").html(data);
}
The main page:
#model EMBAProgram.ViewModels.GroupPageViewModel
#{ Layout = "~/Views/Shared/_Layout.cshtml"; }
<h2>Group Selection</h2>
<div class="row" id="groupSelect">
#{ Html.RenderPartial("_GroupSelect", Model._groupSelectVM);}
</div>
<hr size="5" />
<div style="display: flex;">
<div>
#{Html.RenderPartial("_Students", Model._studentVM);}
</div>
<div>
#{ Html.RenderPartial("_GroupMembers", Model._groupMembersVM);}
</div>
<div>
#{ Html.RenderPartial("_Users", Model._users);}
</div>
<br style="clear: left;" />
</div>
The partial view:
#model EMBAProgram.ViewModels.ViewModelGroupMembers
<div class="table-responsive" id="groupResults">
<table class="table table-condensed table-responsive">
<thead>
<tr>
<th>#Html.DisplayName("M-Number")</th>
<th>#Html.DisplayName("Name")</th>
<th>#Html.DisplayName("Student")</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model._groupVM) {
<tr>
<td>#Html.DisplayFor(m => item.MNumber)</td>
<td>#Html.DisplayFor(m => item.Name)</td>
<td>#Html.DisplayFor(m => item.Student)</td>
</tr>
}
</tbody>
</table>
</div>
Basically I need to be able pull the ViewModel for the partial view from the main ViewModel (which I believe is what is being returned in the Ajax,) and refresh the partial view.
I removed the original answer, it's available in the edit log if folks want to see it I think. But it was taking up too much space and was incorrect.
You can return multiple partial views, I thought it was a built in way to get them to a string (I was in a rush in my comment), but I've got a working example.
In the controller I have the following:
public ActionResult Index()
{
var model = new TestViewModel
{
Students = GetStudents(),
Categories = GetCategories(),
Groups = GetGroups()
};
return View("Index", model);
}
// Returns multiple partial views as strings.
public ActionResult StudentsAndGroups()
{
return Json(new
{
Students = RenderRazorViewToString("_Students", GetStudents()),
Groups = RenderRazorViewToString("_Groups", GetGroups())
}, JsonRequestBehavior.AllowGet);
}
// Creates a string from a Partial View render.
private string RenderRazorViewToString(string viewName, object model)
{
ControllerContext.Controller.ViewData.Model = model;
using (var stringWriter = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
var viewContext = new ViewContext(ControllerContext, viewResult.View, ControllerContext.Controller.ViewData, ControllerContext.Controller.TempData, stringWriter);
viewResult.View.Render(viewContext, stringWriter);
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
return stringWriter.GetStringBuilder().ToString();
}
}
I have my main index view that looks like the following:
<button class="refresh">Refresh</button>
<div class="row">
<div class="col-md-4 students">
#{
Html.RenderPartial("_Students", Model.Students);
}
</div>
<div class="col-md-4">
#{
Html.RenderPartial("_Category", Model.Categories);
}
</div>
<div class="col-md-4 groups">
#{
Html.RenderPartial("_Groups", Model.Groups);
}
</div>
</div>
#section scripts
{
<script type="text/javascript">
$(".refresh").click(function () {
$.get("/Home/StudentsAndGroups", function (d) {
$(".students").html(d.Students);
$(".groups").html(d.Groups);
})
});
</script>
}
The controller action StudentsAndGroups turns two partial views into strings. From there, the javascript calls that view and accesses the elements and returns them.
Helper method for rendering a view as a string was found here: https://stackoverflow.com/a/34968687/6509508

Knockout - Instead of the data-bind value, javascript is displayed

I created a ASP.Net MVC 5 project and used Knockout.js library.
I have a View called Statement which basically shows the a table with a couple of Transaction items.
My complete Statement.cshtml is as follow:
#using Newtonsoft.Json;
#model IEnumerable<ATMMVCLearning.Models.Transaction>
#{
ViewBag.Title = "Statement";
}
<h2>Statement</h2>
<table class="table table-striped table-bordered">
<thead>
<tr>
<td><strong>Transaction ID</strong></td>
<td><strong>Amount</strong></td>
</tr>
</thead>
<tbody data-bind="foreach:currentTransactions">
<tr>
<td data-bind="text:Id"></td>
<td data-bind="text:formattedPrice"></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">
<span data-bind="click:previousPage" class="glyphicon glyphicon-circle-arrow-left"
style="cursor:pointer;"></span>
<span data-bind="text:currentPage"></span>
<span data-bind="click:nextPage"class="glyphicon glyphicon-circle-arrow-right"
style="cursor:pointer;"></span>
</td>
</tr>
</tfoot>
</table>
<script src="~/Scripts/knockout-3.4.0.js"></script>
<script>
function formattedPrice(amount) {
var price = amount.toFixed(2);
return price;
}
function StatementViewModel() {
var self = this;
//properties
//note that there is a ko.observableArray for making bindings for array
self.transactions = #Html.Raw(JsonConvert.SerializeObject(Model, new JsonSerializerSettings {
ReferenceLoopHandling = ReferenceLoopHandling.Ignore}));
//TODO: embed transactions from server as JSON array
self.pageSize = 5; //number of transactions to display per page
self.currentPage = ko.observable(1); //the first observable. If the page changes, then the grid changes
self.currentTransactions = ko.computed(function () {
var startIndex = (self.currentPage() - 1) * self.pageSize; //because currentPage is an observable, we get the value by calling it like a function
var endIndex = startIndex + self.pageSize;
return self.transactions.slice(startIndex, endIndex);
});
//methods to move the page forward and backward
self.nextPage = function () {
self.currentPage(self.currentPage() + 1);
};
self.previousPage = function () {
self.currentPage(self.currentPage() - 1);
};
};
ko.applyBindings(new StatementViewModel()); //note this apply bindings, used extensively in KnockOut
</script>
As you can see in the <tbody> I have two <td> elements which have data-bind attribute:
<tbody data-bind="foreach:currentTransactions">
<tr>
<td data-bind="text:Id"></td>
<td data-bind="text:formattedPrice"></td>
</tr>
</tbody>
And the formattedPrice can be referred to the script section below:
function formattedPrice(amount) {
var price = amount.toFixed(2);
return price;
}
Now, I expect the resulting View when it is rendered should show a table with 5 transactions each page, where each table row shows an Id as well as its transaction amount. I.e. something like:
1 100.00
2 150.00
3 -40.00
4 111.11
5 787.33
However, when I render the page, I got the following result:
Instead of Id and amount, I got Id and javascript.
Any idea?
Update:
The Transaction class is as follow:
public class Transaction {
public int Id { get; set; } //this is internally used, not need to have anything
[Required]
[DataType(DataType.Currency)]
public decimal Amount { get; set; }
[Required]
public int CheckingAccountId{ get; set; }
public virtual CheckingAccount CheckingAccount { get; set; } //this is to force the entity framework to recognize this as a foreign key
}
Since formattedPrice is not part of your view-model, Knockout won't automatically unwrap it, nor will it pass it the amount argument.
Try this instead:
<td data-bind="text: formattedPrice(Amount)"></td>
Price probably needs to be computed field and you need to bind to price (I think). It's been a while since I did Knockoutjs.

In an MVC project, how do you update the model when a drop down list changes value?

I have a MVC project using Kendo controls. On one of the views is a drop down box and text box. Both are initially getting their values from the model. How can I change the model (and therefore the text box) when the user selects an item from the drop down?
For example, the Model is filled in the controller setting the original value of the item the drop down box is based on to "General" and the item the text box is based on to "Widgets". When the user selects "Special" from the drop down, the controller would query the database to get data based on "Special", find that the new value of the text box should say "Doodads", add "Doodads to the model and change the text box to "Doodads".
View
#model GPC.Models.ModelInstrumentListingDetail
#using (Html.BeginForm("InstrumentListingDetailClick", "Home", FormMethod.Post, new { id = "InstrumentListingDetailForm" }))
{
<div id="divInstrumentListingDetailHeader" class="detailDivs">
<table>
<tr>
<tr>
<td style="text-align: right;" class="dropdowns">
<label>Category:</label>
</td>
</tr>
</table>
</div> // divInstrumentListingDetailHeader
<div id="divInstrumentListingDetailBody" class="detailDivs details">
<table class="details">
#*Field 1*#
<tr>
<td style="text-align: right;">
#Html.DisplayFor(m => m.Label1)
</td>
<td width="2px;"> </td>
<td class="dropdowns">
#Html.TextBoxFor(m => m.Field1, new { #class = "details" })
</td>
</tr>
</table>
</div> // divInstrumentListingDetailBody
}
<script>
function onChange_ddInstrumentCategory(arg) {
var categoryID = $(arg).find('option:selected').val();
// Update model based on the category ID
}
</script>
Controller
public ActionResult InstrumentListingEdit(TblInstrumentTag model)
{
TblInstrumentTag currentInstrumentTag = data.GetInstrumentTagByID(model.InstrumentTagID);
// Fill Category drop down
List<TblInstrumentFormCategory> categories = data.GetAllCategories();
// Create model
ModelInstrumentListingDetail detailModel = new ModelInstrumentListingDetail
{
InstrumentTagID = currentInstrumentTag.InstrumentTagID,
InstrumentCategory = categories.FirstOrDefault().InstrumentFormCategoryID,
Field1 = currentInstrumentTag.FormCategory1Value1,
Label1 = categories.FirstOrDefault().Label1 + ":",
ieInstrumentCategories = new SelectList(categories, "InstrumentFormCategoryID", "InstrumentFormCategoryName")
};
return View("InstrumentListingEdit", detailModel);
}
Model
public class ModelInstrumentListingDetail
{
// Drop down ID's
public int InstrumentTagID { get; set; }
public int InstrumentCategory { get; set; }
// Detail fields
public string Field1 { get; set; }
// Detail labels
public string Label1 { get; set; }
// Drop downs for add/edit page
public IEnumerable<SelectListItem> ieInstrumentCategories { get; set; }
}
What I'd like is to get from the javascript to something like this code below to update the text box. I'd rather not post the entire page. I don't want the screen to "blink"; I just want the user to select an item from the dropdown and for the textbox value to change.
Need to get from jQuery to something like this without submitting the form:
public ActionResult UpdateModel(TblInstrumentTag model, int newCatgoryID)
{
TblInstrumentTag currentInstrumentTag = data.GetInstrumentTagByID(model.InstrumentTagID);
// Fill Category drop down
List<TblInstrumentFormCategory> categories = data.GetAllCategories();
// Create model
ModelInstrumentListingDetail detailModel = new ModelInstrumentListingDetail
{
InstrumentTagID = currentInstrumentTag.InstrumentTagID,
InstrumentCategory = categories.FirstOrDefault().InstrumentFormCategoryID,
Field1 = currentInstrumentTag.FormCategory2Value1, // <- Value of Field 1 has changed
Label1 = categories.FirstOrDefault().Label1 + ":",
ieInstrumentCategories = new SelectList(categories, "InstrumentFormCategoryID", "InstrumentFormCategoryName")
};
return View("InstrumentListingEdit", detailModel);
}
JQuery is a good place to start. If I understand correctly, you only want to query the DB after changing the drop down's value, and then changing the value of the textbox to the corresponding change.
JQuery:
$(document).ready(function(){
$('#myDropDown').change(selectionChange());
});
function selectionChange() {
var dropDownValue = $('#myDropDown').val();
var textBox = $('#myTextBox');
$.ajax({
url: "/mycontroller/querydb",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify(dropDownValue),
success: function (data, status) {
textBox.val(data);
},
type: "post"
});
return;
}
Controller:
[HttpPost]
public JsonResult QueryDB(string dropDownValue)
{
string newTextBoxValue = string.Empty;
//your db code
return Json (newTextBoxValue) );
}
It's a fairly watered down version of a JQuery AJAX to MVC Controller deal. Hopefully it will work for you!

ASP.NET MVC 4 How to add rows to table containing dropdownlistfor

I have a small table as part of a form. It displays a number alongside a dropdown that allows a user to select a value for to go along with that number. I want to allow the user use add rows to this table and be able to assign as many values as is required. I've been searching Google all day and trying many things but nothing seems to fit my situation. What is the best way to achieve this functionality?
Here is the code from my view containing the table and my add actionlink:
<div class="form-group">
#Html.LabelFor(m=>m.Part.PartConnectionTypes, new {#class = "control-label col-md-5"})
<div class="col-md-7">
<table class="table table-condensed table-striped table-bordered">
<thead>
<tr>
<td>Number</td>
<td>Value</td>
</tr>
</thead>
<tbody>
#{
for(var i = 0; i < Model.Part.PartConnectionTypes.Count; i++)
{
var count = i + 1;
<tr>
<td>#count</td>
<td>#Html.DropDownListFor(m => m.Part.PartConnectionTypes[i].ConnectionType, new SelectList(Model.ConnectsTo, "CodeId", "ValChar"), "", new { #class = "form-control" })</td>
</tr>
}
}
</tbody>
</table>
#Html.ActionLink("Add Connection", "AddNewPartConnection", "Home", new { id = "addConnection"})
</div>
And here is my controller method from for the action link:
public ActionResult AddNewPartConnection(IndexViewModel model)
{
var newPartConnectionType = new PartConnectionType();
model.Part.PartConnectionTypes.Add(newPartConnectionType);
return View("Index", model);
}
and the index method:
public ActionResult Index(string searchString)
{
var model = new IndexViewModel { Part = new Part() };
model.Part.PartConnectionTypes.Add(new PartConnectionType());
if (!String.IsNullOrEmpty(searchString))
{
model.Part = CoreLogic.GetPartByPartCode(searchString);
if (model.Part == null)
{
model.Part = new Part();
model.Part.PartConnectionTypes.Add(new PartConnectionType());
ViewBag.SearchMessage = "That is not a current part";
}
}
return View(model);
}
I appreciate any help anyone can provide.
I would just separate the view for "table rows" to be as a "partial view" and render that on add button click event.
Two was you can achieve this,
1) using jquery, call ajax and let mvc return html to you and you
place it right after your "last row" in the table
2) Use mvc ajax
form and tell it what to do - render an extra row(placeholder) in
the end and give that as an id to replace html on success.
Hope this helps.

Categories

Resources