Copied to clipboard

Flag this post as spam?

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


  • Wesley 7 posts 77 karma points
    Nov 25, 2021 @ 13:16
    Wesley
    0

    Get BlocklistItem values from datalist

    I've tested some of the options and decided to add our DataListSource In the backend everthing works like a charm. I can pick the Element types I want and it shows.

    When I want to show the name and quote in a partial view I only get back a List<Guid>. Is there an alternative way to pick BlockListItems and retrieve them in a partial view?

    public class UmbracoContentTestimonialsDataListSource : IDataListSource, IDataListSourceValueConverter
    {
        private readonly IUmbracoContextAccessor umbracoContextAccessor;
    
        public Dictionary<string, object> DefaultValues => new Dictionary<string, object>();
    
        public IEnumerable<ConfigurationField> Fields => new ConfigurationField[0];
    
        public OverlaySize OverlaySize => OverlaySize.Small;
    
        public string Name => "Umbraco Content Filters";
    
        public string Description => "Get all testimonials from Umbraco Content.";
    
        public string Icon => "icon-thumbnail-list";
    
        public string Group => "Custom Data Source";
    
        public UmbracoContentTestimonialsDataListSource(IUmbracoContextAccessor umbracoContextAccessor)
        {
            this.umbracoContextAccessor = umbracoContextAccessor;
        }
    
        public IEnumerable<DataListItem> GetItems(Dictionary<string, object> config)
        {
            var dataListItems = new List<DataListItem>();
            var umbracoContext = this.umbracoContextAccessor.UmbracoContext;
    
            if (umbracoContext.Content.GetAtRoot().FirstOrDefault() is Home home &&
                home.Descendants<TestimonialOverview>().FirstOrDefault() is TestimonialOverview testimonialOverview &&
                testimonialOverview.Testimonials.OfType<BlockListItem<Testimonial>>() is IEnumerable<BlockListItem<Testimonial>> testimonials)
            {
                foreach (var testimonial in testimonials)
                {
                    dataListItems.Add(new DataListItem()
                    {
                        Name = testimonial.Content.FullName,
                        Description = testimonial.Content.Quote.ToString(),
                        Value = testimonial.Content.Key.ToString()
                    });
                }
            }
    
            return dataListItems;
        }
    
        public Type GetValueType(Dictionary<string, object> config) => typeof(Guid);
    
        public object ConvertValue(Type type, string value)
        {
            if (!string.IsNullOrEmpty(value) &&
                Guid.TryParse(value, out var elementKey))
            {
                return elementKey;
            }
    
            return null;
        }
    }
    

    Here some screenshots from the backend. The datatype with results

    picker on the page

    I think the problem is with the Value because that's a Guid. I can imagine the way I'm doing it now isn't the best way. Is there an option to get BlockListItems from another page or not? Or is it just easier to make every testimonial a node with a NoUrlProvider?

  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Nov 25, 2021 @ 13:52
    Lee Kelleher
    100

    Hi Wesley,

    Do you mean how to get the Testimonial item on the frontend (Razor)?

    If so, because you'll only have the Guid value, it'd be a case of locating the container node, looping over the BlockList items to find the matching item/Guid.

    Initially it doesn't sound very performant, but a static dictionary lookup/cache could be used to retrieve the Testimonial items?

    But I'm just thinking out loud, I haven't tried that scenario myself.

    Cheers,
    - Lee

  • Wesley 7 posts 77 karma points
    Nov 25, 2021 @ 13:59
    Wesley
    0

    Hi Lee,

    Thanks for your (quick) response! And that's what I mean indeed. I need the Testimonial in my Razor View.

    Each testimonial got a Full name (TextString) Quote (Richtext editor) and an Image (New Media Picker).

    And if you state it like that it isn't very performant indeed. The initial thought was to create nodes and use a NoUrlComposer but if we've got a lot of testimonials the node count adds up on the Umbraco Cloud package (which is unnecessary if you can pick it as BlockList.

    I'm not very familiar with the static dictionary/look/cache. Do you maybe have an example of this?

    Thanks, Wesley

  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Nov 25, 2021 @ 14:25
    Lee Kelleher
    0

    Hi Wesley,

    Off the top of my head, it could be done as an extension method to UmbracoHelper, something like this (based on your code from above)...

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Umbraco.Core.Models.Blocks;
    using Umbraco.Core.Models.PublishedContent;
    
    namespace Umbraco.Web.Mvc
    {
        public static class UmbracoHelperExtensions
        {
            private static readonly Dictionary<Guid, Testimonial> _lookup = new Dictionary<Guid, Testimonial>();
    
            public static Testimonial GetTestimonial(this UmbracoHelper helper, Guid key)
            {
                if (_lookup.Count == 0 &&
                    helper.ContentAtRoot() is Home home &&
                    home.Descendants<TestimonialOverview>().FirstOrDefault() is TestimonialOverview testimonialOverview &&
                    testimonialOverview.Testimonials as IEnumerable<BlockListItem<Testimonial>> testimonials)
                {
                    foreach (var testimonial in testimonials)
                    {
                        if (_lookup.ContainsKey(testimonial.Content.Key) == false)
                        {
                            _lookup.Add(testimonial.Content.Key, testimonial);
                        }
                    }
                }
    
                return _lookup.ContainsKey(key) == true
                    ? _lookup[key]
                    : null;
            }
        }
    }
    

    Note, I haven't tested this, excuse any typos or broken code, it's just something I quickly coded up. 😆

    Then you would be able to call @Umbraco.GetTestimonial(item.Value) in your partial-views.

    One pain point of this is that the static lookup would be caching the testimonial instances at the time of population. Meaning that if you updated a testimonial in the CMS, then the old version would still be cached. The cache would be invalidated on web-app restart, but if you need cache invalidation, then a better solution would probably need to be fleshed out.

    Hope this helps get you in the right direction?

    Cheers,
    - Lee

  • Wesley 7 posts 77 karma points
    Nov 25, 2021 @ 14:30
    Wesley
    0

    Thanks Lee!

    I'll try that.

    For now I've tested the (not very performant) loop version. It gets the content I want and is fast for now but if the number of testimonial grows the website gets slower...

    @if (Model.Content.Testimonials is List<Guid> testimonialGuids && testimonialGuids.Any())
        {
            foreach (Guid testimonialGuid in testimonialGuids)
            {
                foreach (BlockListItem<Testimonial> testimonial in testimonialOverview.Testimonials.Where(t => t.Content.Key == testimonialGuid))
                {
                    <h2>@testimonial.Content.Quote.ToString()</h2>
                    <h2>@testimonial.Content.FullName</h2>
                }
            }
        }
    
Please Sign in or register to post replies

Write your reply to:

Draft