Showing posts with label folder. Show all posts
Showing posts with label folder. Show all posts

Thursday, 10 March 2022

How to retrieve all the files by traversing folder and sub folders on drag and drop using LWC


OUTLINE
While working on one of the requirements for a manufacturing sector solutions customer based out in Atlanta, Georgia, there was a requirement to traverse\navigate folder and sub folders to get all the files dropped by the user.

CHALLENGE Our main challenge was reading each entry in the directory (folder) or sub-directory (subfolder) dropped by the user to retrieve all the files within the directory (folder).

SOLUTION
  • To solve this issue, we've used JavaScript async function where each dataTransfer.item is converted to webkitGetAsEntry and checked whether it is a directory or a file.
  • If it is a fileEntry then it will be stored in array variable but if it is a directory then an async function named as traverseDirectory will be called and Directory entry will passed as a parameter.
  • In traverseDirectory function a reader variable is created and returned a promise, an anonymous function is associated with promise in which we created an array(local) variable and defined a function named as readEntries.
  • readEntries will traverse the directory.
    • if directory is empty then resolve will be return with all the promises.
    • if directory is not empty then further it will check for fileEntry.
      • If it is a fileEntry then it will be pushed into the array (local) variable.
      • If it is not a fileEntry then traverseDirectory will be called recursively and sub directory will be passed as a parameter.
  • And if, any error occurs then reject will be returned with an error.
Sample code is as below:

FileUploader.html

<template> 
    <lightning-card title="File Uploader"> 
        <div class="slds-align_absolute-center slds-p-bottom_medium"> 
            <form id="fileUploadForm"> 
                <div class="slds-form-element slds-align_absolute-center"> 
                    <span class="slds-form-element__label" id="file-selector-primary-label"></span> 
                    <div class="slds-form-element__control"> 
                        <div class="slds-file-selector slds-file-selector_files"> 
                            <div class="slds-file-selector__dropzone slds-has-drag-over slds-grid slds-wrap" 
                                ondrop={dropHandler} ondragover={dragOverHandler}> 
                                <div class="slds-m-around_xx-large"> 
                                    Drop File(s)/Folder 
                                </div> 
                            </div> 
                        </div> 
                    </div> 
                </div> 
            </form> 
        </div> 
        <template if:true={showFilePropertiesModal}> 
            <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" 
                aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open"> 
                <div class="slds-modal__container"> 
                    <header class="slds-modal__header"> 
                        <button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" 
                            title="Close" onclick={handleCloseModal}> 
                            <lightning-icon icon-name="utility:close" alternative-text="close" variant="inverse" 
                                size="small" onclick={handleCloseModal}></lightning-icon> 
                            <span class="slds-assistive-text">Close</span> 
                        </button> 
                        <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Set File Properties 
                        </h2> 
                    </header> 
                    <div class="slds-modal__content slds-var-p-around_large" id="modal-content-id-1"> 
                        <div class="slds-card slds-var-p-around_large"> 
                                <template for:each={files} for:item="file"> 
                                    <div class="slds-grid slds-gutters" key={file.name} > 
                                        <div class='slds-col'> 
                                            <lightning-input label="File Name" type="text" value={file.name}> 
                                            </lightning-input> 
                                        </div> 
                                        <div class='slds-col'> 
                                            <lightning-combobox name="types" label="Document Type" 
                                                placeholder="Select File Type" options={options} 
                                                onchange={handleFileTypeSelectionChange} required> 
                                            </lightning-combobox> 
                                        </div> 
                                    </div> 
                                </template> 
                        </div> 
                    </div> 
                    <footer class="slds-modal__footer">
                        <button class="slds-button slds-button_neutral" onclick={handleCloseModal} 
                            title="Cancel">Cancel</button> 
                        <button class="slds-button slds-button_brand" onclick={handleFileUpload} 
                            title="Upload">Upload</button> 
                    </footer> 
                </div> 
            </section> 
            <div class="slds-backdrop slds-backdrop_open"></div> 
        </template> 
    </lightning-card> 
</template>


FileUploader.Js

import { LightningElement, api, track} from 'lwc'; 

export default class FileUploader extends LightningElement { 
    @api recordId; 
    @track showFilePropertiesModal=false; 
    @track showDocumentPropertiesForm=false; 
    @track files=[]; 
    @api options=[{label:'Print Card', value:'Print_Card'},{label:'Vertices Output', value:'Vertices_Output'}]; 

 
    dragOverHandler(event){ 
        event.preventDefault(); 
    } 

    dropHandler(event){ 
        event.stopPropagation(); 
        event.preventDefault(); 
        this.handleDroppedContent(event.dataTransfer.items); 

    } 

    async handleDroppedContent(items) { 
        var isDirectory = false; 
        var isFile = false; 

        for (let i = 0; i < items.length; i++) { 
            let item = items[i].webkitGetAsEntry(); 
            if (item.isDirectory) { 
                if (isFile) { 
                    this.files = []; 
                    console.log('File and Folder combination are not allowed to upload'); 
                    return; 
                } else if (isDirectory) { 
                    this.files = []; 
                    console.log('Multiple Folders are not allowed to upload'); 
                    return; 
                } 
                else { 
                    isDirectory = true; 
                } 
            } else if (item.isFile) { 
                if (isDirectory) { 
                    this.files = []; 
                    console.log('File and Folder combination are not allowed to upload'); 
                    return; 
                } else { 
                    isFile = true; 
                } 
            } 
        } 
        for (let i = 0; i < items.length; i++) { 
            let item = items[i].webkitGetAsEntry(); 
            if (item) { 
                if (item.isDirectory) { 
                    await this.traverseDirectory(item).then((result) => {                                                 
                        this.pushResultIntoFiles(result); 
                    }); 
                } else if (item.isFile) { 
                    this.files.push(item); 
                } 
            } 
        } 
        this.convertFileEntryToFile();         
        this.handleCachedFile(); 
    } 

    async traverseDirectory(entry) { 

        let _this = this; 
        const reader = entry.createReader(); 
        return new Promise((resolve, reject) => { 
            const iterationAttempts = []; 
            function readEntries() { 

                reader.readEntries((entries) => { 

                    if (!entries.length) { 
                        resolve(Promise.all(iterationAttempts)); 
                    } else { 
                        iterationAttempts.push(Promise.all(entries.map((ientry) => { 

                            if (ientry.isFile) { 
                                return ientry; 
                            } 
                            return _this.traverseDirectory(ientry); 
                        }))); 
                        readEntries(); 
                    } 
                }, error => reject(error)); 
            } 
            readEntries(); 
        }); 
    } 

    handleCachedFile(){ 
        if(this.files.length>0){ 
           this.showFilePropertiesModal=true; 
            this.showDocumentPropertiesForm=true; 
        } 
        else{ 
            console.log('Error'); 
        } 
    } 

    pushResultIntoFiles(result) { 
            result.forEach(element => { 
                if (element.isFile) { 
                    this.files.push(element); 
                } else { 
                    this.pushResultIntoFiles(element); 
                } 
            }); 
    } 

    convertFileEntryToFile() { 
        var promises = []; 
        var tempFile; 
        this.files.forEach(fileEntry => { 
            tempFile = new Promise(resolve => { 
                fileEntry.file(file => { 
                    resolve(file); 
                }); 
            }); 
            promises.push(tempFile); 
        }); 
        Promise.all(promises).then(file => { 
            this.files = []; 
            this.files = file; 
        }); 
    } 

    handleCloseModal(){ 
        this.files=[]; 
        this.showFilePropertiesModal=false; 
        this.showDocumentPropertiesForm=false; 
    } 

    handleFileUpload(){ 
        //Write your file upload code. 
    }            
} 
Output
CONCLUSION
By using promises and file reader effectively in LWC, we should be able to get all the files from directory and its sub-directories.

If you have any questions you can reach out our Salesforce Consulting team here.

Thursday, 27 May 2021

CUSTOM REPORT TYPES NEEDS AND FEATURES

INTRODUCTION 

report is a list of records that meet your defined requirements. It can be sorted, clustered, or shown in a graphical map in Salesforce and is shown in rows and columns. 

Each report is kept in its own folder. Folders can be made public, secret, or exchanged, and read-only or read/write and permissions can be set. Based on roles, permissions, public groups, and license types, you can control who has access to the contents of the folder. You may render a folder public or private so that only the owner has access to it. 


Every report is built around a specific report type. Only records that meet the requirements specified in the report type are shown in reports. One of the most crucial steps in creating a report is deciding on the appropriate report type. You choose the records and fields that will appear in your report when you choose a report type. Let's see what Report Type has to offer...… 


WHAT IS REPORT TYPE ?

A report type is similar to a prototype that simplifies reporting. When making a report, the report type decides which fields and records are accessible. The relationships between a primary object and its associated objects are the basis for this. 

The report type dictates the records are included in the report. When creating a report, the report type is chosen first. A primary object and one or more related objects exist for each report type. All of these items must be linked, either directly or indirectly. 

In Salesforce, there are two forms of report types:- 

  1. Standard Report Types

    Standard Report Types are included by default for standard objects and custom objects that have the “Allow Reports” checkbox selected. Standard Report Types are not customizable and contain standard and custom fields for each object within the report type by default. When an object and/or a relationship are created, standard report forms are created as well. 

    As an example, the primary object in the 'Contacts & Accounts' report type is 'Contacts,' and the associated object is 'Accounts.' 


  2. Custom Report Types

    Custom report types are reporting models that have been designed to make the reporting process more effective. An administrator or a user with the permission to "Manage Custom Report Types" may build Custom Reports. 

    We may define items that will be available in a specific report in custom report types. Custom Report Types can endorse the following object relationships: 

    • Each "A" record must have at least one related "B" record. 
    • "A" records may or may not have related "B" records. 

    The following are described by Report Types: 

    • Objects: Which Objects Can Be Seen by the Report 
    • Object Relationship 
    • Field layout 
    • Default Field 
    • Field name 

    WHY WE NEED CUSTOM REPORT TYPE 

    The first step in creating a report in Salesforce is to choose a report type. Salesforce has a lot of pre-defined Report Types, which is awesome, but they don't always have exactly what we need. Standard report types cannot often have visibility through all the records or fields we need, or standard report types may be too difficult to use effectively.


    You can't change Salesforce's predefined Report Types because they must be consistent with all orgs. Salesforce, fortunately, allows you to build custom report types. When standard report types can't determine which records will be available on reports, custom report types are developed. 


    FEATURES OF CUSTOM REPORT TYPES 

    Custom Report Types make creating nuanced, interactive reports that go beyond traditional Salesforce reports a breeze. We'll need to create custom report styles if we want to create reports that aren't normal. In comparison to regular report types, custom report types provide a number of advantages. Let's look at the features of custom report types in more detail... 
     

    1. Rename the field section folder name 

    The field panel in Salesforce's report builder does a good job of grouping fields, but some fields belong in a separate folder area. It's possible that a completely new custom folder section would be needed. 

    We can change the folder name that grouped all the fields while creating a report.

     

    2. Lookup Fields are a great way to add more fields 

    You can not only add or delete fields related to the objects in the report type, but you can also add additional fields from related objects to the report. 

    Under the object pick list, you'll see a connection that says Add fields related through lookup. When you click this, all of the objects that are connected to the selected object will appear. 

    Now using this feature, we can get value from the related objects. 

     

    Set Default Fields 

    Salesforce will fill the report with a few default fields when you choose a regular report form. This makes the task of creating reports a lot simpler. 

    Users must take some extra precautions when using custom report types since no data has defaulted. However, you can give the report some immediate value by defaulting fields to the report form, giving users a report prototype to work with right away. 

    This allows the user to create a default layout by creating default fields. 

     

    3. Reports with specific fields hidden 

    We may also specify which fields can be recorded using custom report types. 

    Have any old fields you don't want users to be able to access in a report? Perhaps you just want to reduce the number of fields you can report on in order to speed up the report development process. It's possible! 

    Although fields may be omitted, it's worth noting that fields can need to be added to the report form at times. When you add new fields to an item, they don't always appear in the report because they haven't been added to the report type. 

     

    WE'LL LEARN HOW TO CREATE CUSTOM REPORT TYPES AND SET FIELD LAYOUT FOR REPORTS IN OUR NEXT BLOG. 


    If you have any questions you can reach out our Salesforce Consulting team here.