Copied to clipboard

Flag this post as spam?

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


  • Peter Laurie 54 posts 148 karma points
    7 days ago
    Peter Laurie
    0

    Umbraco 15 - Custom Block Grid - Displaying Data Type values from a settings element in CMS

    Hi, I have recently been assisted on displaying the slots/areas in a custom Typescript block, which uses Vite and Lit.

    I have not made any progress from this again this week.

    In Umbraco 13, using Angular in the controller of a html document, you can query the $scope and using code, like in the following example, get the values I had made up from the data types to help style up and control the custom block grid html.

    Code in Angular Controller:

    angular.module("umbraco").controller("twoColumnSectionController", function ($scope, mediaResource) {
    
      // get the values from settings and add to scope to display values on the form
      var bg = $scope.block.settingsData.backgroundColour;
      if (typeof bg === 'undefined' || bg === null) {
        $scope.bgcolor = "cmt-bg-none";
      } else {
        console.log("backgroundColour :: ", bg[0]);
        $scope.bgcolor = "cmt-bg-" + bg[0];
      }
    
      var cp = $scope.block.settingsData.containerHPadding;
      if (typeof cp === 'undefined' || cp === null) {
        $scope.containerHPadding = "";
      } else {
        console.log("container padding :: ", cp[0]);
        $scope.containerHPadding = cp[0];
      }
    
      var ai = $scope.block.settingsData.alignItems;
      if (typeof ai === 'undefined' || ai === null) {
        $scope.alignItems = "";
      } else {
        console.log("align items :: ", ai[0]);
        $scope.alignItems = ai[0];
      }
    
    
      var theContainer = "container-xxl";
      var cf = $scope.block.settingsData.containerFluid;
      if (typeof cf === 'undefined' || cf === null) {
        $scope.theContainer = "container-xxl";
      } else {
        console.log("container class :: ", cf[0]);
        if (parseInt(cf[0]) === 1) {
          $scope.theContainer = "container-fluid";
        }
        if (parseInt(cf[0]) === 0) {
          $scope.theContainer = theContainer;
        }
      }
    
    });
    

    Code in html:

    <div ng-controller="twoColumnSectionController">
      <section class="cmt-section-wrap {{containerHPadding}} {{alignItems}} {{bgcolor}}">
        <div class="{{theContainer}}">
          <umb-block-grid-render-area-slots></umb-block-grid-render-area-slots>
        </div>
      </section> 
    </div>
    

    This works a treat. The slots as well, can also be written as, for example:

    <slot name=left"></slot><slot name="right"</slot>
    

    Anyway, I have been assisted, with great thanks, on including the slots in the new .ts equivalent, but I am getting sometimes strange results or no results in getting block grid settings values and being able to use them in the same way in an Umbraco 15 block custom Grid.

    Code of the typescript with the slots:

    import type { UmbBlockEditorCustomViewElement } from '@umbraco-cms/backoffice/block-custom-view';
    //import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element";
    import { html, customElement, LitElement, property, css, state } from '@umbraco-cms/backoffice/external/lit';
    import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
    import type { UmbBlockDataType } from '@umbraco-cms/backoffice/block';
    import { UmbBlockGridTypeModel } from '@umbraco-cms/backoffice/block-grid';
    
    //import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging'; // Import the repository
    //import type { UmbMediaPickerPropertyValue } from '@umbraco-cms/backoffice/media';
    
    
    const elementName = "columns-66-custom-view";
    
    // eslint-disable-next-line local-rules/enforce-umb-prefix-on-element-name
    @customElement(elementName)
    // eslint-disable-next-line local-rules/umb-class-prefix
    export class Columns66
        extends UmbElementMixin(LitElement)
        implements UmbBlockEditorCustomViewElement
    {
    
        @property({ attribute: false })
        content?: UmbBlockDataType;
    
        @property({ attribute: false })
        settings?: UmbBlockDataType;
    
        _styleElement?: HTMLLinkElement;
    
    
        @state()
        rowId: Map<string, string> = new Map();
    
        @state()
        blockType?: UmbBlockGridTypeModel;
    
    
        override render()
        {
    
    
            return html`
    
            <div class="${this.settings?.rowId ? 'align-' + this.settings?.rowId : undefined}">
                    <h5>Row ID snippet</h5>
                    <p>Alignment: ${this.settings?.rowId}</p>
                </div>
    
            ${this.renderSingleSettingWithType('hideSection', 'boolean')}
            ${this.renderSingleSettingWithType('containerFluid', 'boolean')}
            ${this.renderSingleSettingWithType('rowId', 'string')}
            ${this.renderSingleSettingWithType('rowBackgroundColour', 'string')}
            ${this.renderSingleSettingWithType('containerHPadding', 'string')}
            ${this.renderSingleSettingWithType('alignItems', 'string')}
    
                <h5>My Custom View</h5>
                <umb-block-grid-areas-container
                .areas="${this.blockType?.areas ?? []}"
                .areaGridColumns="${this.blockType?.areaGridColumns}">
                </umb-block-grid-areas-container>
                <h5>My Custom View - left</h5>
                    <umb-block-grid-entries
                    part="area"
                    class="umb-block-grid__area"
                    .areaKey="right"></umb-block-grid-entries>
                <h5>My Custom View - right</h5>
                <umb-block-grid-entries
                    part="area"
                    class="umb-block-grid__area"
                    .areaKey="right"></umb-block-grid-entries>
                <umb-block-grid-entries
                    part="area"
                    class="umb-block-grid__area"
                    .areaKey=${this.blockType?.areas[0].key}
                    .layoutColumns=${this.blockType?.areas[0].columnSpan}></umb-block-grid-entries>
                 <umb-block-grid-entries
                    part="area"
                    class="umb-block-grid__area"
                    .areaKey=${this.blockType?.areas[1].key}
                    .layoutColumns=${this.blockType?.areas[1].columnSpan}></umb-block-grid-entries>
    
                `;
    
        }
    
        private renderSingleSettingWithType(key: string, type: 'string' | 'boolean' | 'integer')
        {
            if (!this.settings) return html`<p>${key} is: undefined</p><p>${key} is value: undefined</p>`;
    
            let value = this.settings[key];
    
            console.log("settings key and value :: ", this.settings, key, value);
    
            switch (type)
            {
                case 'boolean':
                    value = (value as boolean) ? 'true' : 'false';
                    break;
                case 'integer':
                    value = parseInt(value as string, 10);
                    break;
                case 'string':
                default:
                    value = (value as string)?.toString() ?? 'undefined';
                    break;
            }
    
            return html`
            <p><strong>${key} is value: ${value}</strong></p>
        `;
        }
    
        //private renderSingleSetting(key: string)
        //{
        //    if (!this.settings) return html`<p>${key} is: undefined</p><p>${key} is value: undefined</p>`;
    
        //    const value = this.settings[key];
        //    return html`
        //    <p>${key} is: ${value ?? 'undefined'}</p>
        //    <p>${key} is value: ${value}</p>
        //`;
        //}
    
        //private renderSettings()
        //{
        //    if (!this.settings) return html``;
    
        //    return Object.keys(this.settings ?? {}).map(key => html`
        //    <p>${key} is: ${this.settings?.[key] ?? 'undefined'}</p>
        //    <p>${key} is value: ${this.settings?.[key]}</p>
        //`);
        //}
    
        static styles = [
            css`
                a {
                  display: block;
                  color: inherit;
                  text-decoration: inherit;
                  border: 1px solid transparent;
                  border-radius: 2px;
                }
    
                a:hover {
                    border-color: var(--uui-color-interactive-emphasis, #3544b1);
                }
    
                .preview-alert {
                    background-color: var(--uui-color-danger, #f0ac00);
                    border: 1px solid transparent;
                    border-radius: 0;
                    margin-bottom: 20px;
                    padding: 8px 35px 8px 14px;
                    position: relative;
    
                    &, a, h4 {
                        color: #fff;
                    }
    
                    pre {
                        white-space: normal;
                    }
                }
    
                .preview-alert-warning {
                    background-color: var(--uui-color-danger, #f0ac00);
                    border-color: transparent;
                    color: #fff;
                }
    
                .preview-alert-info {
                    background-color: var(--uui-color-default, #3544b1);
                    border-color: transparent;
                    color: #fff;
                }
    
                .preview-alert-danger, .preview-alert-error {
                    background-color: var(--uui-color-danger, #f0ac00);
                    border-color: transparent;
                    color: #fff;
                }
            `
        ]
    }
    
    export default Columns66;
    
    declare global
    {
        interface HTMLElementTagNameMap
        {
            [elementName]: Columns66;
        }
    }
    

    Code of the Typescript for the feature:

    import { html, customElement, LitElement, property, css } from '@umbraco-cms/backoffice/external/lit';
    import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
    import type { UmbBlockDataType } from '@umbraco-cms/backoffice/block';
    import type { UmbBlockEditorCustomViewElement } from '@umbraco-cms/backoffice/block-custom-view';
    
    const elementName = "example-block-custom-view";
    
    // eslint-disable-next-line local-rules/enforce-umb-prefix-on-element-name
    @customElement(elementName)
    // eslint-disable-next-line local-rules/umb-class-prefix
    export class ExampleBlockCustomView
        extends UmbElementMixin(LitElement)
        implements UmbBlockEditorCustomViewElement
    {
    
        @property({ attribute: false })
        content?: UmbBlockDataType;
    
        @property({ attribute: false })
        settings?: UmbBlockDataType;
    
        override render()
        {
            return html`
                <div class="${this.settings?.blockAlignment ? 'align-' + this.settings?.blockAlignment : undefined}">
                    <h5>My Custom View</h5>
                    <p>Name: ${this.settings?.featureName}</p>
                    <p>Detals: ${this.settings?.featureDetails}</p>
                </div>
            `;
        }
    
        static override styles = [
            css`
                :host {
                    display: block;
                    height: 100%;
                    box-sizing: border-box;
                    background-color: #fff;
                    border-radius: 0px;
                    padding: 0px;
                }
    
                .align-center {
                    text-align: center;
                }
                .align-right {
                    text-align: right;
                }
            `,
        ];
    }
    
    export default ExampleBlockCustomView;
    
    declare global
    {
        interface HTMLElementTagNameMap
        {
            [elementName]: ExampleBlockCustomView;
        }
    }
    

    This is what is rendered. I use the alias of the settings value, but the same value is repeated for each!

    Render of block grid in cms

    I thought I would put in the Feature block on its own, and not within the Layout block to see if that would render the correct values, but no, that still repeated the first value I put in:

    entering values

    resulting preview

    Thank you again for any feedback.

    I think I need to start learning Typescript from the start to begin to understand this properly. Check out the new Backend Typescript Umbraco code to see how it is put together. It is a shame that the Umbraco documentation on this is as yet very shallow, in Our Umbraco, with no thorough how to to display rich text, strings, text areas and Umbraco url pickers in custom Views.

    Kind regards,

    Pete

  • Afreed 64 posts 277 karma points
    4 days ago
    Afreed
    0

    Hi peter,

    I think that is an issue, all the property values are inherited from the first one. Will have to look into this for the root cause though.

  • John P Scott 15 posts 72 karma points
    1 day ago
    John P Scott
    0

    Hi Peter/Alfreed,

    I also came to this conclusion while working on a similar project. I have raised this as an issue on the Umbraco GIT site, but as yet no takers.

    https://github.com/umbraco/Umbraco-CMS/issues/17989

    regards

    John

  • Peter Laurie 54 posts 148 karma points
    1 day ago
    Peter Laurie
    0

    Hi Afreed/John, Thank you for your feedback.

    I have tried other methods to get the actual values for the settings alias / parameter name or alias:

    private renderSettings()
        {
            const settings = [
                { label: 'hideSection', value: this.settings?.hideSection },
                { label: 'containerFluid', value: this.settings?.containerFluid },
                { label: 'rowId', value: this.settings?.rowId },
                { label: 'rowBackgroundColour', value: this.settings?.rowBackgroundColour },
                { label: 'containerHPadding', value: this.settings?.containerHPadding },
                { label: 'alignItems', value: this.settings?.alignItems }
            ];
    
            return settings.map(setting => html`
                <p>${setting.label} is: ${setting.value ?? 'undefined'}</p>
                <p>${setting.label} is value: ${setting.value}</p>
            `);
        }
    

    I also had an issue in my manifest, when, if I did not put in the forContentTypeAlias, the first block declared in the manifest would be applied to all the block grids.

    So that has made me think that also maybe I simply do know know how to put in the name or alias to get the value - Just for this block grid, maybe there is required a block grid name - I wondered if the Uid, or Key of the block grid item being displayed was needed somewhere in the code that grabs the values so it know what to grab, not just the first value: grid Key or Uid or something > property alias/name to get that value for this item in the grid.

    I am waffling a bit, but I hope you see what I mean. I may put in to their errors and bugs list myself. They have been very good in the past responding and fixing bugs and issues I have discovered.

    Kind regards,

    Pete

Please Sign in or register to post replies

Write your reply to:

Draft