Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Jeroen Oostwouder 74 posts 180 karma points
    1 week ago
    Jeroen Oostwouder
    0

    Best practice to render optional links

    I'm wondering what is best practice, when you have to deal with optional links/containers in html.

    For example: I have a list of elements. Each element has an image field, and a Url-picker field. The Url-picker value is optional.

    So I loop through the elements, to render the html like this:

    foreach (var element in elements) {
        <a href="@element.Link.Url"><img src="@element.Image.Url()"></a>
    }
    

    How can I make the anchor optional?

    I can't do:

    foreach (var element in elements) {
        if (element.Link != null) {
            <a href="@element.Link.Url">
        }
        <img src="@element.Image.Url()">
        if (element.Link != null) {
            </a>
        }
    }
    

    Because then I open an -tag without closing in the same block.

    In Umbraco7 I did this:

    foreach (var element in elements) {
        @Umbraco.If(element.Link != null, "<a href=\"" + element.Link.Url + "\">")
        <img src="@element.Image.Url()">
        @Umbraco.If(element.Link != null, "</a>")
    }
    

    But the UmbracoHelper doesn't seem to have an 'if' function anymore.

    What's the best solution for this problem?

  • Marc Goodson 1695 posts 11278 karma points MVP 7x c-trib
    5 days ago
    Marc Goodson
    100

    Hi Jeroen

    Number of ways you could look at this...

    You could create a razor helper to write out the image bit eg

    @{
    void DisplayLovelyImage(IPublishedContent image, string altText){
    <img src="@image.Url" alt="@altText" />
    }
    }
    

    and then in your foreach have:

    @if (element.Link != null)
    {
       <a href="@element.Link.Url">
        @{ DisplayLovelyImage(element.Image, element.Image.Name); }
      </a>
    }
    else
    {
      @{ DisplayLovelyImage(element.Image, element.Image.Name); }
    }
    

    It's also not a bad idea to have a ViewComponent responsible for rendering all your img / picture tags from your razor, so 'one place' to update the logic around the displaying of images in a consistent way, eg if you want to provide responsive srcsets etc - then your DisplayLovelyImage method would be:

    @await Component.InvokeAsync("DisplayLovelyImage", new { image = element.Image, width = 900, height = 0, cssClassName = "scale" })
    

    regards

    Marc

  • Jeroen Oostwouder 74 posts 180 karma points
    4 days ago
    Jeroen Oostwouder
    0

    That's a nice way indeed. I'm still not too familiar with components, but I might give that a try too.

    Thank you.

  • Marc Goodson 1695 posts 11278 karma points MVP 7x c-trib
    4 days ago
    Marc Goodson
    0

    Hi Jeroen

    This is a good intro: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-6.0#add-a-viewcomponent-class

    With ViewComponents, there are two parts, the 'code' and the 'view'

    You create a class that inherits from ViewComponent, so for example, we tend to use Slimsy a lot on sites, and so this ViewComponent builds up different variations of the Url, low quality image etc for lazyish loading

    public class ResponsiveImageViewComponent : ViewComponent
        {   
                public async Task<IViewComponentResult> InvokeAsync(MediaWithCrops image, UrlHelper urlHelper, int width, int height, string cssClassName = "")
                {
                    var imageModel = new ResponsiveImageViewModel();
    
                    if (image != null)
                    {
                        string defaultMimeType;
                        var umbracoExtension = image.Value<string>(Constants.Conventions.Media.Extension);
                        var defaultFormat = umbracoExtension;
    
                        switch (umbracoExtension)
                        {
                            case "jpg":
                                defaultMimeType = "image/jpeg";
                                break;
                            case "png":
                                defaultMimeType = "image/png";
                                break;
                            case "gif":
                                defaultMimeType = "image/gif";
                                break;
                            default:
                                defaultMimeType = "image/jpeg";
                                defaultFormat = "jpg";
                                break;
                        }
    
                        var lqipWidth = (int)Math.Round((decimal)width / 2);
                        var lqipHeight = (int)Math.Round((decimal)height / 2);
    
                        var imgSrcSet = urlHelper.GetSrcSetUrls(image, width, height, outputFormat: defaultFormat);
                        var imgSrc = SlimsyExtensions.GetCropUrl(image, width, height, furtherOptions: "&format=" + defaultFormat);
    
                        // ** Using half width/height for LQIP to reduce filesize to a minimum, CSS must oversize the images **
                        var imgLqip = SlimsyExtensions.GetCropUrl(image, lqipWidth, lqipHeight, quality: 20, furtherOptions: "&format=" + defaultFormat);
    
                        //Image Model Stuff
                        imageModel.DefaultSrcSetUrl = imgSrcSet;
                        imageModel.Id = image.Id;
                        imageModel.Url = imgSrc;
                        imageModel.CssClass = cssClassName;
                        imageModel.LqipUrl = imgLqip;
                        imageModel.AltText = image.Name;
                    }
    
    
                    return View("ResponsiveImage", imageModel);
                }
            }
    

    Then you create a corresponding View in /Views/Components/ResponsiveImage/ResponsiveImage.cshtml

    Which would then write out the img tag

    @model ResponsiveImageViewModel
    @{
        if (Model != null)
        {
            var cssClass = "lazyload";
            if (!string.IsNullOrEmpty(Model.CssClass))
            {
                cssClass += $" {Model.CssClass}";
            }
    
    
            <picture>
                <!--[if IE 9]><video style="display: none"><![endif]-->
                <source data-srcset="@Model.DefaultSrcSetUrl" srcset="@Model.LqipUrl" type="@Model.MimeType" data-sizes="auto" />
                <!--[if IE 9]></video><![endif]-->
                <img src="@Model.LqipUrl"
                     data-src="@Model.Url"
                     class="@cssClass"
                     data-sizes="auto"
                     alt="@Model.AltText" />
            </picture>
        }
    }
    

    if that gives you the idea!

    regards

    marc

Please Sign in or register to post replies

Write your reply to:

Draft