I have an Angular component called folder-browser which serves as a folder browser. It displays the folders in a vertical list, and when a folder is opened it displays folders within that folder if there are any:
Here is the HTML for this:
<div *ngFor="let element of folders">
<div class="folder" (click)="selectFolder(element)" [ngClass]="{'selected': checkSelected(element)}">
<div class="chevron-container">
<img class="chevron-icon" src="{{getChevronIcon(element)}}">
</div>
<img class="folder-icon" src="{{folderIcon}}" />
<span class="folder-name">{{element.name}}</span>
</div>
<div class="sub-folders" *ngIf="element.expanded">
<folder-browser [folders]="element.children"></folder-browser>
</div>
</div>
My question is about the two <img> elements (there are three distinct SVGs—the chevron img element uses either a down chevron or a right chevron). As you can see, these img elements are used many times; not only are they inside an *ngFor div, this folder-browser component is used recursively to display subfolders.
The src of these img elements points to a CDN. Looking at the network tab of Chrome dev tools, I can see that each folder-browser component that is initialized makes three calls to the CDN for the three image types. Additionally, every time a folder is opened and closed (which switches the chevron between facing down and right), the down and right chevron SVGs are loaded from the CDN.
Since there are only three different SVGs being used, I would like the browser to only load each one once and then use the cached image for each subsequent img element. This seems to happen to some extent, since only one request is made per image type when the component is initialized as opposed to however many folders are in the *ngIf. However, when initializing a new instance of the same component it would be nice if it would use the cached images since they are the same.
Is there a built-in way to do this?
Related
I'm playing around with a basic Dot-Net web assembly application. In the application I'm displaying two images using two different image tags image and img. The size of the image is bound to a private variable Size. I've noticed a problem where images do not render in a specific scenario using the image tag.
Replication:
dotnet new blazorwasm
I downloaded the SVG from: Bootstrap icons, then I placed the SVG file in "wwwroot/Media/".
In index.razor I've updated the code as follows:
#page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<img src="Media/search.svg" alt="Bootstrap" width="#Size" height="#Size">
<image src="Media/search.svg" alt="Bootstrap" width="#Size" height="#Size"/>
#code
{
private static string Size => "75";
}
The result of running the above code shows only one image displaying
Through process of elimination, the image defined using the image tag is the problem here. If you tweak the code to use hardcoded values i.e.
<image src="Media/search.svg" alt="Bootstrap" width="75" height="75"/>
then the code works again as expected.
I'm aware that <image> is deprecated, but I'd like to understand if the reason the binding breaks the image displaying is due to the deprecation or something else?
Update
The generated HTML using the template is
<!--!--><div class="top-row px-4" b-vv8m6rf2se="">About</div>
<article class="content px-4" b-vv8m6rf2se=""><!--!--><!--!--><!--!--><!--!-->
<!--!--><h1 tabindex="-1">Hello, world!</h1>
Welcome to your new app.
<img src="Media/search.svg" alt="Bootstrap" width="75" height="75">
<image src="Media/search.svg" alt="Bootstrap" width="75" height="75"></image></article>
An interesting find, although of course not of any practical value, just use <img>.
I could easily reproduce this with a jpg image so it's not about svg.
Now for a speculative answer:
Blazor treats <image> like any other tag and the generated HTML looks like expected. But according to this answer,
The HTML5 parsing spec requires that the tag is mapped to the img element at the tree construction stage
This makes me think that when the complete tag is rendered just once it works fine, handling is up to the browser.
But after Blazor has filled in the #Size it will try to update the HTML it generated earlier. If the Browser really changed <image> to <img> internally then the JS Bridge will have trouble finding the element again and the updates fail.
I have 2 images in html, one is shown, but the second one is not, I have tried to put another different image in the second one but it is still not shown. Both are in the same path.
<a class="navbar-brand" href="#">
<img id="borderIcono" th:src="#{/images/icono_prueba.png}" width="60" height="60" alt=""/></a>
<button ng-click="cargando = true" ng-disabled="cargando">
<img th:src="#{/images/redo.png}" class="redoBoton"/>
</button>
I have also tried to put the second image outside the button and without the class attribute, but it is still not displayed. So the css is not the problem.
I have tried to interchange them and the first one is seen where the second one is but the second one is still not seen in the place of the first one.
In inspect the browser tells me that the image could not be loaded.
I know that both images are in the same folder, so both should be accessible.
I found the bug, the server was not putting the image inside the compiled application, that is, I put the file in the source folder, but when I run it, it reads whatever is in the target folder.
I have a "menu" component that I am trying to make live API calls from during rendering - thus it needs to be "server" rendered vs static. This is in the _Host.cshtml.
For some reason the CSS classes I am wrapping it with do not render when it's "server" rendered, but work fine when it's static.
<div class="main-menu menu-fixed menu-light menu-accordion menu-shadow" data-scroll-to-active="true">
<component type="typeof(AppFrdb.Shared.MainMenu)" render-mode="Server" />
</div>
I tried "ServerPrerendered", in that case, the menu component appears, but none of the icons referenced in it appear (they are all also CSS based)
Help would be very appreciated.
I'm trying to recreate a similar look to the Windows 8 app view and I'm having an issue with the display. If your running Windows 8 and you're on the home page, just click the down arrow icon located at the bottom of the screen. This takes you to a list of all of the applications installed on your computer grouped alphabetically. This is essentially what I'm trying to recreate visually (only my data is not apps).
What I want:
I've created a grouped ListView (alphabetical) using the html/css/js method and what I get is close, but not exact. I get "A" followed by items that start with A.... then a new column "B" followed by B items... new column "C", etc. If there are only 2 "A" items I want "B" to show up next in the list. Not in a new column. Basically, I want to override the block formatting of the display but I can't seem to figure out how. I've played around with the DOM Explorer and there are so many classes and styles I haven't been able to figure out what needs to be changed.
What my app looks like:
Has anyone else done this? Does anyone have any suggestions I can try? Thanks.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Grocery List</title>
<link href="grocerylist.css" rel="stylesheet" />
<script src="/js/groceryData.js"></script>
<script src="grocerylist.js"></script>
</head>
<body>
<div class="grocerylist fragment">
<header class="page-header" aria-label="Header content" role="banner">
<button class="back-button" data-win-control="WinJS.UI.BackButton"></button>
<h1 class="titlearea win-type-ellipsis">
<span class="pagetitle">Grocery List</span>
</h1>
</header>
<section class="page-section" aria-label="Main content" role="main">
<!-- Template for the grouped headers for the list -->
<div id="headerTemplate" data-win-control="WinJS.Binding.Template" style="display: none">
<div class="simpleHeaderItem">
<h1 data-win-bind="innerText: title"></h1>
</div>
</div>
<!-- Template for each item in the list -->
<div id="mediumListIconTextTemplate" data-win-control="WinJS.Binding.Template" style="display: none">
<div id="groceryNameTemplate">
<!-- Display the name of our food -->
<h4 data-win-bind="innerText: name"></h4>
</div>
</div>
<!-- Our list of available grocery items -->
<div id="groceryListView"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource : GroceryData.itemList.dataSource,
itemTemplate: select('#mediumListIconTextTemplate'),
groupDataSource: GroceryData.itemList.groups.dataSource,
groupHeaderTemplate: select('#headerTemplate'),
layout: { type : WinJS.UI.GridLayout },
itemsDraggable: true,
itemsReorderable: false }">
</div>
</section>
</div>
</body>
</html>
Code added as requested. I went through all of my CSS and 100% of it is style only (colors, fonts, etc) all placement and positioning is defined in the WinJS styles which come with ListView.
In essence, the Apps view is just a standard 2D grid layout in a ListView, laid out in vertical columns first, then horizontally.
The trick is that there are two different item types: a letter/header item and an app item. This means that your data source--which in this case can be a single WinJS.Binding.List, is a single list of the apps sorted alphabetically, with then header header/letter items inserted at the appropriate points before items that start with that letter. (Remember to use globalization-sensitive collation if you're sorting alphabetically.)
With such a list, however, you cannot rely on declarative templates for your rendering, because you need to render both types of items distinctly. For this you need to use an item rendering function instead of a template. Because the rendering function gets called for each item, it can check the type and dynamically render each one according to its data. In the Apps view, an App item would be rendered with an icon and name, whereas a header/letter would be rendered with just text. The item renderer also assigns appropriate behaviors, which in the Apps view is that app items launch the app and header items do a semantic zoom out.
A rendering function is really the approach to take whenever you have a standard layout--a GridLayout again in this case--but need per-item rendering control. (This is different from a custom layout that lets you depart from the non-standard layout algorithms, and of course a custom layout can also be combined with a rendering function.)
For all the details of rendering functions, check out chapter 7 of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, specifically the sections "How Templates Work with Collection Controls" and its subsections on pages 364-376, and "Template Functions (Part 2): Optimizing Item Rendering" starting on page 414 if you want to go deeper, but probably isn't necessary for your scenario.
Custom layouts, just to note, are covered in Appendix B. Also, custom layouts only work with WinJS 2.0 on Windows 8.1; template functions work with WinJS 1.0 or 2.0 on 8.0 and 8.1 both.
Anyway, with per-item rendering via a template function, you can just create CSS as appropriate for the classes in each item structure you create, so you don't have to play games with the ListView's style hierarchy. You'll just want to make sure that your header/letter items have a suitable top margin to make the gap. Alternately, you could also have a gap item type in your data source that would render a blank space and not respond to any input. Also, you'll need to style the various selected, active, and hover states of the header/letter items to be all the same so they don't get highlighted like an app item.
The last note I'll make is that you don't need to use the GridLayout's grouping capabilities at all, because that forces the groups into new columns as you've observed. That's just its layout behavior when it has a group items source. That said, you could use a standard List for your app items and a create a group data source from it like you're doing, and then use a custom layout to handle the groups differently than the standard GridLayout. That should work as well, so it's really your choice in approach. I'd use this way if for some reason you can't get the header/letter styling to work with the template function approach, or if you're targeting Windows 8.0/WinJS 1.0 and not 8.1/2.0.
Hope that explains your options.
I have large, wide images within a portfolio page. The images are saved "progressive" and they load fine.
I was wondering if there's a way though to kind of preload those images to make them appearing faster and smoother. Or maybe there's a way to preload all the other pages and images into the cache so that at least all the following pages after the first appear smooth and fast? Whatever helps to make the pages load faster and smoother.
Any suggestions?
Each image consists of a variety of images, all of them within one wide image (prepared in PSD) and the visitor can shift left and right to call for the respective image to appear in the center.
Unfortunately sacrificing on the image quality or make them smaller is not an option here.
I know there are posts here on preloading images ad stuff but I can't find any that work with the image embedded in the HTML code.
Please have merci, I'm a CSS and Javascript novice, so the simpler the more likely I'll understand it. I'm afraid breaking up the images in single instances (make it a row of images instead of one whole image), place them in a floated div and change the respective Javascript code could be too challenging for me, right...? How else could I do that?
Appreciated!
Here's what I have (I guess it would be overkill to post all my HTML, Javascript and CSS here, I'll post some). The large images are placed within the HTML page and called via Javascript.
see here
<div class="ShiftGroup">
<div class="ShiftGroupC">
<div class="ShiftGroupI"><div id="ShiftGalleryFive"><img src="images/gallery_anzic1.png" width="3348" height="372" alt="imagegallery1" /></div></div>
<div class="ShiftGroupP" style="margin-left: -990px;"><div id="ShiftLeft" class="ShiftGroupD"><span class="pointer"><img src="images/arrowleft.png" width="78" height="50" alt="arrowsleft" /></span></div></div>
<div class="ShiftGroupP" style="margin-left: 341px;"><div id="ShiftRight" class="ShiftGroupD"><span class="pointer"><img src="images/arrowright.png" width="78" height="50" alt="arrowright" /></span></div></div>
and
gallery = document.getElementById('ShiftGalleryFour').style;
This is how we preloaded images in one of our projects:
preloadImages = function(imageIndex) {
// Finds div element containing index (1..n) and url of image
// (both stored as data attributes)
var image = $(".data .image[data-index=" + imageIndex + "]");
// Creates an image (just virtually) that is not embedded to HTML.
// Its source is the url of the image that is to be preloaded
var preloadedImage = $(new Image()).attr("src", image.attr("data-full-url"));
// Bind an event to the "virtual" image to start preloading next image when
// this one is done
preloadedImage.load(function() {
// Start preloading the next one
preloadImages(imageIndex + 1);
});
}
// Start preloading the first image
preloadImages(1)
In your case this solves only one part of the problem - preloading.
I see you include all images in html as img tags. If you want to achieve better performance, do not place any img tags in your html of the gallery. Just div tags that will become the future containers of your images. These divs may have indexes and contain data attributes with image urls (as seen in my example). When your page gets loaded, start preloading procedure. When an "virtual image" gets loaded. Create new image tag inside its container and start preloading the next image.
This will definitely cut off the download time of your page.
My example uses jQuery which simplifies the script. Pure javascript would be more complicated.
UPDATE:
This is how preloading example may work like.
HTML
Let's say you have 4 images and all of them has its container - a div in which individual image is to be placed.
<div class="images">
<div class="image" data-index="1" data-src="image_1_source_path"></div>
<div class="image" data-index="2" data-src="image_2_source_path"></div>
<div class="image" data-index="3" data-src="image_3_source_path"></div>
<div class="image" data-index="4" data-src="image_4_source_path"></div>
</div>
JavaScript
After the the document is loaded, preloading procedure may start. You start by preloading the first image. After this one is loaded, you append it to its container and trigger preloading of the next image. There is also return called if all images are loaded and no container is found.
preloadImages = function(imageIndex) {
var imageContainer = $(".images .image[data-index=" + imageIndex + "]");
return if imageContainer.length === 0
var preloadedImage = $(new Image()).attr("src", image.attr("data-full-url"));
preloadedImage.load(function() {
imageContainer.append(preloadedImage);
preloadImages(imageIndex + 1);
});
}
$(document).ready(function(){
preloadImages(1);
});
Hopefully you get the idea.