• Ritika Agarwal

Customize Notes and Attachments - Power Apps Portals

In this blog post, I will walk through the steps that can be followed to create a customized experience for the users to view notes and attachments in Power Apps Portals. This will enable Power Apps Portals developers to introduce custom design to the application while working with the data from Microsoft Dataverse.


Problem Statement

How to show attachments on Power Apps Portals? How to build a custom design for showing attachments in Power Apps Portals? How to introduce a slider control in Power Apps Portals pages? How to change the design of timeline control in Power Apps Portals? How to get rid of timeline control in Power Apps Portals and build a user-friendly experience to show attachments?


Solution


To build such a user experience, we will use fetchxml queries to interact with Dataverse and then place the returned output to the HTML code, the base template is taken from here.


Pre-Requisites:

1) Permissions on the relevant tables. You can set up Entity Permissions for this.

2) Pass a reference to the record's unique identifier as a URL parameter with name as id.


HTML


<div class="container">
 <div class="row">
 <div class="MultiCarousel1" data-items="1,3,5,6" data-slide="1" id="MultiCarousel1" data-interval="1000">
 <div class="MultiCarousel1-inner">
{% fetchxml myquery_attachmentsnotes %}
 <fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
 <entity name="annotation">
 <attribute name="subject"></attribute>
 <attribute name="notetext"></attribute>
 <attribute name="filename"></attribute>
 <attribute name="annotationid"></attribute>
 <order attribute="subject" descending="false"></order>
 <link-entity name="cr553_myrequests" from="cr553_myrequestsid" to="objectid" link-type="inner" alias="ac">
 <filter type="and">
 <condition attribute="cr553_myrequestsid" operator="eq" uitype="cr553_myrequests" value="{{request.params['id']}}"></condition>
 </filter>
 </link-entity>
 </entity>
 </fetch>
    {% endfetchxml %}
    {% for resultchild in myquery_attachmentsnotes.results.entities %}
 <div class="item">
 <div class="pad15">
 <a href="/_entity/annotation/{{resultchild.annotationid}}" target="_blank" class="details-link launch-modal">
 <p>{{resultchild.subject}}</p>
 </a>
 </div>
 </div>
    {% endfor %}
</div>
 <button class="btn btn-primary leftLst">&lt;</button>
 <button class="btn btn-primary rightLst">&gt;</button>
 </div>
 </div>
</div>
 

Here, the purple highlighted part in the code needs to be replaced with the logical name of the table and the green highlighted part to be replaced with the reference to the Unique identifier of the record. I am passing this as a URL parameter named id. Note: If you do not have the parameter in the URL, you can pass the unique identifier by adding it manually or through some dynamic operation.


Custom CSS


.MultiCarousel1 { float: left; overflow: hidden; padding: 15px; width: 100%; position:relative; }
 .MultiCarousel1 .MultiCarousel1-inner { transition: 1s ease all; float: left; }
 .MultiCarousel1 .MultiCarousel1-inner .item { float: left;}
 .MultiCarousel1 .MultiCarousel1-inner .item > div { text-align: center; padding:10px; margin:10px; background:#f1f1f1; color:#666;}
 .MultiCarousel1 .leftLst, .MultiCarousel1 .rightLst { position:absolute; border-radius:50%;top:calc(50% - 20px); }
 .MultiCarousel1 .leftLst { left:0; }
 .MultiCarousel1 .rightLst { right:0; }
 
 .MultiCarousel1 .leftLst.over, .MultiCarousel1 .rightLst.over { pointer-events: none; background:#ccc; }

You can add this at the Web Page-level in the advanced tab.


Custom Javascript


$(document).ready(function () {
 var itemsMainDiv = ('.MultiCarousel1');
 var itemsDiv = ('.MultiCarousel1-inner');
 var itemWidth = "";

    $('.leftLst, .rightLst').click(function () {
 var condition = $(this).hasClass("leftLst");
 if (condition)
            click(0, this);
 else
            click(1, this)
    });

    ResCarouselSize();




    $(window).resize(function () {
        ResCarouselSize();
    });

 //this function define the size of the items
 function ResCarouselSize() {
 var incno = 0;
 var dataItems = ("data-items");
 var itemClass = ('.item');
 var id = 0;
 var btnParentSb = '';
 var itemsSplit = '';
 var sampwidth = $(itemsMainDiv).width();
 var bodyWidth = $('body').width();
        $(itemsDiv).each(function () {
            id = id + 1;
 var itemNumbers = $(this).find(itemClass).length;
            btnParentSb = $(this).parent().attr(dataItems);
            itemsSplit = btnParentSb.split(',');
            $(this).parent().attr("id", "MultiCarousel1" + id);


 if (bodyWidth >= 1200) {
                incno = itemsSplit[3];
                itemWidth = sampwidth / incno;
            }
 else if (bodyWidth >= 992) {
                incno = itemsSplit[2];
                itemWidth = sampwidth / incno;
            }
 else if (bodyWidth >= 768) {
                incno = itemsSplit[1];
                itemWidth = sampwidth / incno;
            }
 else {
                incno = itemsSplit[0];
                itemWidth = sampwidth / incno;
            }
            $(this).css({ 'transform': 'translateX(0px)', 'width': itemWidth * itemNumbers });
            $(this).find(itemClass).each(function () {
                $(this).outerWidth(itemWidth);
            });

            $(".leftLst").addClass("over");
            $(".rightLst").removeClass("over");

        });
    }


 //this function used to move the items
 function ResCarousel(e, el, s) {
 var leftBtn = ('.leftLst');
 var rightBtn = ('.rightLst');
 var translateXval = '';
 var divStyle = $(el + ' ' + itemsDiv).css('transform');
 var values = divStyle.match(/-?[\d\.]+/g);
 var xds = Math.abs(values[4]);
 if (e == 0) {
            translateXval = parseInt(xds) - parseInt(itemWidth * s);
            $(el + ' ' + rightBtn).removeClass("over");

 if (translateXval <= itemWidth / 2) {
                translateXval = 0;
                $(el + ' ' + leftBtn).addClass("over");
            }
        }
 else if (e == 1) {
 var itemsCondition = $(el).find(itemsDiv).width() - $(el).width();
            translateXval = parseInt(xds) + parseInt(itemWidth * s);
            $(el + ' ' + leftBtn).removeClass("over");

 if (translateXval >= itemsCondition - itemWidth / 2) {
                translateXval = itemsCondition;
                $(el + ' ' + rightBtn).addClass("over");
            }
        }
        $(el + ' ' + itemsDiv).css('transform', 'translateX(' + -translateXval + 'px)');
    }

 //It is used to get some elements from btn
 function click(ell, ee) {
 var Parent = "#" + $(ee).parent().attr("id");
 var slide = $(Parent).attr("data-slide");
        ResCarousel(ell, Parent, slide);
    }

});

You can add this at the Web Page-level in the advanced tab.


DEMO


In this post, we saw how to customize the Power Apps Portals pages to display notes and attachments in customized style designs. This will help the developers working on Power Apps Portals to provide a better user experience to the users.


I hope this was useful to you. In case of any questions or suggestions, feel free to reach out on Twitter at @agarwal_ritika.

Recent Posts

See All

Understanding Header in Power Apps Portals

In this blog post, I will break out the components associated with the Header in Power Apps Portals. Problem Statement How to customize header in a Power Apps Portals? How to hide/show navigations in