Foreach property in item from list - asp.net-mvc-4

I'm revamping a small helpdesk application (moving from ASP classic to MVC4). This is my first full MVC application. I've opted to go with Razor, and I feel like it's pretty intuitive. But I've hit a wall on this part.
I get a list of tickets from the database to display to the user. What I would like is to dynamically create a table. Right now I have put the headers in place manually, which is fine. If there's a way to do that dynamically, though, I'm open to suggestions.
But the crucial piece is to get each property from the ticket. I have a Ticket class that has over 20 properties. I'll be working on whittling those down to the minimum we want to display, but as a starting point, I'm trying to throw them all up on the screen.
I have the following:
#model IList<Helpdesk4.Models.Ticket>
...
#foreach (var ticket in Model)
{
<tr>
#foreach (var item in ticket)
{
<td>item</td>
}
</tr>
}
But I can't run that foreach on ticket. I'm enough of a noob that I don't totally understand why, but I think that the properties have to be loaded and so can't be enumerated by default. So without pulling up each property name, how do I just get each property's value from ticket?
I am using NHibernate for the queries to the db if that makes any difference.

The absolutely clearest and easiest way to do this if to manually add each of the properties values to the row. This does require a small amount of "extra" work, but it is a one time thing, and 20 properties is not that much. It also gives you much finer control over exactly how each property is displayed, if it is aligned right or left, etc.
What you end up with is something like this
#foreach (var ticket in Model)
{
<tr>
<td>
#ticket.FirstProperty
</td>
<td class="align-right">
#ticket.SecondProperty
</td>
<td class="align-center bold">
#Html.DisplayFor(m => ticket.ThirdProperty)
</td>
<td>
<i>#ticket.FourthProperty</i>
</td>
...
</tr>
}
Stylings and such added for emphasis of customizability.
Good luck with your first MVC project!

Foreach only works with an enumerable item, and general classes don't implement IEnumerable. You need to put each item on there yourself. The best way would be to use something like Html.TextBoxFor(m => m.Property) for each property.
edit:
The best way would be to just use Html.LabelFor on the object itself.
#for(int index = 0; index < Model.Count; ++index)
{
<tr>
#Html.LabelFor(m => m[index])
<tr>
}

Related

Showing a substring of text that contains HTML

I am implementing something similar to a blog engine, where on the entry page I would like to see a listing of my posts, and, for each, its title and a short piece of its body content.
This body content is HTML, and is stored in the database as a string, so, it's not enough to just show a substring of it, because it might break the HTML structure.
I guess the algorithm would be: render the string to HTML and extract only the first X characters of it.
Is there any solution for this problem?
Here is a working demo like below:
View:
#model IEnumerable<Post>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.Body)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Body)
</td>
</tr>
}
</tbody>
</table>
Controller:
public IActionResult Test()
{
var model = new List<Post>()
{
new Post(){ Title="asp.net core mvc",Body="<html><div><h1>The Model-View-Controller (MVC) architectural pattern separates an application into three main groups of components: Models, Views, and Controllers. This pattern helps to achieve separation of concerns. Using this pattern, user requests are routed to a Controller which is responsible for working with the Model to perform user actions and/or retrieve results of queries. </h1></div></html>"},
new Post(){Title="asp.net core razor pages",Body="<html><div>Razor Pages is designed to make common patterns used with web browsers easy to implement when building an app. Model binding, Tag Helpers, and HTML helpers all just work with the properties defined in a Razor Page class.</div></html>"}
};
foreach(var item in model)
{
item.Body= Regex.Replace(item.Body, #"<[^>]+>| ", String.Empty);
item.Body= item.Body.AsSpan(0, 30).ToString()+"...";
}
return View(model);
}
Result:

Improvement With Xpath Search?

I am a student learning selenium and trying to set a checkbox(by placing a tick) that resides on the same row as a target document. Here is how the row looks:
<span><input>Checkbox</input></span> - <span>_lblDocumentCategoryDescription</span> - <span>_lblDocumentdescription</span>
Each row consists of a checkbox for that row, a category and a description. I am provided the description and need to set the corresponding checkbox at the beginning of the row.
Although I have a solution that works I am looking for a better method as my method is very slow and I can call it multiple times, compounding the slowness. The page in question has 315 fixed different documents that might possibly be added (to a merge and print feature). I usually add at least 3 rows so need to run the search
I am currently using xpath to search since my knowledge of CSS is limited and it seems like traversing up the DOM (no :parent?) is not easy unless using some javascript, which I am trying to avoid. I have read that executing JS doesn't emulate the user correctly so I guess xpath is the solution?
As I know there must be a quicker/better/more efficient solution I am hoping others might have found better?
Here is what I am currently using:
Method Call
CPTools,setStupidCheckbox("Sublease Addendum", true);
Method:
public CPTools setStupidCheckbox(String documentToPrint, Boolean onIfTrue) {
this.log.trace("Starting: setStupidCheckbox; parameter: documentToPrint " + documentToPrint);
String xpathForCheckboxes = "/ancestor::tr//input";
try {
String xpathForDocumentDescriptions = String.format("//span[contains(text(), '%s')]", documentToPrint);
tools.waitForElement(By.xpath(xpathForDocumentDescriptions), 2);
if (driver.findElement(By.xpath(xpathForDocumentDescriptions)).isDisplayed()) {
driver.findElementByXPath(xpathForDocumentDescriptions + xpathForCheckboxes).click();
}
} catch (org.openqa.selenium.NoSuchElementException | ElementNotVisibleException e) {
}
this.log.trace("Ending: setStupidCheckbox");
return this;
}
Here is the full xpath for a table row (the dynamic part of the id is the _ctl316):
<tr class="DataGridCellData">
<td class="DataGridCellDataCenterAligned">
<span class="DataLabel"><input id="ctl01_PageBody_grdDocument_ctl316_chkSelect" name="ctl01$PageBody$grdDocument$ctl316$chkSelect" onclick="javascript:uiHelper.GridCellCheck(this, 'chkAllSelect');" type="checkbox"></span>
</td><td>
<span id="ctl01_PageBody_grdDocument_ctl316_lblDocumentCategoryDescription">Approval Form</span>
</td><td align="left">
<span id="ctl01_PageBody_grdDocument_ctl316_lblDocumentdescription">Sublease Addendum</span>
</td><td align="center">
</td>
</tr>
Thanks in advance for any possible help.
Try below XPath if span text (Sublease Addendum) is unique:
//td[.= 'Sublease Addendum']

ASP .NET Core: View grid of multiple items to be edited

I'm trying to construct a view where the records from a table (machines) get uploaded and that each one can be edited in a view.
This is my Get method:
public async Task<IActionResult> Test()
{
return View(await _context.Machines.Include(t => t.MachineTypes).Include(p => p.Suppliers).AsNoTracking().ToListAsync());
}
Pretty simple for what I need. I'm getting a list of the table Machines and including the related tables: MachineTypes and Suppliers because this information should be able to be edited in the view.
Now, with the view I'm quite lost, sorry for this.
I started defining the model to be used:
#model IEnumerable<Application.Models.Machine>
Question[Answered] Is correct to use an IEnumerable?
Answer by uni: Yes
Then, as seen in some examples, I use a Html.BeginForm to indicate the controller and action:
#using (Html.BeginForm("Machines","TestPost"))
Sidenote: The Post method is not defined yet so I'm using any name for now
Then comes the iteration to read every element of the list:
#Html.ValidationSummary(true)
#for(int i = 0; i < Model.Count() ; i++)
Question[Answered]: In this part I'm not sure if 'Model.Machine.Count()' is correct.
Answer by uni: It's correct. No need to specify 'Machine'
Inside the iteration I don't know how to code to show the MachineName (MchName) to begin with. I'm doing this:
#for(int i = 0; i < Model.Count() ; i++)
{
<tr>
<td>
#Html.HiddenFor(modelItem => modelItem.Machine[i].Id)
#Html.DisplayNameFor(model => model.MchName)
</td>
</tr>
}
Question[Answered]: In this part:
#Html.HiddenFor(modelItem => modelItem.Machine[i].Id)
I don't know why I need to use the HiddenFor, I saw it on another example in the forum.
Answer by uni: This is one of many ways to send the values to the controller.
Question[Answered]: Also, this part
#Html.HiddenFor(modelItem => modelItem.Machine[i].Id)
Answer by uni: 'Machine' don't need to be included, same as before
Continuation of the View
For the iteration part, since 'Machine' don't need to be included...
[NEW]Question: How should I call the item wanted from the Machine table?
I've got this:
#for(int i = 0; i < Model.Count() ; i++)
{
<tr>
<td>
#Html.HiddenFor(modelItem => i.Id)
#Html.DisplayNameFor(modelItem => model.MchName)
</td>
</tr>
}
Both of this are wrong, but I was trying to figure out how to show the item inside the iteration. I wonder if, since I'm using an IEnumerable, it would be best if I use a #foreach instead of a for();
Thanks
Question: Is correct to use an IEnumerable?
Answer: Yes
Question: In this part I'm not sure if 'Model.Machine.Count()' is correct, and well, I'm getting the error: IEnumerable does not contain a definition for Machine. Is it enough if I put it this way?: #for(int i = 0; i < Model.Count() ; i++)
Answer: You already have a collection of Machines (IEnumerable<Application.Models.Machine>) which is stored in your model so Model.Count() would be the correct way to go as each Machine model doesn't have a child Machine object
Question: #Html.HiddenFor(modelItem => modelItem.Machine[i].Id) I don't know why I need to use the HiddenFor, I saw it on another example in the forum.
Answer: When you do an HTTP post you have many ways to send values to the controller action. Hidden fields are a way to do that. In this case you are sending the id of the machine as part of your form submission.
Question: #Html.HiddenFor(modelItem => modelItem.Machine[i].Id) is showing the same error: IEnumerable does not contain a definition for Machine. I tough I should mention the table.
Answer: The answer to this one is the same as the answer that I mentioned above regarding the Count() invocation in the for loop.
Hope this helps!

Display specific data with parameter using Stored Procedure

Hi I have error in regards with my displaying specific data. I passed a parameter through a model and I get error on regarding creating a new instance. here is my code:
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult ViewEmployeeSalary(spGetSalaryPerEmployee getsal)
{
var salary = _Context.Set<spGetSalaryPerEmployee>().FromSql("spGetSalaryPerEmployee #empID = {0}", getsal.EmployeeID).AsNoTracking();
return View(salary);
}
View
This the button where you click to redirect to the details page
#foreach(var item in Model)
{
a class="btn btn-primary btn-sm" asp-controller="Salary" asp-action="ViewEmployeeSalary" asp-route-id="#item.EmployeeId"><span class="glyphicon glyphicon-dashboard" style="vertical-align:middle;margin-top: -5px"></span> Salary Details</a>
}
Here is the page where it views the details
#model PEMCOLoan.DAL.Entities.spModels.spGetSalaryPerEmployee
#{
ViewBag.Title = "Salary Details";
}
<h2>#Html.DisplayFor(model => model.FullName)'s Salary Details</h2>
<table class="table">
<tr>
<td class="form-horizontal">ID:</td>
<td>#Html.DisplayTextFor(model => model.EmployeeID)</td>
</tr>
<tr>
<td class="form-horizontal">Salary Amount:</td>
<td>#Html.DisplayFor(model => model.Salary)</td>
</tr>
<tr>
<td class="form-horizontal">Remarks:</td>
<td>#Html.DisplayTextFor(model => model.Remarks)</td>
</tr>
</table>
I get this error:
I don't really what really happens but stated the I need to create an instance.
I have still a little knowledge about asp.net core and ef core and still studying for this language for now. I really need your help in regards with this since I'm a bit new with this language.
Any suggestion would be a greatly appreciated!
Your controller method needs to return single record as error states. Now it returns a list, so you need to change to something like this:
var salary = _Context.Set<spGetSalaryPerEmployee>().FromSql("spGetSalaryPerEmployee #empID = {0}", getsal.EmployeeID).AsNoTracking().SingleOrDefault();
//salary now is single record
return View(salary);
And maybe you need to check salary variable is not null before passing it to view, because then you'll have errors there.

Get classname from tag in LESS

I want to use a classname to set a background on a element, but i'm not sure if it's possible with LESS js at the moment.
The case is:
I have a table with a list of athletes, on these lists i want to show a country flag as a background of a p tag.
In HTML it looks like:
<table>
<tr>
<td class="athlete"><p class="us">Athlete 1</p></td>
</tr>
</table>
I want to set the background image through a mixin, if i can get the classname from the P tag i would be there.
.athlete {
p {
.setBackgroundImage(#ClassName);
}
}
is there anyway in LESS to achieve this?
The problem is that i need a whole new rule for every possible country. My loop implementation didn't cut it either.
I went for the javascript implementation, i add a background image to a iso coded classname in a certain parent class.
<div class="nationality">
<p class="athlete">Athlete X<span class="nl"></span></p>
</div>
jQuery(".nationality span").css('background-image', 'url(../img/flags/60/'+jQuery(".nationality span").attr("class")+'.png);
Thanks guys.