MVC - Expandable Partial View within ForEach loop - javascript

I have a View with a model of type:
IEnumerable<StoreMonthlyTarget>
Here is the class
public partial class StoreMonthlyTarget
{
public int STORE_KEY { get; set; }
public Nullable<short> RetailYear { get; set; }
public Nullable<short> RetailMonthID { get; set; }
public IEnumerable<F_MonthlyTargets> EmployeeTargets { get; set; }
}
In my Index view, I loop over each StoreMonthlyTarget. I then want to be able to click on any of these, and expand a PartialView that will show the attached list of F_MonthlyTargets.
So far, I have this
#foreach (var item in Model)
{
<table class="table">
<tr>
<th>Store</th>
<th>Month</th>
<th>
Expand
</th>
</tr>
<tr>
<td>#Html.DisplayFor(modelItem => item.Store)</td>
<td>#Html.DisplayFor(modelItem => item.RetailMonth)</td>
<td>
<input type="button" value="Expand" id="btnExpand"
onclick="javascript:BtnOnclick('#item.STORE_KEY'
,'#item.RetailYear'
,'#item.RetailMonthID');"/>
</td>
</tr>
</table>
<div id="divpopup" style="display:none">
#Html.Partial("_Store", item);
</div>
This passes the keys needed into a function
<script>
function BtnOnclick(Store, Year, Month) {
$.ajax({
type: 'POST',
url: '#Url.Content("~/Targets/PartialStore")',
data: {
store: Store,
year: Year,
month: Month
},
success: function (data) {
$('#divpopup').css("display", "block");
$('#btnExpand').css("display", "none");
$('#divpopup')[0].innerHTML = data;
}
});
}
function CollapseDiv() {
$('#divpopup').css("display", "none");
$('#btnExpand').css("display", "block");
}
</script>
That in turn, uses those keys to find the record needed and return it.
[HttpPost]
public ActionResult PartialStore(string store
, string year
, string month)
{
StoreMonthlyTarget s = (
from a in db.StoreMonthlyTargets
where a.RetailMonthID.ToString() == month
&& a.RetailYear.ToString() == year
&& a.STORE_KEY.ToString() == store
select a
).ToList()[0];
s.EmployeeTargets = (
from t in db.F_MonthlyTargets
where t.STORE_KEY == s.STORE_KEY
&& t.RetailYearID == s.RetailYear
&& t.RetailMonthID == s.RetailMonthID
select t
);
return PartialView("_Store", s);
}
The Partial view is then of type StoreMonthlyTarget, and contains a table that loops over the EmployeeTargets in here.
When I run it, i get my list of Stores. I then click on the button, and it retrieves the correct store. But every time it places the PartialView div after the first Store item. Not under the corresponding entry.
Anyone have any ideas as to why? Perhaps it's badly structured and there are better ways to do this?

Related

How to Pass ViewModel from MVC Html to Jquery?

I have a cshtml file which uses for each loops from the value of the model that comes from controller.My cshtml code is below:-
<tbody>
#foreach (var item model.TechnicianInvoice)
{
<tr>
<td>#item.Name</td>
#foreach (var rate in item.Rates)
{
<td class="td10"><span class="prefix">$</span><input type="text" class="form-control my-input" value="#rate.Rate.ToString("0.00")"></td>
}
<td><a class="btn btn-sm" href="#" data-action="ShowPayTableModal" data-id="#item.Id" name="#item.Name">view</a></td>
<td><a class="updateRate" data-id="#item">update</a></td>
</tr>
}
</tbody>
And model.TechnicianInvoice is a ist of view Model of:
public class TechnicianInvoiceViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<TechRateViewModel> Rates { get; set; }
}
public class TechRateViewModel
{
public int Id { get; set; }
public double Rate { get; set; }
}
Now What I need is when I click on the update button the current foreach value of model.Technician should be passed into jquery. Currently I am using data-id = #item but when I console log this value only shows TechnicianInvoiceViewModel . My jquery code is
$(".updateRate").on("click", function () {
var data = $(this).attr("data-id");
console.log(data);
});
How Can I do this?
The problem ist that you want to parse a C# model into Javascript.
When you do data-id = #item, C# is asked for the string value of the item. But what do you expect this value to be? The Id? Then you should go for data-id = #item.Id. But there are plenty other options, it's also possible to return for example a JSON by implementing #item.getJSON() ...
Long story told short: You need to specify which value of your item you want to parse to your JavaScript ;)

How to display data from model in td based off select list item in another td

I have a separate TR and inside, a TD from the rest of my table. I have some data in my model that contains a list of strings, and also a list of IDs (not sure if I need the list of IDS for this) and I would like to display on the lower Tr's td a specific part of the list, based off of the selection of a SelectListItem in the table row's td above it.. i.e. If a user select's a list item of X, I want the TD below to display "X's help description" (which like I mentioned earlier, is being stored inside a list of strings in my model)
I am not sure if I should be doing this in Razor, Javascript, or something else. Can anyone give me some tips? Below is some code.
View:
<div class="row">
<div class="col-md-12" style="overflow-y:scroll">
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>Terminal</th>
<th>Command</th>
<th>Command Value</th>
<th> </th>
</tr>
</thead>
<tbody>
<tr>
<td>#Html.DropDownListFor(o => o.TerminalsDDL, Model.TerminalsDDL, new { id = "ddlTerminalID", #class = "form-control" })</td>
<td>#Html.DropDownListFor(o => o.TerminalCommandLookupsDDL, Model.TerminalCommandLookupsDDL, new {id = "ddlCommandValue", #class = "form-control" })</td>
<td>#Html.TextBoxFor(o => o.UserEnteredTerminalCommands, new { Class = "form-control", Id = "cmdValueValue"})</td>
<td> <input id="btnSaveTerminalCommand" type="button" value="Insert" class="btn btn-primary" /> </td>
</tr>
<tr>
<td colspan="4" id="helpDescript">#Html.DisplayFor(model => model.HelpDescription)</td>
</tr>
</tbody>
</table>
</div>
</div>
VM:
public TerminalCommandVM()
{
//Terminals Drop Down List
TerminalsDDL = new List<SelectListItem>();
//Terminal Commands Drop Down List
TerminalCommandLookupsDDL = new List<SelectListItem>();
//Terminal Command Values list
TerminalCommandValues = new List<SelectListItem>();
}
public TerminalCommand TerminalCommand { get; set; }
public List<TerminalCommand> TerminalCommands { get; set; }
[Display(Name = "Terminal ID")]
public List<SelectListItem> TerminalsDDL { get; set; }
[Display(Name = "Command")]
public List<SelectListItem> TerminalCommandLookupsDDL { get; set; }
public List<SelectListItem> TerminalCommandValues { get; set; }
public string UserEnteredTerminalCommands { get; set; }
public List<string> HelpDescription { get; set; }
public List<int> HelpDescriptionID { get; set; }
}
The DisplayFor I want populated is the one with the ID = "helpDescript", and the select list item that should dictate which help descript is displayed has the ID = "ddlCommandValue".
As of now, helpDescript is displaying the entire list (obviously).
If anyone needs any other code or more information, please let me know.
Try the following. In the dropdown change event call the action to display the value and in the success function display the value in label
$("#ddlCommandValue").change(function () {
var obj = {
valueToPass: $(this).val()
};
$.ajax({
url: '/Home/GetValueToDisplayInlabel',
contentType: 'application/json; charset=utf-8',
type: 'POST',
data: JSON.stringify(obj),
cache: false,
success: function (result) {
$("#helpDescript").html(result);
},
error: function () {
alert("Error");
}
});

Get values from an Html table and map to a C# List using jQuery

I have an MVC project where I'm rendering the data using a table in my partial view as:
<table id="tblUserSettings" class="table table-bordered CmtTable">
<thead>
<tr>
<th>User Name</th>
<th>Country</th>
<th>System (s)</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
#if (Model == null)
{
<tr></tr>
}
else
{
foreach (var item in Model)
{
<tr>
<td><input type="hidden" id="usrId" value="#item.UserId" />
#item.UserName</td>
<td> <input type="hidden" id="usrCountryKey" value="#item.UserCountryKey" style="display:none"/>
#item.UserCountryName</td>
<td> <input type="hidden" id="usrSourceSystemKey" value="#item.UserSourceSystemKey" />
#item.UserSourceSystemDescription</td>
<td><a onclick='DeleteUserSettingsRow();'><i class='fa fa-times'></i> Delete</a><a onclick='EditUserSettingsPopup();'><i class='fa fa-pencil'></i> Edit</a></td>
</tr>
}
}
</tbody>
</table>
I'm going to save the values from this table into the database and need to call the AddUserSettingsaction method in controller as:
[HttpPost, Route("AddUserSettings")]
public ActionResult AddUserSettings(IEnumerable<UserSettings> request)
{
AddUserSettingsRequest apiRequest = null;
return View();
}
The model of UserSettings is as follows:
public class UserSettings
{
public UserSettings();
public string UserId { get; set; }
public string UserName { get; set; }
public string UserCountryKey { get; set; }
public string UserCountryName { get; set; }
public string UserSourceSystemKey { get; set; }
public string UserSourceSystemDescription { get; set; }
}
I need to save the data from the table (including the hidden fields) into the database using jQuery, so I have created a function and calling it as:
<button type="button" id="btnAdd" onclick="SaveUserSettings();"><i class="fa fa-save"></i> Save</button>
function SaveUserSettings()
{
debugger;
var userSettings = [];
$("table#tblUserSettings tr").each(function (i, v) {
userSettings[i] = [];
$(this).children('td').each(function (ii, vv)
{
userSettings[i][ii] = $(this).text();
});
})
alert(userSettings);
$.ajax({
url: '#Url.Action("AddUserSettings", "Admin")',
type: "POST",
contentType: "application/json;",
data: JSON.stringify(userSettings),
success : function (result)
{
//alert(result);
},
error: function (result)
{
//alert(result);
}
});
}
With the above SaveUserSettings() function, I can get the values which are not hidden, but I need to create an array which contains the hidden properties as well and can be sent with the ajax request to the controller as the parameter. How can I get the hidden fields and create an array mapped to the IEnumerable request of my controller?
Provided the javascript is in the partial view, you can set your userSettings directly against the model, for example:
var userSettings = #Html.Raw(JsonConvert.SerializeObject(Model));
This will then serialize your object into JSON and it will be passed to the browser, where you can then use it to pass it back to the server in jQuery without having to go through each row in the table.

Knockout click handler not working for table created using ko.computed

I am trying to bind a table to a drop down menu. Here are my requirements:
The drop down contains a list of MENUs.
A MENU can have many associated MODULES which are displayed in the table for the selected menu item.
A MODULE does not have to be associated to a MENU item, in which case it is consider SHARED between the selected menu items.
This is an example of the screen I have:
In the example, you can see the CommonShellModule is shared, which means that CommonShellModule will be listed for all menus.
This is what I have so far:
http://jsfiddle.net/fag62z1x/
ViewModel:
var ModuleIndexViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
self.selectedMenu = ko.observable();
self.selectedMenuModules = ko.computed(function () {
if (self.selectedMenu()) {
//display modules for the selected menu or where they are not associated to a menu
var modules = ko.utils.arrayFilter(self.Modules(), function (module) {
return self.isModuleAssociatedToCurrentMenuSelection(module) || self.isSharedModule(module);
});
//return a sorted list of modules
return modules.sort(function (left, right) {
return left.Name() > right.Name() ? 1 : -1;
});
}
return null;
});
self.isModuleAssociatedToCurrentMenuSelection = function (module) {
if (self.selectedMenu()) {
return module.MenuId() == self.selectedMenu().Id()
}
return false;
}
self.isSharedModule = function (module) {
return module.MenuId() == null;
}
self.handleSharedCheckboxClick = function (module) {
if (self.isSharedModule(module)) {
module.MenuId(null);
}
else {
module.MenuId(self.selectedMenu().Id());
}
return true; //allow the default click event action
}
}
Models:
The models that are being mapped from (these models are serialized and passed in to this view model):
public class IndexViewModel
{
public IEnumerable<MenuViewModel> Menus { get; set; }
public IEnumerable<ModuleViewModel> Modules { get; set; }
}
public class MenuViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ModuleViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public int? MenuId { get; set; }
}
View:
...
<div class="form-group top-buffer">
<label class="control-label" for="menu">Menu</label>
<select id="menu" class="form-control" data-bind="options: Menus, optionsText: 'Name', value: selectedMenu"></select>
</div>
<div class="row top-buffer">
<div class="col-lg-12">
<table class="table table-striped">
<thead>
<tr>
<th class="col-lg-6">Module Name</th>
<th class="col-lg-3">Shared</th>
<th class="col-lg-3"></th>
</tr>
</thead>
<tbody data-bind="foreach: selectedMenuModules">
<tr>
<td data-bind="text: Name"></td>
<td><input type="checkbox" data-bind="checked: $parent.isSharedModule($data), click: $parent.handleSharedCheckboxClick" /></td>
<td>Edit | Details | Delete</td>
</tr>
</tbody>
</table>
</div>
</div>
...
The problem is that javascript handleSharedCheckboxClick is not working and I have no idea why. It is being executed each time a shared checkbox is clicked but when I update the module value in this function, the knockout computed is not being recalculated and so the table holds the previous values.
I would appreciate any suggestions as to what I may do to resolve this.
Edit: My bad, played around a bit with the findle and that module value is being observed, looks like the problem is here:
self.handleSharedCheckboxClick = function (module) {
if (self.isSharedModule(module)) {
module.MenuId(null);
}
If the module is shared the value is already null so the value is not being changed, modifying that code adding a ! like this:
self.handleSharedCheckboxClick = function (module) {
if (!self.isSharedModule(module)) {
module.MenuId(null);
}
make it work as you wanted, shared status saved between menu item value updates (click the checked checkbox and the line is no more shared but binded to the current menu item, click it again and it goes back to shared)
jsfindle: http://jsfiddle.net/yj1s9qod/

How to get partialview data in this situation?

in my MVC 4 web apps, I have a model data with a jQuery tab table, in each tab, I have some embedded tables which works as one to many relationship with this main tab table, in main tab table, I have a button ("Add"), when click it, a row of data field beside it with the data user entered will be added to the children table (which is a partialview) embedded in one of tab table, now, the code behind the button and controller saved the data to table, but the current embedded does not updated to show the new added table data at all. I appreciate anybody can help me out this problem.
This is the javascript behind Add button,
function AddMeal() {
//Build your Product
var product = { "MDate": $("#MDate").val(), "MRegion": $("#MRegion").va(), "MBAmount": $("#MBAmount").val(),
"MLAmount": $("#MLAmount").val(), "MDAmount": $("#MDAmount").val(), "FINtravelID": $("#FINtravelID").val() };
$.post('#Url.Action("Edit1","Travel")', product, function (data) {
if (data == null) {
location = location.href;
}
else {
//Populate your MealTable element with the results of your Partial View
$('#MealTable').html(data);
}
});}
This is controller:
[HttpPost]
public ActionResult Edit1( Meal product)
{
db.Meals.Add(product);
db.SaveChanges();
ViewBag.SaveMeal = "1";
return PartialView("_MealPartial", product);
}
this is the razor view related to this question,
#model Travelmvc.Models.FINtravel
<input type="button" value="Add" id="AddButton1" onclick="AddMeal()"/>
<div id="MealPartial">
#Html.Partial("~/Views/Shared/_MealPartial.cshtml")
</div>
This is partialview details codes:
#model Travelmvc.Models.FINtravel
<table class="MealT" id="MealTable" >
#if (Model != null) {
if (Model.Meals != null){ //here this line code always returns null, that is why the
read data created in SQL table can not display in the loop
foreach (var item in Model.Meals)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.MDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.MRegion)
</td>
<td>
#Html.DisplayFor(modelItem => item.MBAmount)
</td>
<td>
#Html.DisplayFor(modelItem => item.MLAmount)
</td>
<td>
#Html.DisplayFor(modelItem => item.MDAmount)
</td>
<td>
#Html.DisplayFor(modelItem => item.MTAmount)
</td>
<td>
#Html.DisplayFor(modelItem => item.MProduct)
</td>
<td>
#Html.DisplayFor(modelItem => item.MRC)
</td>
<td>
#Html.DisplayFor(modelItem => item.MLocation)
</td>
<td>
#Html.DisplayFor(modelItem => item.MPOC)
</td>
<td>
aa
</td>
<td>
ff
</td>
</tr>
}
...
and this is the model of FINtravel and Meal.
public class FINtravel
{
public int FINtravelID { get; set; }
public string ClaimNo { get; set; }
public virtual ICollection<Meal> Meals { get; set; }
}
public class Meal
{
public int MealID { get; set; }
public int FINtravelID { get; set; }
public virtual FINtravel FINtravel { get; set; }
}
So my question is why the meal data created in Edit1 controller not display in razor view (partialview)? But I can see the data was saved in SQL table from back end.
can anybody help me out of this problem? thank a lot in advance.
you can try change your controller code like this:
public ActionResult Edit1( Meal product)
{
db.Meals.Add(product);
db.SaveChanges();
product = db.Meals.Find(product.MealID);//get saved product with filled property FINtravel
ViewBag.SaveMeal = "1";
return PartialView("_MealPartial", product.FINtravel);
}
fintravel = db.FINtravel.Find(product.FINtravelID);//
return PartialView("_MealPartial", fintravel);
solves the problem, thanks for all helps in this thread

Categories