Coding Tutorial: How to display related entities in a side panel

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • telecastg
    Active Community Member
    • Jun 2018
    • 907

    Coding Tutorial: How to display related entities in a side panel

    Use Case:

    We have a "Tenancy" entity that is related to a "CollectionIssue" entity in a one to many relationship.

    We want to display the "CollectionIssues" in a side panel when viewing a single "Tenancy" record similar to how "Tasks" are displayed.

    Click image for larger version  Name:	Side Panel Display.png Views:	20 Size:	76.2 KB ID:	67601

    Step 1:

    Specify in the "Tenancy" clientDefs script, that we want to have a side panel that will contain a list of "CollectionIssues"

    custom/Espo/Custom/Resources/metadata/clientDefs/Tenancy.json
    Code:
     "sidePanels": {
        "detail": [
            {
                "name": "collectionIssues",
                "label": "Collection Issues",
                "view": "custom:views/side-panels/collection-issues",
                "aclScope": "CollectionIssue"
            },
        ]
    },
    Step 2:

    Create the view class that will display the list of "CollectionIssues" in the side panel (specified in the script above)

    client/custom/src/views/side-panels/collection-issues.js
    Code:
    define('custom:views/side-panels/collection-issues', 'views/record/panels/relationship', function (Dep) {
        return Dep.extend({
    
        name: 'collection-issues',
        template: 'custom:record/panels/collection-issues',
        tabList: ['open', 'closed'],
        defaultTab: 'open',
        sortBy: 'createdAt',
        asc: false,
    
        buttonList: [
            {
                action: 'createCollectionIssue',
                title: 'Create Collection Notice',
                acl: 'create',
                aclScope: 'CollectionIssue',
                html: ' < span class="glyphicon glyphicon-plus" > < /span >'
            }
        ],
    
        listLayout: {
            rows: [
                [
                    {
                        name: 'name',
                        link: true,
                    }
                ],
                [
                    {name: 'createdAt'},
                    {name: 'totalAmountDemanded'},
                    {name: 'status'},
                ]
            ]
        },
    
        events: _.extend({
            'click button.tab-switcher': function (e) {
                var $target = $(e.currentTarget);
                this.$el.find('button.tab-switcher').removeClass('active');
                $target.addClass('active');
                this.currentTab = $target.data('tab');
                this.collection.where = this.where = [
                    {
                        type: 'primary',
                       value: this.currentTab
                    }
                ];
                this.listenToOnce(this.collection, 'sync', function () {
                    this.notify(false);
                }.bind(this));
                this.notify('Loading...');
                this.collection.fetch();
                this.getStorage().set('state', this.getStorageKey(), this.currentTab);
            }
        }, Dep.prototype.events),
    
        data: function () {
            return {
                currentTab: this.currentTab,
                tabList: this.tabList
            };
        },
    
        getStorageKey: function () {
            return 'collectionIssues-' + this.model.name + '-' + this.name;
        },
    
        setup: function () {
            this.scope = this.model.name;
            this.link = 'collectionIssues';
            this.currentTab = this.getStorage().get('state', this.getStorageKey()) || this.defaultTab;
            this.where = [
                {
                    type: 'primary',
                    value: this.currentTab
                }
            ];
        },
    
        afterRender: function () {
            var url = this.model.name + '/' + this.model.id + '/' + this.link;
            if (!this.getAcl().check('CollectionIssue', 'read')) {
                this.$el.find('.list-container').html(this.translate('No Access'));
                this.$el.find('.button-container').remove();
                return;
            };
            this.getCollectionFactory().create('CollectionIssue', function (collection) {
                this.collection = collection;
                collection.seeds = this.seeds;
                collection.url = url;
                collection.where = this.where;
                collection.sortBy = this.sortBy;
                collection.asc = this.asc;
                collection.maxSize = this.getConfig().get('recordsPerPageSmall') || 3;
                var rowActionsView = '';
                this.listenToOnce(this.collection, 'sync', function () {
                    this.createView('list', 'views/record/list-expanded', {
                        el: this.getSelector() + '  > .list-container',
                        pagination: false,
                        type: 'listRelationship',
                        rowActionsView: rowActionsView,
                        checkboxes: false,
                        collection: collection,
                        listLayout: this.listLayout,
                    }, function (view) {
                        view.render();
                    });
                }.bind(this));
                this.collection.fetch();
            }, this);
        },
    
        actionCreateCollectionIssue: function (data) {
            var self = this;
            var link = this.link;
            var scope = 'CollectionIssue';
            var foreignLink = this.model.defs['links'][link].foreign;
            this.notify('Loading...');
            var viewName = this.getMetadata().get('clientDefs.' + scope + '.modalViews.edit') || 'views/modals/edit';
            this.createView('quickCreate', viewName, {
                scope: scope,
                relate: {
                    model: this.model,
                    link: foreignLink,
                }
            }, function (newView) {
                newView.render();
                newView.notify(false);
                this.listenToOnce(newView, 'after:save', function () {
                    this.collection.fetch();
                    this.model.trigger('after:relate');
                }, this);
            });
        },
    
        actionRefresh: function () {
            this.collection.fetch();
        },
    
        actionClose: function (data) {
            var id = data.id;
            if (!id) {
                return;
            }
            var model = this.collection.get(id);
            model.save({
                status: 'Closed'
            },
            {
                patch: true,
                success: function () {
                    this.collection.fetch();
                }.bind(this)
             });
        },
    
    });
    });
    Step 3:

    Create the custom template to display the list of CollectionIssues as a side panel

    client/custom/res/templates/record/panels/collection-issues.tpl
    Code:
    < div class="btn-group button-container" >
        {{#each tabList}}
            < button class="btn btn-default all{{#ifEqual ../currentTab this}} active{{/ifEqual}} tab-switcher" data-tab="{{./this}}" > {{translate this scope='CollectionIssue' category='presetFilters'}} < /button >
        {{/each}}
    < /div >
    
    < div class="list-container small" >
        {{{list}}}
    < /div >
    Step 4:

    Clear cache and rebuild
    Last edited by telecastg; 02-14-2021, 07:37 AM.
  • item
    Active Community Member
    • Mar 2017
    • 1476

    #2
    Always nice telecastg .. i imagine where use this
    Thanks.
    If you could give the project a star on GitHub. EspoCrm believe our work truly deserves more recognition. Thanks.​

    Comment

  • rabii
    Active Community Member
    • Jun 2016
    • 1250

    #3
    Very useful. Thanks for sharing.
    How to add ability to view/edit each record using (small detail view) ? is it doable ?

    Thanks
    Rabii
    Rabii
    Web Dev

    Comment

    • telecastg
      Active Community Member
      • Jun 2018
      • 907

      #4
      Hi, yes it is possible but it will require custom coding.

      I am very busy working on an implementation in our system and can't help you but if I run across a usage in our application for this functionality I will post it here.

      If you want to try yourself you can start by checking this view class that provides the functionality for the side panel list (and all other relationship lists for that matter)



      If you manage to implement these changes please post them in the forum to help others that might have the same question in the future.

      Best wishes

      Comment


      • rabii
        rabii commented
        Editing a comment
        Great thanks for your reply. If I manage to do it, I will share it here.

        Have a great day

        Rabii
    Working...