Render a view containing hundreds of images - c#

My viewmodel object contains approximately 200 to 300 objects.
One property for that object is an 100x100px image converted to Base64string.
Here is my code for rendering the entire collection:
<table class="table-condensed">
<tbody>
#foreach (var ua in day.Value)
{
<tr>
<td style="width: 105px;">
<img src="data:image;base64,#ua.UserProfileImage" style="width:100px;height:100px;" alt="User Image" />
</td>
<td valign="top">
<b>#ua.UserFirstName #ua.UserLastName</b> <a asp-controller="Users" asp-action="File" asp-route-id="#ua.UserId">(Profile)</a><br />
#if (!string.IsNullOrEmpty(ua.TeamName))
{
<small>#ua.TeamName</small><br />
}
<span class="label label-default">#ua.FunctionName</span><text> </text><span class="label label-default">#ua.HireDate</span>
</td>
</tr>
}
</tbody>
The logic and the time till the controller returns the data is 0.5 seconds so my problem isn't there.
The render speed for the entire view is about 5 to 8 seconds because of those many images.
Is there a way to improve that number?
I'm using ASP.NET Core 2.

You can use Ajax javascript to load every image and you can implement some pagination , the load time will be the same but on the client side it looks better

You should consider to use lazy image loading
https://github.com/craigbuckler/progressive-image.js. You can see at demo that images are not loading until you scroll to their destination

Related

How to insert different images in ASP.NET MVC while using a foreachloop and a db

<table class ="table" cellpadding="0" cellspacing="0">
<tr>
<th>Item Name</th>
<th>Price</th>
<th>Seafood</th>
<th>Has Gluten</th>
<th>Picture</th>
</tr>
#foreach (var items in #Model)
{
<tr>
<td>#items.ItemName</td>
<td>#items.Price</td>
<td>#items.IsSeafood</td>
<td>#items.HasGluten</td>
</tr>
}
I am currently learning how to develop using ASP.NET MVC.
I am trying to insert different images for each of my items. My foreach loop creates the table and items from a mysql database. I want to add the pictures next to the tables unless I'm unable to with the code I have.
You can use an <img src=""/> tag inside your last <td> to embed the image directly into the html.
Something like this, depending on how your image is stored.
<td><img src="#items.Url"/></td>

How do I select a button before text using Selenium C#

I have a bit of a dilemma. I have a table of 3 columns. Column 1 is a button to make that row active. The 2nd column is the name of the person. And the 3rd column is a button to view the person's details.
Using Selenium C#, I can search for a specific person in the table and click the button to View, using the code below:
currentDriver.FindElement(By.XPath("(.//*[normalize-space(text()) and normalize-space(.)='Name of person'])")).Click();
How do I select the button before the name of the person?
EDIT: Added HTML -
<table class="table table-hover>
<thead>...</thead>
<tbody id="listCompany">
<tr>
<td>
<span id="a5" class="badge btnActivateCompany clsActiveCompany15" ><i class="fas fa-times"></i></span>
</td>
<td>Test Director</td>
<td>
<button.>...</button>
</td>
</tr>
</tbody>
The class btnActivateCompany is used several times depending on how many rows exist. And the id changes depending on the rows as well. So I have to search to find the correct record and then select the span before it.
I tried the following to select the object:
currentDriver.FindElement(By.XPath("(.//*[normalize-space(text()) and normalize-space(.)='Test Director'])[1]/preceding::span[1]")).Click();
I get the feeling you've modified the HTML as you've posted. I guess you stripped out some data and typed other content in. You say you want the button before the person, but in your code the button is after the person?
Either way, both are achievable. I made a few quick additions to help visibility. If i'm wrong please correct me as it influences xpaths.
Quick change log: I ran your html through a beautifier, added text to all the columns for visibility, added "border=1" to the table, remove the . from button, added the </table> and duplicated the row so i can check unique objects are found.
This is the result: (useful if anyone else wants to chip in an identifier)
<html>
<body>
<table class="table table-hover" border=1>
<thead>...</thead>
<tbody id="listCompany">
<tr>
<td>
<span id="a5" class="badge btnActivateCompany clsActiveCompany15" >
<i class="fas fa-times"> Column 1</i>
</span>
</td>
<td>Test Director</td>
<td>
<button> Button in col 3
</button>
</td>
</tr>
<tr>
<td>
<span id="a5" class="badge btnActivateCompany clsActiveCompany15" >
<i class="fas fa-times"> Column 1</i>
</span>
</td>
<td>Second Row!</td>
<td>
<button> Button in col 3
</button>
</td>
</tr>
</tbody>
</table>
</body>
</html>
That renders like this:
Based on fact you say you can get the text in the middle column...
If you want the button in column 3, you can try xpath:
//td[text()='Test Director']/parent::*/td/button
If you want the span in column 1, you can try:
//td[text()='Test Director']/parent::*/td/span[contains(#class,'btnActivateCompany')]
In both of these instances this selects a unique hit in the source.
However, please note, this is dependent on the html provided. If there are other elements/attributes in the table the xpath might need more work. I'm happy to help more but you'll need to share more content.

Combining ASP.NET Controller Route with AngularJS object id

I have an ASP.NET View using server-side #foreaches, now replaced with AngularJS.
Now I use ng-repeat="record in records", and I don't use anymore the #foreach.
The actual code that worked with #record.Id now does not work with {{record.id}}:
<td class="text-nowrap">
<a asp-controller="Records" asp-action="Edit" asp-route-id="{{record.id}}">Edit</a>
<a asp-controller="Records" asp-action="Details" asp-route-id="{{record.id}}">View</a>
<a asp-controller="Records" asp-action="Delete" asp-route-id="{{record.id}}">Delete</a>
</td>
obviously, because the #record.Id was on the server side...
Now, the solution I see it to set something like
However if the controller's route will change it could lead to nowhere... Is there a way to workaround that?
PS.
Some more code for better understanding:
<div ng-app="tablesApp" ng-controller="tablesController as tc">
<table>
<thead>
<tr>
<th>#Html.DisplayNameFor(model => model.Name)</th>
<th>#Html.DisplayNameFor(model => model.Description)</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in records">
<td>{{record.name}}</td>
<td>{{record.description}}</td>
<td>
<a asp-controller="Records" asp-action="Edit" asp-route-id="{{record.id}}">Edit</a>
<a asp-controller="Records" asp-action="Details" asp-route-id="{{record.id}}">View</a>
<a asp-controller="Records" asp-action="Delete" asp-route-id="{{record.id}}">Delete</a>
</td>
</tr>
</tbody>
</table>
</div>
JS Controller:
$http.get("/api/Records")
.then(function (response) {
tc.records = response.data;
}));
Actually a little bit better option that the hardcoded route is to use
<a href="#Url.Action(action: "Edit", controller: "Records")/{{record.id}}" ></a>
However this one is supposed to have the {ActionRoute}/{id} fixed structure...
In case you use foreach in razor view (obviously you do) then you can't use angular syntax {{record.id}} you have to use #record.id
But if you get records, as an array/list whatever, in AngularJs then you have to change your html and use ng-repeat
-- Edit
Sorry just see your code again, you used asp-action and asp-controller etc
They are TAG HELPERS from core version, in other words they will get rendered on the server side and what you will get in the browser actually is anchor link like
<a href="/Records/Edit/{{record.id}}" >Edit</a>
AngularJs as you know is client side so it won't render. Either use #Url.Action as you already did or you could remove asp-route-id and add the id later using custom directive.

# of records in table divided into pages - ASP.Net MVC Bootstrap

How do I make the HTML table display 10 records only and whenever it goes beyond 10 it will display a link for pages below? e.g. "1, 2, 3.." And limits up to 7 links like how YouTube displays it (See image below)? I'm using ASP.Net MVC with Bootstrap by the way.
<table id="userTbl" onclick="getTblRec('userTbl,'userBody','0')" class="table-scroll table-striped bootgrid-table">
<thead>
<tr>
<th class="hidden">
#Html.DisplayNameFor(model => model.USR_ID)
</th>
<th class="hidden">
#Html.DisplayNameFor(model => model.USR_FNAME)
</th>
</tr>
</thead>
<tbody id="userBody">
#foreach (var item in Model)
{
<tr>
<td id="userId" class="hidden">
#Html.DisplayFor(modelItem => item.USR_ID)
</td>
<td>
#Html.DisplayFor(modelItem => item.USR_FNAME)
</td>
<td>
</tr>
}
</tbody>
</table>
YouTube sample pic:
There are two ways of implementing pagination, Server and Client side. Firstly server implementation, Load only the set of items that are to be shown for the page from the server along with the page options. Pagination at the client side, load the entire set of items and provide pagination with the support of table and/or other controls. Both the options have pros and cons. These links should get you started on the topic,link1 , link2.
Since you are using ASP.NET MVC, there is an build in helper named WebGrid for scenarios like paging. This is super easy to use and provides almost all the functionality that you may need.
You can also take a look at this beginner level walk through.
WebGrid in asp.net mvc (By Sheo Narayan).
Make a partial view for paging and call that partial view if records you have more than 10. i.e. in this link Paging
Or you can install this nuget also, and your wish style would be able to design this is link for nuget Nuget Packege

Placing data within an HTML template using ASP.NET

I have an ASP.NET web app that retrieves a JSON collection and outputs the content of the collection, via LINQ to a StringBuilder that has a lot of table and other tags in HTML, with my data pieces interspersed.
This is what I mean:
sb.AppendFormat(#"
<table cellpadding=""0"" class=""TableStyle"" style=""width: 70%; height: 100%;"" cellspacing=""5"">
<tr>
<td class=""PicHolder"" style=""width: 151px"" rowspan=""2"">
<a href=""http://twitter.com/{0}"" target=""_blank""><img height=""45px"" width=""45px"" src=""{1}"" alt=""{4}""></td>
<td style=""width: 100%; height: 36px;"" class=""LinkTitleCell"" valign=""top"">
<span class=""BoldText"" style=""height: 23px"">
<span class=""HyperLinks"">{6}</span></span></ br><span class=""NormText"">
</tr>
<tr>
<td style=""width: 100%; height: 10px;"" class=""MentionedXTimes"" valign=""top"">
<span class=""NormText"">Mentioned {3} time(s).</span></td>
</tr>
<tr>
<td style=""width: 151px"" class=""UserName""><span class=""UserName""><img height=""1"" src=""spacer.gif"" width=""1"" />{4}:</span></td>
<td style=""height: 23%; width: 100%"" class=""WhatUserSaid"" valign=""top"">
{5}</td>
</tr>
<tr>
<td style=""width: 151px"" class=""UserName"">Info:</td>
<td style=""height: 24px; width: 100%"" class=""LinkTitleCell"" valign=""top""><span class=""NormText"">{7}</span></td>
</tr>
<tr>
<td style=""height: 9px; width: 151px""></td>
<td class=""LinkGreen"" style=""height: 9px; width: 100%"" valign=""top"">{2}</td>
</tr>
<tr>
<td style=""height: 9px; width: 151px""> </td>
<td class=""TableSpacer"" style=""height: 9px; width: 100%"" valign=""top""> </td>
</tr>
</table>",
count.First.userName, count.First.imgURL, newdata, count.Count, count.First.userName, count.First.txtDesc, (title != string.Empty) ? title2 : newdata, metaDesc2);
You can see all of the {0} and {1} data place holders I am using in the code above, and the reference to the data required at the end of the StringBuilder method call.
I want to be able to separate the HTML from my app so that I can edit it more easily without having to escape all of the "" quote marks.
So I need a template that has place holder for where my data goes. This is a noob question and I know exactly what want but not how to implement it.
Any help much appreciated!
I'm getting some bad vibes here, like you're building the entire page or at least a very large part of it inside a single stringbuilder for output. This is a very bad thing to do.
If you want to "accumlate" your page string like this, you should write to the Response object directly instead. The StringBuilder forces you to keep all that html in memory on your server for every request, and memory use like this on a web server just won't scale well when you have many requests going at once. Even worse, there's a chance of these stringbuilders ending up on the Large Object Heap. If that happens it could ultimately cause OutOfMemoryExceptions. By contrast, the Response object buffers your html as you write it, and so you only use memory per request up to the size of the buffer. That will scale/perform much better.
Even better, though, is if you work with ASP.Net to make this happen rather than against it. That solves both the scaling problem and your "templating" problem. For example, I'll show you a better way to adapt just a small snippet of the html you posted. First write your .aspx markup like this:
<table cellpadding="0" class="TableStyle" style="width: 70%; height: 100%;" cellspacing="5">
<tr>
<td class="PicHolder" style="width: 151px" rowspan="2">
<asp:HyperLink runat="server" ID="TwitterLink" target="_blank">
<asp:Image runat="server" ID="TwitterLinkImage" Height="45px" Width="45px"/>
</asp:HyperLink></td>
...
</tr>
And then use code like this in your .cs code-behind:
TwitterLink.NavigateUrl = "http://twitter.com/" + count.First.userName;
TwitterLinkImage.ImageUrl = count.First.imgURL;
TwitterLinkImage.AlternateText = newdata;
There you go. Clean separation of markup and code/data that's more readable, avoids all the messy escaping, and will perform even better than your stringbuilder. If you have a lot of records that are exactly the same and you still want fine control over your HTML output, use an <asp:Repeater> control and set up markup like this in it's ItemTemplate. Then c# code will go in the repeater's ItemDatabound event.
As with most things there are a number of ways of doing it; from the pure code behind which you have as your original question through to the pure client side / jquery templating.
Another option would be to have a user control with the html part of the output in. This could have an associated property with the data object assigned to it. You could then access the data in the html you would put somethihng like:
<%= MyClass.Property %>
Where ever you want that value to appear in the html.
So what I'd look to do (not tested as don't have VS installed on this machine) is the following:
create a class which will store the data which you will be receiving in json format
Create the user control
Specifiy a property on the control which takes an instance of your class defined above
Add the control onto your aspx page
In the code behind of the aspx page set the retrieval and parsing of the json data in the Page_Load event.
Still in the Page_Load; assign the newly created instance of the object to the usercontrol property
Once this is done when the page renders the user control can access the data and display it through the mechanism (bit of code) above.
Hope this helps, been doing a lot of MVC stuff recently so my Web Forms is a little rusty :-)
If this doesn't make much sense, I appologise in advance and if you let me know I will try and update the post with some more code examples to try and make it make more sense :-)
Perhaps it's better to return the data as json to the browser and there use jqGrid or jQuery template.
Grz, Kris.

Categories

Resources