Click on thumbnail and route to Controller method - asp.net

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.

Related

ASP.Net MVC: Css file reference's style is not applying on html when partial view returning as string

I have a small partial view which has a css class reference at. from one of my action i am converting my partial view to string but i noticed css file reference is not working means css style, color nothing is applying on html inside partial view when check the output.
So when i hard code the same css style instead of css file reference in page then it worked.
So my question is if i refer css file in partial view instead of hard code style inside partial view then what i need to do as a result
style should be applied on html when i will check the partial view string ?
The below routine return the string of partial view
public static class StringUtilities
{
public static string RenderViewToString(System.Web.Mvc.ControllerContext context, string viewPath, object model = null, bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
{
viewEngineResult = ViewEngines.Engines.FindPartialView(context, viewPath);
}
else
{
viewEngineResult = ViewEngines.Engines.FindView(context, viewPath, null);
}
if (viewEngineResult == null)
{
throw new FileNotFoundException("View cannot be found.");
}
// get the view and attach the model to view data
var view = viewEngineResult.View;
context.Controller.ViewData.Model = model;
string result = null;
using (var sw = new StringWriter())
{
var ctx = new ViewContext(context, view, context.Controller.ViewData, context.Controller.TempData, sw);
view.Render(ctx, sw);
result = sw.ToString();
}
return result.Trim();
}
}
this way i am getting string of the partial view
List<Student> studentsVM = new List<Student>
{
new Student {ID=1,FirstName="Joy", LastName="Roy", FavouriteGames="Hocky"},
new Student {ID=2,FirstName="Raja", LastName="Basu", FavouriteGames="Cricket"},
new Student {ID=3,FirstName="Arijit", LastName="Banerjee",FavouriteGames="Foot Ball"},
new Student {ID=4,FirstName="Dibyendu", LastName="Saha", FavouriteGames="Tennis"},
new Student {ID=5,FirstName="Sanjeeb", LastName="Das", FavouriteGames="Hocky"},
};
var viewToString = StringUtilities.RenderViewToString(ControllerContext, "~/Views/Shared/_Report.cshtml", studentsVM, true);
This is my partial view where i refer a css file whose style definition is not getting applied on partial view html.
#model List<TestBOL.Models.Student>
<html>
<head>
<link href="~/Content/Student.css" rel="stylesheet" />
</head>
<body>
<table class="table" id="student">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>First Name </th>
<th>Last Name </th>
<th>Favourite Game</th>
</tr>
</thead>
#foreach (var d in Model)
{
<tr>
<td>#d.ID</td>
<td>#d.FirstName</td>
<td>#d.LastName</td>
<td>#d.FavouriteGames</td>
</tr>
}
</table>
</body>
</html>
Guide me what to do. thanks

Changing number of table elements displayed on page resets my search results

I have numerous data displayed in a table, let's say a long list of users (first name & last name), so I set up a paging feature to display the elements by pages via the PagedList NuGet package. I was inspired by this tutorial: https://learn.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application
I implemented a drop-down list in my view, so that I can directly choose the number of elements to display per page. I managed to include a jQuery script that makes the page size update whenever the drop-down list has a new selected value.
Using the mentioned tutorial , I also added a search feature: submitting a string in a search form allows to filter the data.
My problem is: changing the page size by selecting a new value in the drop-down list after having done a search doesn't work: the search results are reset, all the entries being displayed instead. I guess I forgot to pass some parameter somewhere but I just can't figure out where...
Here is my controller:
public ActionResult Index(string sortOrder, string currentFilter, string searchString, int? page, int? PageSize)
// Sort order is passed to view in order to keep it intact while clicking in another page link
ViewBag.CurrentSort = sortOrder;
// Ascending or descending sorting by first or last name according to sortOrder value
ViewBag.LastNameSortParm = String.IsNullOrEmpty(sortOrder) ? "lastname_desc" : "";
ViewBag.FirstNameSortParm = sortOrder == "firstname" ? "firstname_desc" : "firstname";
// Not sure here
if (searchString == null)
{
searchString = currentFilter;
}
// Pass filtering string to view in order to maintain filtering when paging
ViewBag.CurrentFilter = searchString;
var users = from u in _db.USER select u;
// FILTERING
if (!String.IsNullOrEmpty(searchString))
{
users = users.Where(u => u.lastname.Contains(searchString)
|| u.firstname.Contains(searchString)
}
// Ascending or descending filtering by first/last name
switch (sortOrder)
{
case "lastname": // Ascending last name
users = users.OrderBy(u => u.lastname);
break;
case "lastname_desc": // Descending last name
users = users.OrderByDescending(u => u.lastname);
break;
case "firstname": // Ascending first name
users = users.OrderBy(u => u.firstname);
break;
case "firstname_desc": // Descending first name
users = users.OrderByDescending(u => u.firstname);
break;
default:
users = users.OrderBy(u => u.lastname);
break;
}
// DROPDOWNLIST FOR UPDATING PAGE SIZE
int count = _db.USER.OrderBy(e => e.Id).Count(); // Total number of elements
// Populate DropDownList
ViewBag.PageSize = new List<SelectListItem>() {
new SelectListItem { Text = "10", Value = "10", Selected = true },
new SelectListItem { Text = "25", Value = "25" },
new SelectListItem { Text = "50", Value = "50" },
new SelectListItem { Text = "100", Value = "100" },
new SelectListItem { Text = "All", Value = count.ToString() }
};
int pageNumber = (page ?? 1);
int pageSize = (PageSize ?? 10);
ViewBag.psize = pageSize;
return View(users.ToPagedList(pageNumber, pageSize));
}
And my Index.cshtml view:
<script src="~/Scripts/jquery-3.2.1.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () { // Submit pageSizeForm when another pageSize value is selected
$("#pageSize").change(function () {
$("#pageSizeForm").submit();
});
});
</script>
#model PagedList.IPagedList<AfpaSIPAdmin.Models.USER>
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "Users management";
}
<h1>Users management</h1>
<!-- Creating a new entry in table -->
<p>
#Html.ActionLink("Create new user", "Create")
</p>
<!-- Filtering table entries -->
#using (Html.BeginForm("Index", "Users", FormMethod.Get, new { id = "filterForm" }))
{
<p>
Filter: #Html.TextBox("SearchString", ViewBag.CurrentFilter as string, new { #placeholder = "First or last name..." })
<input type="submit" value="Apply"/>
</p>
}
<!-- Display table -->
<table class="table">
<tr>
<th>
#Html.ActionLink("Last name", "Index", new {
sortOrder = ViewBag.LastNameSortParm,
currentFilter = ViewBag.CurrentFilter
})
</th>
<th>
#Html.ActionLink("First name", "Index", new {
sortOrder = ViewBag.FirstNameSortParm,
currentFilter = ViewBag.CurrentFilter
})
</th>
<th style="min-width: 170px"></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td style = "min-width: 150px">
#Html.DisplayFor(modelItem => item.lastname)
</td>
<td style = "min-width: 150px">
#Html.DisplayFor(modelItem => item.firstname)
</td>
<td> <!-- Using images as buttons for actions -->
<a href="#Url.Action("Edit", "Users", new { id = item.Id })" title="Edit">
<img src="~/Content/images/edit.gif" />
</a>
<a href="#Url.Action("Details", "Users", new { id = item.Id })" title="Details">
<img src="~/Content/images/info.gif" />
</a>
<a href="#Url.Action("Delete", "Users", new { id = item.Id })" title="Delete">
<img src="~/Content/images/delete.gif" />
</a>
</td>
</tr>
}
</table>
<br/>
<!-- Paging -->
#using (Html.BeginForm("Index", "Users", FormMethod.Get, new { id = "pageSizeForm" }))
{
<div class="pager">
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) sur #Model.PageCount<br/>
#Model.Count of #Model.TotalItemCount elements
#Html.PagedListPager(Model, page => Url.Action("Index", new {
page,
sortOrder = ViewBag.CurrentSort,
currentFilter = ViewBag.CurrentFilter,
searchString = ViewBag.CurrentFilter,
pageSize = ViewBag.psize
}))
<!-- DropDownList for setting page size -->
Elements per page :
#Html.DropDownList("pageSize")
</div>
}
The reason is because you have 2 forms. When you submit the first form containing the textbox, the only value you send back to the controller is SearchString and all the other parameters in your method will be their default (for example when you return the view, PageSize will default to null and therefore return only 10 records even if the user previously selected say 50.
Likewise, when you submit the 2nd form containing dropdownlist for the page size, the value of SearchString will be null because its not sent in the request.
You need to have one form only containing both form controls. And if you wanted to send additional properties, for example the current sort order, then you can add those as query string values in the form element (for example, #using(Html.BeginForm("Index", "Users", new { sortOrder = .... }, FormMethod.Get))
I would also strongly recommend you use a view model containing the properties you need in the view and strongly bind to them rather than using ViewBag
public class UsersVM
{
public string SearchString { get; set; }
public int PageSize { get; set; }
public IEnumerable<SelectListItem> PageSizeOptions { get; set; }
.....
public IPagedList<USER> Users { get; set; }
}
View
#model UsersVM
...
#using(Html.BeginForm("Index", "Users", FormMethod.Get))
{
#Html.LabelFor(m => m.SearchString)
#Html.TextBoxFor(m => m.SearchString)
#Html.LabelFor(m => m.PageSize)
#Html.DropDownListFor(m => m.PageSize, Model.PageSizeOptions)
<input type="submit" value="Filter" />
}
....
<div class="pager">
Page #(Model.Users.PageCount < Model.Users.PageNumber ? 0 : Model.Users.PageNumber)
....
#Html.PagedListPager(Model.Users, page => Url.Action("Index", new {
page,
sortOrder = Model.CurrentSort,
currentFilter = Model.CurrentFilter,
searchString = Model.CurrentFilter,
pageSize = Model.PageSize
}))
</div>
and in the controller method, initialize a new instance of UsersVM and assign its properties
public ActionResult Index(string sortOrder, string currentFilter, string searchString, int? page, int? pageSize)
{
UsersVM model = new UsersVM();
....
var users = from u in _db.USER select u;
....
pageSize = pageSize ?? 10;
model.PageSize = pageSize.Value;
model.Users = users.ToPagedList(pageNumber, pageSize);
model.PageSizeOptions = new List<SelectListItem> { .... };
return View(model);
}

Validation Message in MVC for extracting the unique values by validating from database

I am new to MVC. I want to add a Validation Message for a input textbox in a cshtml file. The input should have the building names. It should only take the unique names after validating from the database. How should I write the validation message for this input box. Please help.
The code is below:
<tr>
<td style="height: 0.3em;width: 22em;">
<div style="margin-top: -0.0em;margin-left: 0.8em;">
#Html.TextBoxFor(model => model.buildingName, new { #class = "InfoTextBox", #placeholder = "Building name", id = "buildingName", maxlength = 25, onkeyup = "FormDirty();" })
<br />
#Html.ValidationMessageFor(model => model.buildingName)
<span style="position: absolute; top: 6.6em; left: 14.8em; color: #FF0000; font-size: 1.7em;">*</span>
</div>
</td>
I have to enhance the validation by only letting it take the unique values. Please help
You can use DataAnotation Remote attribute for this like below:
ViewModel :
[System.ComponentModel.DisplayName("buildingName")]
[System.Web.Mvc.Remote("CheckBuildingName", "ControllerName", AdditionalFields = "buildingName", HttpMethod = "Post")]
public string buildingName { get; set; }
Controller:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult CheckBuildingName() {
string _buildingName = Request.Form["buildingName"]; // put your control name here
if (repository.isExist(_buildingName)) {
return Json("Building already taken, Pleaes try with different name.", JsonRequestBehavior.AllowGet);
} else {
return Json(true, JsonRequestBehavior.AllowGet);
}
}
Your .cshtml code seems fine.
Hope this will helps you.

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
})
})
);
}

ASP.NET MVC 3 Logic for dynamically generated dropdown controls

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")
}

Resources