I have a .cshtml file which I am using the model:
#model IEnumerable<WebApplicationMVCTest.Models.Test>
The model contains the parameter Date (among other parameters). I access this value when creating a table:
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.SiteID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Date)
</td>
...
I have a function in my code NotTested and I want to pass the item.Date value into the function as one of the arguments:
<script>
document.onload = NotTested("Site 001", item.Date);
function NotTested(SiteId, TestDate) { ... }
</script>
The problem is two-fold. 1) I know that the Date is part of the model in this view so that it is present onload when the function is called, but 2) the model is out of context in the <script> section of code.
I know that item is not present onload since this is is only transient for the for loop. I have just used item.Date as a placeholder argument.
How do I get access to this when calling the function?
From your comments, property Date is the same for each item in your collection, so you can assign it to a javascript variable using
var date = JSON.parse('#Html.Raw(Json.Encode(Model[0].Date))');
or in the controller, assign in to ViewBag.Date = yourDate; and in the script
var date = JSON.parse('#Html.Raw(Json.Encode(ViewBag.Date))');
You can then use call the function using
<script>
$(function() {
var date = JSON.parse('#Html.Raw(Json.Encode(ViewBag.Date))');
NotTested("Site 001", date);
});
</script>
Assign a css class to the date item and use jquery to select the date items based on the css class as following:
#Html.DisplayFor(modelItem => item.SiteID, new { htmlAttributes = new { #class = "mySiteIDClass" } })
#Html.DisplayFor(modelItem => item.Date, new { htmlAttributes = new { #class = "myDateClass" } })
<script>
document.onload = NotTested($(".mySiteIDClass"), $(".myDateClass"));
function NotTested(SiteIds, TestDates) { //loop through date items }
</script>
Related
I am doing the CRUD for the table called Recipe. The whole functionality is already created in an API and at this point, I am trying to implement it on a web page. During the creation of a new recipe, you have to complete some fields such as TotalTimeUnitId. This is a column in the Recipe table that only takes integers; however, in the user interface you have to pick from a list with seconds/minutes/hours, their value being the thing that needs to be passed in the database through the controller.
<div class="form-group">
#Html.LabelFor(model => model.TotalTimeUnitId)
#Html.EditorFor(model => model.TotalTimeUnitId)
#Html.ValidationMessageFor(model => model.TotalTimeUnitId)
<select id="list" onchange="getSelectedtValue();">
<option value="1">seconds</option>
<option value="2">minutes</option>
<option value="3">hours</option>
</select>
<script>
function getSelectedtValue() {
var selectedValue = document.getElementById("list").value;
console.log(selectedValue);
}
getSelectedtValue();
</script>
This is the code that I wrote trying to achieve this. If you pick, let's say, minutes from the list, the getSelectedValue() function returns "2". How can I be able to call this method inside the EditorFor so the variable TotalTimeUnitId will be assigned the value? Is there another way to doing this?
#Html.DropDownListFor(
model => model.TotalTimeUnitId,
new SelectList(
new List<Object>{
new { value = 1 , text = "Seconds" },
new { value = 2 , text = "Minutes" },
new { value = 3 , text = "Hours"}
},
"value",
"text",
Model.TotalTimeUnitId
)
)
This did the job.
I am presenting the question with the wording shown so that it indicates what I am really trying to do. While I could present the question as, "How can I use jQuery to read a table's contents an convert to type IEnumerable<ModelName>?" I would be hiding important context that could suggest a far better way of achieving this outcome, which is what I'm really hoping for.
When auto-scaffolding a controller and views from a model (using Entity Framework), the resulting view allows one to edit one record at a time by clicking the row, editing the record, and returning to the Index. For many cases that is the desired functionality.
But what about when one wants to mass-update values in a table, as if one were working on a spreadsheet. For example, toggle some checkboxes and click "Update", like in this scenario.
One working idea I have is to use jQuery to:
Read the table data and convert into a variable of type IEnumerable<SampleModel>.
Pass the table contents to a public void method in the controller.
Notify the user that the action was successful and refresh the page.
It's really #1 where I am stuck because I need to be able to pass an object of type IEnumerable<SampleModel> to the controller's method, and I do not know how to do this.
Controller:
public void UpdateTable(IEnumerable<SampleModel> query)
{
// I'm verifying existence before writing code here.
// This is where I'll be updating the database.
Debug.WriteLine("Test for query existence " + query.Count());
}
View:
#model SampleSystem.Areas.FakeName
<button id="UpdateTableTop">Update</button>
<table class="table">
<tr>
<th></th>
<th>
#Html.DisplayNameFor(model => model.Reconciled)
</th>
<th>
#Html.DisplayNameFor(model => model.StudentID)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.HiddenFor(modelItem=>item.ID)
</td>
<td>
#Html.CheckBoxFor(modelItem => item.Reconciled)
</td>
<td>
#Html.DisplayFor(modelItem => item.StudentID)
</td>
</tr>
}
</table>
#section scripts {
<script>
$(function () {
$("UpdateTableTop").click(function (e) {
$.ajax({
url: '#Url.Action("UpdateTable")',
data:
/* ... Need to convert table
* to IEnumerable<SampleModel> ...
*/,
success: function (){
console.log("Success!");
}
});
});
});
</script>
}
One more possible approach in addition to previous answer.
Add data attribute to the checkbox
#Html.CheckBoxFor(modelItem => item.Reconciled, new { data_myid = item.ID }) /* the _ gets converted to - */
Later, use jQuery to retrieve all checked checkboxes.
var selectedIds = new Array();
$( "input:checked" ).each(function(eachCheckbox){
selectedIds.push($(eachCheckbox).data('myid'));// could serialize to JSON but if the update action is the same for all id's then there is no need for additional data transfer
});
Then pass selectedIds as your data in ajax call and notify on success.
On the server side:
public void UpdateTable(IEnumerable<int> query) /* change type to int */
{
Debug.WriteLine("Test for query existence " + query.Count());
}
This is not a complete answer, but should lead you in the correct direction.
$("UpdateTableTop").click(function (e) {
var selectedModels = [];
// Get all of the rows that were selected
// Loop through each row, create an object and add to array
for(var i = 0; i < numItemsSelected; i++)
{
// Make sure the properties of the model match your definition of SampleModel
var currentModel = {
Reconciled = ??? , //Reconciled from the current row
StudentID = ???
};
selectedModels.push(currentModel);
}
$.ajax({
url: '#Url.Action("UpdateTable")',
data: /* pass in the list of models (selectedModels) */,
success: function (){
console.log("Success!");
}
});
});
On the server-side, model-binding will take care of creating the SampleModel based on the posted values.
In my MVC application I have a view where I will display different data from a SQL table. This will generate different amount of drop down lists and text boxes, depending on what is passed in from the Model.
My issue is if I want to then use that data I can't seem to figure out how I can relate control X to object Y in SQL. For example, if I have 2 textboxes that I want to do an update on, then when the Post happens in my application the FormCollection parameter will let me see the Value of the objects, but not their control name or any form of identifying factor.
I could set the Value to a combination of the entered value + a name, then split this, but it seems very much like a lazy workaround.
I've tried to assign an ID to each, for example:
#foreach (DataObject item in Model.AllDataObjects)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Data)
</td>
<td>
#if (item.Rule.Contains("Yes;No"))
{
#Html.DropDownListFor(model => item.Value, new List<SelectListItem>
{
new SelectListItem {Text="Yes", Value="Yes"},
new SelectListItem {Text="No", Value="No" }
}, new { #id = item.ObjectId });
}
else
{
#Html.TextAreaFor(model => item.Value, new { style = "width: 400px;", #rows = 5, #id = item.ObjectId })
}
</td>
</tr>
}
Edit: The following is my Post ActionResult method in the Controller, albeit it isn't complete as I can't figure out how to get an ID for the control from the FormCollection
[HttpPost]
[ValidateInput(false)]
public ActionResult UpdateData(FormCollection collection, int objectId=0)
{
try
{
int propertyTypeId = 0;
string propertyValue = string.Empty;
// Get all the control values from the collection
string[] allValues = new string[] { };
IValueProvider valueProvider = collection.ToValueProvider();
foreach(string key in collection.Keys)
{
ValueProviderResult result = valueProvider.GetValue(key);
allValues = result.RawValue as string[];
}
ObjectData objectData = _execution.GetObjectDetails(0);
UpdateDataResponse result = _execution.UpdateData(0, objectId,
objectValue, UserName);
return RedirectToAction("Details",
new { ObjectId = objectData.ObjectId, error = result.ErrorMessage });
}
catch (Exception ex)
{
// My exception handling here
}
}
So I can see in the mark-up that the controls are assigned the object ID as their own ID, but how can I get this back? When I check FormCollection I only see the values for each control, but no way of identifying which is which.
Edit: I'm using MVC version 4.
A form only submits the values of its successful controls (as name/value pairs based on the controls name and value attributes) so if you do not generate a control for the ObjectId properties, they will not be submitted.
However, you current use of foreach loop will not allow you to obtain any meaning information from the data which is posted because all your names are identical and there is no way to reliable match up which value belongs to which item in the collection. Instead use a for loop or EditorTemplate in the view and bind to your model, rather than using FormCollection.
The view should be
#for (int i = 0; i < Model.AllDataObjects.Count; i++)
{
<tr>
<td>#Html.DisplayFor(m => m.AllDataObjects[i].Name)</td>
<td>#Html.DisplayFor(m => m.AllDataObjects[i].Data)</td>
<td>
#Html.HiddenFor(m => m.AllDataObjects[i].ObjectId)
#if (Model.AllDataObjects[i].Rule.Contains("Yes;No"))
{
#Html.DropDownListFor(m => m.AllDataObjects[i].Value, new SelectList(new string[]{ "Yes", "No" }));
}
else
{
#Html.TextAreaFor(m => m.AllDataObjects[i].Value, new { style = "width: 400px;", #rows = 5 })
}
</td>
</tr>
}
And assuming the model in the view is #model MyModel, change the POST method to
[HttpPost]
[ValidateInput(false)]
public ActionResult UpdateData(MyModel model)
and the value of model.AllDataObjects will contain a collection with its ObjectId and Value properties correctly bound.
For more information on why using a foreach loop will not work, refer to this answer.
Here is what my view looks like:
#model Affiliate
<div class="box paint color_16">
<div class="title">
<h4><i class="icon-tasks"></i><span>#Model.CompanyName's Commissions</span> </h4>
</div>
<div class="content top ">
<div class="subtitle">
#Html.ActionLink("Void", "DeleteInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
#Html.ActionLink("Create Invoice", "CreateInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
#Html.ActionLink("Pay", "PayInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
</div>
<table class="table table-striped table-hover">
<tr>
<h3>Commissions</h3>
</tr>
<tr>
<td></td>
<td>Amount</td>
<td>Status</td>
<td>Action</td>
</tr>
#foreach (var item in Model.Commissions)
{
<tr>
#if (item.Status == ViewBag.PaymentStatus || ViewBag.PaymentStatus == "All")
{
<td>#Html.CheckBox("commId", new { value = item.Id })</td>
<td>#Html.DisplayFor(x => item.PayoutAmount)</td>
<td>#Html.DisplayFor(x => item.Status)</td>
}
</tr>
}
</table>
</div>
What I want to be able to do is when I hit an actionlink on the top, grab all the items from the table that are checked, and pass that list of id's to the controller logic. I am assuming a viewmodel may be the solution, something like this:
public Affiliate affilite { get; set; }
public List<int> selectedItems { get; set; }
etc.
But how to I get the selected Items into that VM selectedItems container?
Based off your comments, you don't seem to be looking for the most "correct" answer, but rather just a quick and dirty "how would I do this" answer. If you just want to pass the list, you could setup your controller action like this:
public ActionResult MyAction(int[] id)
{
...
}
Or, you seem to indicate it is strongly typed to a view model with a property that contains a List (I would shorten the name of the property, you'll see why in a second).
In javascript, the easiest thing to do would be to use jQuery to bind a click event on your hyperlink that gets the list of items that are checked and appends that to the query string.
$("#myLink").click(function()
{
var url = "site.com/action?";
var ids = $(".table").find("input:checked");
ids.each(function()
{
url += "id=" + $(this).val() + "&"
});
window.location = url;
});
Basically, you want to create one long query string with the action parameter's name repeated over and over, which identifies an array. It looks something like this (id is for int[] id in MyAction):
id=15&id=20&id=25&id=30&....
And then once the query string is built, redirect the user to that url. MVC should then be able to bind that to an array and you're all set.
That's basically the idea, anyway; the syntax and the javascript I wrote could be way off so don't copy my code and expect it to work as is - I wrote that off the top of my head. If your action is bound to a viewmodel, then you need to set the parameter in the query string to the name of the property of your model:
selectedids=1&selectedids=2&selectedids=3...
Or, if the array is a property of an object, which is a property of the model...
model.selectedids=1&model.selectedids=2&model.selectedids=3...
You'll just need to play around with it some.
Use html checks inside form tag ( you could use helpers too) and post the model to a post action.
MVC will serialize the model automatically
I have 3 dropdownlist i wanna make 3 dropdownlist with cascade. I am using LinqSql for database..
I have 3 tables Product(id,name), Design(id,master_id,name), Model(id,design_id,name)
master_id bound to Product(id), design_id bound to Design(id)..
I want to create one dropdown which is gonna show Products and than when i choose a product its gonna make Design dropdown enabled else it will stay disabled.. also here is the tricky part that i couldnt solve and i need great explanation in here creating 3rd dropdown which is gonna be disabled normally till a Design is chosen.
Each of them gonna populate a lower dropdownlist bound to them.Its like;
Product gonna enable and populate Design,
Design gonna enable and populate Model.
I can do it with 2 dropdowns but when it comes to 3 dropdown i stuck really badly im on (brain-freeze)..
I already checked the other questions couldnt find any solution for my self. As i said im using LinqSql i need a solution about 3 cascadingdropdown list for this type of data reach.
thanks already for anything u can do! and if u can explain Model-View-Controller partials and the parameters and why you use them that would be awesome. Iam kinda beginner at this MVC3.
I would approach the problem something like this:
First, in the controller, we'll set up have the following methods:
public JsonResult GetDesignsForProduct(int productId)
{
// Instantiate our context and do whatever goo we need to select the objects we want
using (MyDatabaseContext ctx = new MyDatabaseContext())
{
return Json(ctx.Designs.Where(d => d.master_id == productId).ToList(), JsonRequestBehavior.AllowGet);
}
}
public JsonResult GetModelsForDesign(int designId)
{
// Instantiate our context and do whatever goo we need to select the objects we want
using (MyDatabaseContext ctx = new MyDatabaseContext())
{
return Json(ctx.Models.Where(d => d.design_id == designId).ToList(), JsonRequestBehavior.AllowGet);
}
}
I've turned on "get" here; if your data contains sensitive information - user names/e-mail addresses, other proprietary or legally protected data, etc. - you can change this to only allow "post", and modify your Javascript accordingly. See Phil Haack's article.
Also, if you expect this data to change frequently, these methods will cache it by default according to your application's cache settings. You can add an OutputCache attribute on the method to alter this behavior.
Then, in the view you'll have some AJAX plumbing, something like this:
function LoadDesigns() {
// Get the currently-selected value in our Product dropdown
var prod = $("#Product").val();
// Call our controller method and process the list of Design objects
$.getJSON('#Url.Content("~/ControllerName/GetDesignsForProduct")', { productId: prod },
function (designs) {
$("#Design").empty();
$.each(designs, function (i, c) {
$("#Design").append(
$('<option></option>').val(c.id).html(c.name)
);
});
});
}
function LoadModels() {
// Get the currently-selected value in our Design dropdown
var des = $("#Design").val();
// Call our controller method and process the list of Model objects
$.getJSON('#Url.Content("~/ControllerName/GetModelsForDesign")', { designId: des },
function (models) {
$("#Model").empty();
$.each(models, function (i, c) {
$("#Model").append(
$('<option></option>').val(c.id).html(c.name)
);
});
});
}
Finally, define all three drop-downs as follows:
#Html.DropDownList("Product", productSelectList, new { onchange = "LoadDesigns()" })
#Html.DropDownList("Design", null, new { onchange = "LoadModels()" })
#Html.DropDownList("Model")
Don't forget that the HTML helpers are really just shortcuts to generate the underlying HTML, and in Razor you frequently just go straight to HTML instead of messing with the helpers. So you could just as easily write these as:
<select id="Product" onchange="LoadDesigns()">
#foreach (var prod in products) {
<option value="#prod.id">#prod.name</option>
}
</select>
<select id="Design" onchange="LoadModels()"></select>
<select id="Model"></select>
Forget to set my finished work.. People may wanna see how it happens..
Here is my:
View + Jquery
$(function () {
$("select#Design").attr('disabled', 'true');
$("select#Model").attr('disabled', 'true');
$("select#Product").click(function () {
var prod = $("select#Product option:selected").val();
if (prod == "" || prod == 0) {
$("select#Design").attr('disabled', 'true');
$("select#Model").attr('disabled', 'true');
} else {
$.getJSON('#Url.Content("~/Admin/GetDesigns/")', { productId: prod }, function (data) {
$("select#Design").empty();
$("select#Model").empty();
$.each(data, function (i, c) {
$('select#Design').append('<option value="' + c.Value + '">' + c.Text + '</option>');
})
$("select#Design").removeAttr('disabled');
$("select#Design option:first").attr('selected', 'selected');
var des = $("select#Design option:selected").val();
if (des == "" || des == 0) {
$("select#Model").attr('disabled', 'true');
} else {
$.getJSON('#Url.Content("~/Admin/GetModels/")', { designId: des }, function (data) {
$("select#Model").empty();
$.each(data, function (i, c) {
$('select#Model').append('<option value="' + c.Value + '">' + c.Text + '</option>');
})
$("select#Model").removeAttr('disabled');
$("select#Model option:first").attr('selected', 'selected');
})
}
})
}
})
})
reason i use Jquery this way to fill all dropdowns and select first elements as default selection! When i choose an element from first dropdown the other two dropdowns starts to fill themselves and select their first element as default selection.. same code can be used for other dropdowns click function just like this:
$("select#Design").click(function () {
var des = $("select#Design option:selected").val();
if (des == "" || des == 0) {
$("select#Model").attr('disabled', 'true');
} else {
$.getJSON('#Url.Content("~/Admin/GetModels/")', { designId: des }, function (data) {
$("select#Model").empty();
$.each(data, function (i, c) {
$('select#Model').append('<option value="' + c.Value + '">' + c.Text + '</option>');
})
$("select#Model").removeAttr('disabled');
$("select#Model option:first").attr('selected', 'selected');
})
}
});
View
#using (Html.BeginForm("Index", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<table>
<tr>
<td style="background-color:#e8eef4;" rowspan="3">
</td>
<td style="width:190px; background-color:#e8eef4;">
#Html.DropDownList("Product", (SelectList)ViewData["ProductList"], "Please Select Product", new { style = "width:190px; padding:4px; margin:4px;" })
</td>
<td rowspan="3" style="width:400;">
</td>
<td style="background-color:#e8eef4;">
</td>
<td style="background-color:#e8eef4;" rowspan="3">
</td>
</tr>
<tr>
<td style="background-color:#e8eef4;">
<select id="Design" style="width:190px; padding:4px; margin:4px;">
<option label="Please Select Design" selected="selected"></option>
</select>
</td>
<td style="background-color:#e8eef4;">
</td>
</tr>
<tr>
<td style="background-color:#e8eef4;">
<select id="Model" style=" width:190px; padding:4px; margin:4px;">
<option label="Please Select Model"></option>
</select>
</td>
<td style="background-color:#e8eef4;">
</td>
</tr>
</table>
}
Just cause im using linqtosql and im too lazy to make a repository..
This is my CONTROLLER
public class AdminController : Controller
{
public linqVipDataContext db = new linqVipDataContext();
//
// GET: /Admin/
public ActionResult Index()
{
IEnumerable<SelectListItem> ProductItems = db.Products.AsEnumerable().Select(c => new SelectListItem()
{
Text = c.name,
Value = c.id.ToString(),
Selected = true,
});
SelectList prod = new SelectList(ProductItems, "Value", "Text");
ViewBag.ProductList = prod;
return View();
}
//
//Fill the Design List..
public JsonResult GetDesigns(int productId)
{
/*var data = dbs.Designs.Where(d => d.master_id == productId).ToList();*/
IEnumerable<SelectListItem> DesignItems = db.Designs.Where(c => c.master_id == productId).AsEnumerable().Select(c => new SelectListItem()
{
Text = c.name,
Value = c.id.ToString()
});
SelectList des = new SelectList(DesignItems, "Value", "Text");
return Json(des, JsonRequestBehavior.AllowGet);
}
//
//Fill the Model List..
public JsonResult GetModels(int designId)
{
/*This code down here! Doesnt work and says it's type is unknown*/
/*var data = dbs.Models.Where(d => d.design_id == designId).ToList();*/
/*For that reason im using this code*/
IEnumerable<SelectListItem> ModelItems = db.Models.Where(d => d.design_id == designId).AsEnumerable().Select(c => new SelectListItem()
{
Text = c.name,
Value = c.id.ToString()
});
SelectList mods= new SelectList(ModelItems, "Value", "Text");
return Json(mods, JsonRequestBehavior.AllowGet);
}
Json requires Value and Text 2 param seperated for creating a selectlist option.. So i must return my value that way..
I posted this cause i found some breakdowns at ur code, ty again for showing me this solution it gave me the idea and allowed me to solve all problems so this is the fully working code.. Ty again. Hope its usefull.