ASP.NET MVC 3 Logic for dynamically generated dropdown controls - asp.net

I have a form that I generate dynamically, based on the amount of rows in an Excel file that I upload. Where can I add logic that looks through the Description string and sets the dynamically generated dropdownlist to a specified value, based on text in the Description?
I want to add a list of checks, such as:
if "blabla" is in the Description string, set dropdownlist value to 4.
Do I have to do this in Javascript? Cause that doesn't feel that clean to me. I'd prefer my business logic be handled in my Controller, but I'm not sure how that would go in this design.
My code looks like this:
Preview page, which basically just links to my Editor Template named Transaction:
#using (Html.BeginForm("Preview", "Import", FormMethod.Post))
{
<table border="1" style="border-color: #FFFFFF">
#Html.EditorFor(m => m.Transactions, new { Categories = Model.Categories })
</table>
<input id="btnSave" type="submit" value="Opslaan in database" />
}
In this Editor Template transaction, I display some static data, and a textbox and dropdownlist for each row in the Excel that I have previously uploaded in another page:
<tr>
<td style="width: 40px; padding: 5px; background-color: #CurrencyHelper.GetCurrencyColor(Model.Amount)" align="right" nowrap="nowrap">#Html.Raw(CurrencyHelper.GetCurrency(Model.Currency, Model.Amount))
</td>
<td style="white-space: nowrap; padding: 5px;">#Model.DateTime.ToString("dd-MM-yyyy")
</td>
<td style="padding: 5px;">#Model.Description
</td>
<td style="padding: 5px;">#Html.EditorFor(m => m.ShortDescription)
</td>
<td style="padding: 5px;">#Html.DropDownListFor(m => m.CategoryId, new SelectList(ViewData["Categories"] as IEnumerable<Category>, "CategoryId", "Name"))
</td>
</tr>
My controller, which enters the data in the View Model:
//Attach unique Transactions and Categories to ViewModel
var viewModel = new ImportViewModel()
{
Transactions = uniqueTransactions.ToList(),
Categories = categoryRepository.GetCategories().OrderBy(c => c.Name).ToList()
};

Static Binding
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to the Training Courses...";
List objcourses = new List();
objcourses.Add("Asp.Net");
objcourses.Add("MVC");
objcourses.Add("WCF");
objcourses.Add("WPF");
objcourses.Add("C#.Net");
ViewBag.Courses = new SelectList(objcourses);
return View();
}
}
#{
ViewBag.Title = "Home Page";
}
Index
#using(#Html.BeginForm(“Index”,”Home”,FormMethod.Get)) {
Courses List; #Html.DropDownList(“Courses“)
}
Dynamic Binding
public class HomeController : Controller
{
public ActionResult Index()
{
private MovieDBContext db = new MovieDBContext();
var GenreLst = new List();
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.Courses = new SelectList(GenreLst);
return View();
}
}
#{
ViewBag.Title = "Home Page";
}
Index
#using(#Html.BeginForm("Index","Home",FormMethod.Get)) {
Courses List; #Html.DropDownList("Courses")
}

Related

How to Post correctly w/ List<Model> from the view to the controller? [duplicate]

I have a HTML table as below in my View:
<table id="tblCurrentYear">
<tr>
<td>Leave Type</td>
<td>Leave Taken</td>
<td>Leave Balance</td>
<td>Leave Total</td>
</tr>
#foreach (var item in Model.LeaveDetailsList)
{
<tr>
<td>#Html.TextBoxFor(m => item.LeaveType, new { width = "100" })</td>
<td>#Html.TextBoxFor(m => item.LeaveTaken, new { width = "100" })</td>
<td>#Html.TextBoxFor(m => item.LeaveBalance, new { width = "100" })</td>
<td>#Html.TextBoxFor(m => item.LeaveTotal, new { width = "100" })</td>
</tr>
}
</table>
I want to iterate through all the html table rows and insert the values in ADO.NET DataTable.
Simple speaking, converting HTML Table to ADO.NET DataTable.
How to extract values from HTML Table and insert into ADO.NET DataTable?
The view is based on the following model
public class LeaveBalanceViewModel
{
public LeaveBalanceViewModel()
{
this.EmployeeDetail = new EmployeeDetails();
this.LeaveBalanceDetail = new LeaveBalanceDetails();
this.LeaveDetailsList = new List<LeaveBalanceDetails>();
}
public EmployeeDetails EmployeeDetail { get; set; }
public LeaveBalanceDetails LeaveBalanceDetail { get; set; }
public List<LeaveBalanceDetails> LeaveDetailsList { get; set; }
}
In order to bind to a model on post back, the name attributes of the form controls must match the model properties. Your use of a foreach loop does not generate the correct name attributes. If you inspect the html you will see multiple instances of
<input type="text" name="item.LeaveType" .../>
but in order to bind to your model the controls would need to be
<input type="text" name="LeaveDetailsList[0].LeaveType" .../>
<input type="text" name="LeaveDetailsList[1].LeaveType" .../>
etc. The easiest way to think about this is to consider how you would access the value of a LeaveType property in C# code
var model = new LeaveBalanceViewModel();
// add some LeaveBalanceDetails instances to the LeaveDetailsList property, then access a value
var leaveType = model.LeaveDetailsList[0].LeaveType;
Since your POST method will have a parameter name (say model), just drop the prefix (model) and that's how the name attribute of the control must be. In order to do that you must use either a for loop (the collection must implement IList<T>)
for(int i = 0; i < Model.LeaveDetailsList.Count; i++)
{
#Html.TextBoxFor(m => m.LeaveDetailsList[i].LeaveType)
....
}
or use a custom EditorTemplate (the collection need only implement IEnumerable<T>)
In /Views/Shared/EditorTemplates/LeaveBalanceDetails.cshtml
#model yourAssembly.LeaveBalanceDetails
<tr>
<td>#Html.TextBoxFor(m => m.LeaveType)</td>
....
</tr>
and then in the main view (not in a loop)
<table>
.... // add headings (preferably in a thead element
<tbody>
#Html.EditorFor(m => m.LeaveDetailsList)
</tbody>
</table>
and finally, in the controller
public ActionResult Edit(LeaveBalanceViewModel model)
{
// iterate over model.LeaveDetailsList and save the items
}
With respect to your requirement, try this
jQuery(document).on("change", ".DDLChoices", function (e) {
var comma_ChoiceIds = '';
var comma_ChoicesText = '';
$('input[class="DDLChoices"]').each(function (e) {
if (this.checked) {
comma_ChoiceIds = comma_ChoiceIds + $(this).val() + ',';
comma_ChoicesText = comma_ChoicesText + $(this).parent('label').parent() + ',';
}
});
$('#ChoiceIds').val(comma_ChoiceIds);
$('#ChoiceText').val(comma_ChoicesText);
});
#using (Html.BeginForm("Actionname", "Controllername", FormMethod.Post, new { id = "frmChoices" }))
{
#Html.HiddenFor(m => m.ChoiceText, new { #id = "ChoiceText" })
#Html.HiddenFor(m => m.ChoiceIds, new { #id = "ChoiceIds" })
<div class="form-group">
<div>
<table>
<tr>
<th>Name</th>
<th>Selected</th>
</tr>
#foreach (var item in #Model.Choices)
{
<tr>
<td> <label>#item.ChoicesText</label> </td>
<td> <input class="DDLChoices" value="#item.ChoiceIds" type="checkbox" /></td>
</tr>
}
</table>
</div>
<input type="button" value="Submit" onclick="return ChoicesPoster.passChoices()"
</div>
}

ASP.NET MVC DropDownListFor

My model looks something like this. It gets populated with items at some point by a stored procedure.
public class myModel
{
public List<SelectListItem> myList { get; set; }
public List<myModel> modelList { get; set; }
}
Here is my Controller.
[HttpGet]
public ActionResult getMyListItems()
{
var viewModel = new myModel();
viewModel.myList = viewModel.getMyList();
viewModel.modelList = viewModel.getMyModelList();
return View(viewModel);
}
Here is my view so far. I'm building a dropdownlist so the user can filter the contents of modelList. Kind of like a WHERE clause in an SQL query. Once the user selects the item and clicks the submit button, it applies the filter? Or would this happen after an item is actually selected in the dropdown without the need of a button click event?
#model SWAM2.Models.EmployeeOfcSpecKnow
#using CommonCode.HtmlHelpers;
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="editor-label">
Filter by Column1
</div>
<div class="editor-field">
#Html.DropDownListFor(model => Model.Column1, Model.myList, new { style = "width:400px" })
#Html.ValidationMessageFor(model => model.Column1)
</div>
<div class="toppad10">
<input type="submit" value="Apply Filter" />
</div>
<table class="grayTable rowStriping">
<thead>
<tr>
<th>Column1</th>
<th>Column2</th>
<th>Column3</th>
</tr>
</thead>
<tbody>
#foreach (var item in #Model.modelList)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Column1)
</td>
<td>
#Html.DisplayFor(modelItem => item.Column2)
</td>
<td>
#Html.DisplayFor(modelItem => item.Column3)
</td>
</tr>
}
</tbody>
</table>
}
You can achieve it by using dropdownlist change event.
refer :
on select change event - Html.DropDownListFor
onchange event for html.dropdownlist
One way to do this is by creating an action on your controller that returns a PartialViewResult and then using AJAX to asynchronously call that action and get the newly filtered list. So for example, you would create an action like this:
public PartialViewResult GetFilteredItems(string filter)
{
var viewModel = new myModel();
viewModel.myList = viewModel.getMyList();
viewModel.modelList = viewModel.getMyModelList();
viewModel.ApplyFilter(filter);
return PartialView(viewModel);
}
and call it using javascript, I prefer jQuery:
$("#dropDownListIdHere").change(function () {
$.ajax({
url: "#Url.Action("GetFilteredItems")",
method: "GET",
data: { filter: $(this).val() },
success: function (result) {
$("#listHolderIdHere").html(result);
}
})
});
Note that with this method, you'd need to create a partial view file (named GetFilteredItems if you don't want to specify a name in the controller action) that would contain the table to be rendered with the filtered items. You would also need to assign an ID to the dropdown and to some sort of container that you'd place your partial view into.

Click on thumbnail and route to Controller method

I written a view that display a table listing of thumbnails. It is working now. Next I am writing another view, which display a new view with detailed information of the
item thumbnail which was clicked. Right now, I am not sure how to add the information which makes the thumbmail clickable that routes to the GetDetailInfo Controller method
that return a detailed information view.
Here is my thumbnail table list code:
<table class="table-responsive" width="100%">
<tbody>
#foreach (var productGroup in Model.Select((e, i) => new { Product = e, Grouping = (i / 4) }).GroupBy(e => e.Grouping))
{
<tr>
#foreach (var product in productGroup)
{
<td>
<div><br /></div>
<img src=#product.Product.Thumbnail style="width: 100px; height: 100px" />
<div><br /></div>
</td>
}
</tr>
}
</tbody>
</table>
Here is my the Controller method which I am writing which is called when the thumbnail is click and return a detailed view.
[MvcSiteMapNode(Title = "Item Detail", ParentKey = "Item-Home", Key = "Item-Detail", PreservedRouteParameters = "itemId")]
[Route("~/item/{itemId}/detail")]
public async Task<ActionResult> GetDetailInfo(int itemId)
{
var result = await ItemService.GetDetailInfo(contentId) );
return View(result.Dto);
}
I am not sure how to route the click on thumnbnail to this controller method to return a new detail info view. Any help is appreciated. Thanks
There are two common ways to make an image as a link.
1 - Using a link tag.
<a href="#Url.Action("GetDetailInfo", "Controller", new { itemId = product.Product.Id })">
<img src="#product.Product.Thumbnail" style="width: 100px; height: 100px" />
</a>
2 - Using a custom Html Helper
Source:
public MvcHtmlString ActionImage(this HtmlHelper htmlHelper,
string controller,
string action,
object routeValues,
string imagePath,
string alternateText = "",
object htmlAttributes = null)
{
var anchorBuilder = new TagBuilder("a");
var url = new UrlHelper(HtmlHelper.ViewContext.RequestContext);
anchorBuilder.MergeAttribute("href",url.Action(action,controller,routeValues));
var imgBuilder = new TagBuilder("img");
imgBuilder.MergeAttribute("src", url.Content(imagePath));
imgBuilder.MergeAttribute("alt", alternateText);
var attributes = (IDictionary<string, object>) HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
imgBuilder.MergeAttributes(attributes);
string imgHtml = imgBuilder.ToString(TagRenderMode.SelfClosing);
anchorBuilder.InnerHtml = imgHtml;
return MvcHtmlString.Create(anchorBuilder.ToString());
}
Using:
#Html.ActionImage("Controller", "GetDetailInfo", new { itemId = #product.Product.Id }, "#product.Product.Thumbnail", "alternateText")
Asp.Net MVC Html Helper to create link with an image
The first method is handy - and rightly commented by Bardicer in your question, - but the second one is more practical.

model binding and sumbit button mvc4

I have multiple forms on a cshtml page. The first of these forms binds correctly, but I have not been able to get the second one to bind. Each time I click the submit button the passed values are all 0 or null (int and stings respectively)
My model contains two objects. AccountInfo and a list of AccountSettings:
public class AccountDetailViewRequest
{
public Account AccountInfo { get; set; }
public List<AccountSettings> Settings { get; set; }
}
public class AccountSettings
{
public int AccountID;
public string Name;
public string Value;
public int ID;
}
My actions. The first action that works correctly, but the second does not.
public ActionResult UpdateAccountDetails(Account model)
{...}
public ActionResult DeleteSetting(AccountSettings model)
{...}
In my cshtml page I have this block which works correctly
#using (Html.BeginForm("UpdateAccountDetails", "Account"))
{
<input type = "hidden" name = "AccountInfo.AcctID" value = #Model.AccountInfo.AcctID />
<table>
<tr>
<td>Company #Html.TextBox("AccountInfo.Company")</td>
<--More text boxes here-->
</tr>
<tr>
<td><input name="Save" type="submit" value="Save Changes"/></td>
</tr>
</table>
}
Later in the page I have this block. When I use the associated submit button the function is called, but the parameters are all 0 or null regardless of the content of the text boxes. The boxes populate properly from my DB when I view the source of the code.
#for (int index = 0; index < Model.Settings.Count; index++)
{
var setting = Model.Settings[index];
using (Html.BeginForm("DeleteSetting", "Account"))
{
<tr>
<td>#Html.TextBox("Name", #Model.Settings[index].Name)</td>
<td>#Html.TextBox("Value", setting.Value)</td>
#Html.Hidden("ID", setting.ID)
#Html.Hidden("AccountID", Model.AccountInfo.AcctID)
<td><input type="submit" value="Delete"/></td>
</tr>
}
}
I've tried a number of different variations on the syntax but cannot seem to get it right.
Suggestions, solutions and resources welcome.
Thank you in advance.
Try something like this
<tr>
<td>#Html.TextBox("AccountSettings.Name", #Model.Settings[index].Name)</td>
<td>#Html.TextBox("AccountSettings.Value", setting.Value)</td>
#Html.Hidden("AccountSettings.ID", setting.ID)
#Html.Hidden("AccountSettings.AccountID", Model.AccountInfo.AcctID)
<td><input type="submit" value="Delete"/></td>
</tr>
Your view model is AccountDetailViewRequest of which Name, Value, etc aren't properties.
It'd be tidier if you did something like this:
#foreach (var setting in Model.Settings)
{
using (Html.BeginForm("DeleteSetting", "Account"))
{
<tr>
<td>#Html.TextBoxFor(m => setting.Name)</td>
<td>#Html.TextBoxFor(m => setting.Value)</td>
#Html.HiddenFor(m => setting.ID)
#Html.HiddenFor(m => setting.AcctID)
<td><input type="submit" value="Delete"/></td>
</tr>
}
}
Note that I improvised the above code and you might need to tweak it to work

Not able to update the comment module on the mvc view using ajax call without refreshing the page

This is a check & mate situation for me...
I am using mvc 3.
I am trying to make a post and comment module on a single view. below is the code for the view and controller. I am able to get the post and all the comments on load but once I add a new comment through an AJAX call its is saved to the correct table in DB but I am not understanding how to update it on view without refreshing the page...
//model
public class PostViewModel
{
public bool? IsActive
{ get; set; }
public string PostDescription
{ get; set; }
...
public List<PostCommentModel> objPostCommentInfo { get; set; }
}
//Post Controller
DBEntities1 db = new DBEntities1();
public ActionResult Index(int ID)
{
int id = Convert.ToInt32(ID);
PostViewModel objPostViewModel = new PostViewModel();
List<PostViewModel> lstobjPostViewModel = new List<PostViewModel>();
PostCommentModel objPostCommentModel;
List<PostCommentModel> lstobjPostCommentModel = new List<PostCommentModel>();
var objPost = (from x in db.PostInfoes
where x.PostId == id
select x).ToList();
var objPostComment = (from y in db.PostCommentInfoes
where y.PostId == id
orderby y.CommentId descending
select y).ToList();
foreach (var x in objPost)
{
objPostViewModel.PostID = x.PostId;
objPostViewModel.IsActive = x.IsActive;
objPostViewModel.PostTitle = x.PostTitle;
objPostViewModel.PostDescription = x.PostDescription;
lstobjPostViewModel.Add(objPostViewModel);
}
foreach (var y in objPostComment)
{
objPostCommentModel = new PostCommentModel();
objPostCommentModel.PostId = y.PostId;
objPostCommentModel.IsActive = y.IsActive;
objPostCommentModel.CommentBody = y.CommentBody;
lstobjPostCommentModel.Add(objPostCommentModel);
}
objPostViewModel.objPostCommentInfo = lstobjPostCommentModel;
return View(lstobjPostViewModel);
}
//view
#model IEnumerable<MVCProjectModels.PostViewModel>
<table border="1">
#foreach (var item in Model)
{
<tr>
<td>
<text>Created By:</text>
#Html.DisplayFor(modelItem => item.PostDescription)
</td>
<td rowspan="2">
#Html.DisplayFor(modelItem => item.PostDescription)
</td>
</tr>
.....
}
</table>
<table>
<tr>
<td>
<textarea cols="10" rows="5" id="txtComment"></textarea>
</td>
</tr>
<tr>
<td>
<input id="btnPostComment" type="button" value="Post Comment" />
</td>
</tr>
</table>
<table border="1">
#foreach (var item1 in Model)
{
foreach (var item2 in item1.objPostCommentInfo)
{
<tr>
<td colspan="2">
#Html.DisplayFor(modelItem => item2.CommentBody)
</td>
</tr>
}
}
</table>
//Ajax call to update the comment (The comments gets saves to the database but I am not finding anyway to update it on the UI or View)
<script type="text/javascript">
$("#btnPostComment").click(function () {
var commentBody = $("#txtComment").val();
postComment(commentBody);
});
function postComment(commentBody) {
$.ajax({
url: "/Post/postComment", // this controller method calls a store procedure to insert the new comment in the database.
type: 'POST',
data: {
Comment: commentBody,
ID: 6
},
success: function (result) {
},
error: function () {
alert("error");
}
});
}
</script>
Please let me know if I am doing any major designing mistakes in the above module. I am new to mvc so just trying to do this by reading some books and articles so not sure if this is correct way of achieving such results. thanks
You need to name your table for easier reference:
<table border="1" id="postList">
On your view you are writing a name of a user <text>Created By:</text> but I don't see that in the model. So assuming that is saved in a session or you can retrieve it in your controller you can do something like:
public ActionResult PostComment(YourModel input){
// everything went well
// you get this from a session or from the database
var username = "the creator";
return Json(new { success = true, username});
}
On success of your ajax call:
success: function (result) {
if (result.success) {
$("#postList").append('<tr><td><text>Created By:</text>' +
result.username + '</td><td rowspan="2">' +
commentBody + '</td>');
</tr>
}
}
It will be cool though if instead of concatenating the tr string that you read it from a template and insert the necessary values. Or you can use other tools like knockout to do the binding on the client side. But that is for another question I guess.
You could just .prepend() the new comment text to the comments table in the success callback of your AJAX call:
success: function (result) {
// give your comments table a class="comments" so that the following
// selector is able to match it:
$('table.comments').prepend(
$('<tr/>', {
html: $('<td/>', {
text: commentBody
})
})
);
}

Resources