ABOUT ME

Salesforce 개발자의 기록

Today
Yesterday
Total
  • [LWC] 실습 4) lightning-datatable에 picklist 필드와 lookup 필드 함께 표시하기 - How to display lookup field and picklist field in datatable
    LWC 2024. 10. 2. 17:53


     

     

    실습 계획

    이번시간은 실습 4) custom lightning-datatable에 lookup 필드와 picklist 필드 함께 표시하기

     

     

    실습 1) lightning-datatable 생성하기

    실습 2) custom lightning-datatable에 picklist field 표시하기

    실습 3) custom lightning-datatable에 lookup field 표시하기

    실습 4) custom datatable에 lookup 필드와 picklist 필드 함께 표시하기

    실습 5) datatable 속성 설정 및 기능 구현

     

     

     


     

    0. 컴포넌트 구조 확인

    • 앞선 실습 내용들 참고
      1.  

     

     


     

     

     

    1. lookupColumn.cmp

    • 조회필드 편집화면처럼 보여줄 컴포넌트

     

    1-1 : lookupColumn.html

    <template>
        <div class="lookupcontainer" id="lookup">
            <div if:true={showLookup} class="lookup-container">
                <div tabindex="0" class="container" style="position:fixed">
                <lightning-record-edit-form object-api-name={object}>
                    <lightning-input-field
                        class="slds-popover slds-popover_edit slds-popover__body"
                        field-name={fieldName}
                        value={value}
                        variant='label-hidden'
                        onchange={handleChange}
                        data-id="input"
                        >
                    </lightning-input-field>
                </lightning-record-edit-form>
     
                </div>
            </div>
     
            <div if:false={showLookup} class="slds-table_edit_container slds-is-relative">
                <span class="slds-grid slds-grid_align-spread slds-cell-edit slds-align_absolute-center">
                <span class="slds-truncate" title={lookupName}>
                    <lightning-formatted-url value={lookupValue} label={lookupName} target={target}>
                    </lightning-formatted-url>
                </span>
                <button data-id={context} class="slds-button slds-button_icon slds-cell-edit__button slds-m-left_x-small" tabindex="-1"
                    title="Edit" onclick={handleClick}>
                    <svg class="slds-button__icon slds-button__icon_hint slds-button__icon_lock slds-button__icon_small slds-button__icon_edit slds-icon slds-icon-text-default slds-icon_xx-small"
                        aria-hidden="true">
                        <use xlink:href="/_slds/icons/utility-sprite/svg/symbols.svg?cache=9.37.1#edit"></use>
                    </svg>
                    <span class="slds-assistive-text">Edit</span>
                </button>
                </span>
            </div>
        </div>
    </template>

     

    1-2 :lookupColumn.js

    • Setup > 정적자원 등록 필요 > 이름 :  LWCDatatableLookup

    LWCDatatableLookup.css
    0.00MB

    import { LightningElement, api, track, wire } from 'lwc';
    import { loadStyle } from 'lightning/platformResourceLoader';
    import LWCDatatableLookup from '@salesforce/resourceUrl/LWCDatatableLookup';
    import { getRecord } from "lightning/uiRecordApi";
     
    export default class LookupColumn extends LightningElement {
        @api value;
        @api fieldName;
        @api object;
        @api context;
        @api name;
        @api fields;
        @api target;
        @track showLookup = false;
     
        @wire(getRecord, { recordId: '$value', fields: '$fields' })
        record;
     
        getFieldName() {
            let fieldName = this.fields[0];
            fieldName = fieldName.substring(fieldName.lastIndexOf('.') + 1, fieldName.length);
            return fieldName;
        }
     
        get lookupName() {
            return (this.value != undefined && this.value != '' && this.record.data != null) ?  this.record.data.fields[this.getFieldName()].value : '';
        }
     
        get lookupValue() {
            return (this.value != undefined && this.value != '' && this.record.data != null && this.record.data.fields[this.getFieldName()].value) ? '/' + this.value : '';
        }
     
        renderedCallback() {
            Promise.all([
                loadStyle(this, LWCDatatableLookup),
            ]).then(() => { });
     
            let container = this.template.querySelector('div.container');
            container?.focus();
     
            window.addEventListener('click', (evt) => {
               if(container == undefined){
                   this.showLookup = false;
               }
            });
        }
     
        handleChange(event) {
            this.value = event.detail.value[0];
            if(this.value == undefined){
                this.record.data = null;
            }

            this.dispatchEvent(new CustomEvent('lookupchanged', {
                composed: true,
                bubbles: true,
                cancelable: true,
                detail: {
                    data: { context: this.context, value: this.value }
                }
            }));    

        }
     
        handleClick(event) {
            setTimeout(() => {
                this.showLookup = true;
            }, 100);
        }
    }

     

    1-3 : lookupColumn.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
        <apiVersion>61.0</apiVersion>
        <isExposed>false</isExposed>
    </LightningComponentBundle>

     

    1-4 : lookupColumn.css

    .lookup-section{
        margin-top: -0.6rem;
        margin-left: -0.5rem;
        position: absolute!important;
        z-index: 9999999999999999999999;
    }
     
    .lookup-section .slds-dropdown{
        position: fixed !important;
        max-height: 120px;
        max-width: fit-content;
        overflow: auto;
    }

     

     

    2. customDatatableAll.cmp

    • extends Lightning-datatable 컴포넌트로서 custom type으로 조회, 선택목록 필드를 보여주기 위한 custom 컴포넌트

     

     

    2-1 : customDatatableAll.html

    <template>
       
    </template>

     

     

     

    2-2 : customDatatableAll.js

    import { LightningElement } from 'lwc';
    import LightningDatatable from 'lightning/datatable';
    import lookupColumn from './lookupColumn.html';
    import picklistColumn from './picklistColumn.html';
    import pickliststatic from './pickliststatic.html';

    export default class CustomDatatableTypeAll extends LightningDatatable {

        static customTypes = {
            picklistColumn: {
                template: pickliststatic,
                editTemplate: picklistColumn,
                standardCellLayout: true,
                typeAttributes: ['label', 'placeholder', 'options', 'value', 'context', 'variant', 'name']
            },
            lookupColumn: {
                template: lookupColumn,
                standardCellLayout: true,
                typeAttributes: ['value', 'fieldName', 'object', 'context', 'name', 'fields', 'target' ]
            }
        }

    }

     

     

    2-3 : customDatatableAll.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
        <apiVersion>61.0</apiVersion>
        <isExposed>false</isExposed>
    </LightningComponentBundle>

     

     

    2-4 : lookupColumn.html

    <template>
        <c-lookup-column
            name={typeAttributes.name}
            value={typeAttributes.value}
            field-name={typeAttributes.fieldName}
            object={typeAttributes.object}
            context={typeAttributes.context}
            fields={typeAttributes.fields}
            target={typeAttributes.target}
           >
        </c-lookup-column>
    </template>

     

    2-5 : picklistColumn.html

    <template>
        <lightning-combobox
            name="picklist"
            data-inputable="true"
            label={typeAttributes.label}
            value={editedValue}
            placeholder={typeAttributes.placeholder}
            options={typeAttributes.options}
            variant='label-hidden'
            dropdown-alignment="auto"
            >
        </lightning-combobox>
    </template>

     

    2-6 : pickliststatic.html

    <template>
        <span class="slds-truncate" title={value}>{value}</span>
    </template>

     

     

     

     

    3. oppItemManagementEdit.cmp

    • custom datatable 및 메인 데요소들이 표시되는 메인 화면 컴포넌트

     

    3-1 : oppItemManagementEdit.html

     <template>

        <section role="dialog" tabindex="-1" aria-modal="true" aria-labelledby="modal-heading-01" class="slds-modal slds-fade-in-open slds-modal_large">
            <div class="slds-modal__container">

            <lightning-button-icon icon-name="utility:close" onclick={fnCloseModal} alternative-text="close" variant="bare-inverse" class="slds-modal__close" ></lightning-button-icon>

            <!-- 모달 헤드 -->
            <div class="slds-modal__header">
                <h2 id="modal-heading-01" class="slds-modal__title slds-hyphenate">기회제품 편집</h2>
            </div>

            <div style="background-color: white; ">
                    <div class="datatable-container" onitemregister={handleRegisterItem}>

                        <c-custom-datatable-type-all
                            key-field="Id"
                            data={data}
                            columns={columns}
                            draft-values={draftValues}
                        >
                        </c-custom-datatable-type-all>
                       
                    </div>
            </div>

             <!-- 모달 푸터 -->
             <div class="slds-modal__footer">
                <lightning-button name="close" label="닫기" class="slds-float_right" onclick={fnCloseModal}></lightning-button>
            </div>


        </div><!--modal container-->
    </section>


    <div class="slds-backdrop slds-backdrop_open" role="presentation"></div>


    </template>

     

    3-2 : oppItemManagementEdit.js

    import { LightningElement, track, wire, api } from 'lwc';
    import getOppItems from '@salesforce/apex/OppItemManagementController.getOppItems';
    import { CurrentPageReference, NavigationMixin } from 'lightning/navigation';

    import { getPicklistValues } from 'lightning/uiObjectInfoApi';
    import FIELD_testSelecteList from '@salesforce/schema/OpportunityLineItem.testSelectList__c';

    //4-1 : columns에 조회 필드 추가
    const columns=[
        { label: '제품 Id',          fieldName : 'Product2Id',         type: 'text', hideDefaultActions : "true",  editable: true, },
        { label: '제품명',          fieldName : 'Product2.Name',         type: 'text', hideDefaultActions : "true",  editable: true, },
        { label: '수량',            fieldName : 'Quantity',             type: 'decimal', hideDefaultActions : "true", editable: true, cellAttributes:{ class: { fieldName: 'quantityCellClass' } }},
        { label: '판매가격',        fieldName : 'UnitPrice',            type: 'currency', hideDefaultActions : "true", editable: true, typeAttributes: { currencyCode: 'KRW', style: 'currency', minimumFractionDigits: 0 } },
        { label: '설명',            fieldName : 'Description',         type: 'text', hideDefaultActions : "true",  editable: true, },
        {
            label: '고객사',
            fieldName: 'Account__c',
            type: 'lookupColumn',
            typeAttributes: {
                object: 'OpportunityLineItem',
                fieldName: 'Account__c',
                value: { fieldName: 'Account__c' },
                context: { fieldName: 'Id' },
                name: 'Account',  
                fields: ['Account.Name'],
                target: '_self'
            },
            editable: false
        },
        {
            label: '제품 등급',
            fieldName: 'testSelectList__c',
            type: 'picklistColumn',
            editable: true,
            typeAttributes: {
                placeholder: '== 선택 ==',
                options: { fieldName: 'pickListOptions' },
                value: { fieldName: 'testSelectList__c' },
                context: { fieldName: 'Id' }
            },
            hideDefaultActions : "true"
        }
    ];


     
    export default class OppItemManagementEdit extends NavigationMixin(LightningElement) {
        columns = columns;
       
        @track data = [];
        @track oppItemList;
        @track draftValues = [];
        @api oppId;
       
       

        //lwc를 호출한 url에서 파라미터 값 가져오기
        @wire(CurrentPageReference)
        setCurrentPageReference(currentPageReference) {
            if (currentPageReference) {
                const state = currentPageReference.state;
                const newOppId = state.c__oppId;
                if (newOppId !== this.oppId) {
                    this.oppId = newOppId;
                    this.fetchOppItems();
                }
            }
        }
     
        //매입상태 선택목록 필드 option 값 세팅-  ( recordTypeId 전달 값 체크 )
        @track pickListOptions;
        @wire(getPicklistValues, { recordTypeId: "012000000000000AAA", fieldApiName: FIELD_testSelecteList })
        wirePickList({ error, data }) {
            if (data) {
                this.pickListOptions = data.values;
                // oppId가 설정된 경우 기회 항목을 가져옴
                if (this.oppId) {
                    this.fetchOppItems();
                }
            } else if (error) {
                console.error(error);
            }
        }

     
       //기회제품 가져오기
       fetchOppItems() {
       
            if (this.oppId && this.pickListOptions ) {  
            getOppItems({ oppId: this.oppId })
                .then(result => {
                    this.data = JSON.parse(JSON.stringify(result));
                    if (this.data) {
                        this.data.forEach(ele => {
                            ele.pickListOptions = this.pickListOptions;  //제품별(row별)로 선택목록 option값 세팅 해주기
                        });
                    }
                })
                .catch(error => {
                    console.error(error);
                    this.data = undefined;
                });
            }
        }

       


        //기본 실행
        connectedCallback() {
     
        }


        fnCloseModal(){
           
            this[NavigationMixin.Navigate]({
                type: 'standard__recordPage',
                attributes: {
                    recordId: this.oppId,
                    objectApiName: 'Opportunity',
                    actionName: 'view'
                }
            });
        }

       
        lookupChanged(event) {
            console.log('lookupChanged ---- ' + event.detail.data);
            event.stopPropagation();
            let dataRecieved = event.detail.data;
            let accountIdVal = dataRecieved.value != undefined ? dataRecieved.value : null;
            let updatedItem = { Id: dataRecieved.context, Account__c: accountIdVal  };
            console.log(updatedItem);
            this.updateDraftValues(updatedItem);
        }
       
        //임시저장 값 최신화
        updateDraftValues(updateItem) {
            console.log('draftValue 확인 ===== ' + JSON.stringify(this.draftValues));
            let draftValueChanged = false;
            let copyDraftValues = [...this.draftValues];
            copyDraftValues.forEach(item => {
                if (item.Id === updateItem.Id) {
                    for (let field in updateItem) {
                        item[field] = updateItem[field];
                    }
                    draftValueChanged = true;
                }
            });
     
            if (draftValueChanged) {
                this.draftValues = [...copyDraftValues];
            } else {
                this.draftValues = [...copyDraftValues, updateItem];
            }

        }


    }

     

    3-3 : oppItemManagementEdit.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
        <apiVersion>61.0</apiVersion>
        <isExposed>true</isExposed>
        <targets>
            <target>lightning__RecordAction</target>
            <target>lightning__AppPage</target>
            <target>lightning__RecordPage</target>
            <target>lightning__HomePage</target>
            <target>lightning__UrlAddressable</target>
        </targets>
    </LightningComponentBundle>

     

     

     

    5. oppItemManagementController.cls

    • apex class로 조회필드(Account__c)와 선택목록필드(testSelectList__c) 모두 쿼리해오도록 주의
    public with sharing class OppItemManagementController {
        public OppItemManagementController() {}
       
        //기회 제품 리스트 반환
        @AuraEnabled
        public static List<OpportunityLineItem> getOppItems(String oppId){

            List<OpportunityLineItem> oppItems = new List<OpportunityLineItem>();
           
            try {
                //Account__c 조회 필드 추가 함
                oppItems = [
                    SELECT Id, OpportunityId, Product2Id, Product2.Name, PricebookEntryId, PricebookEntry.Name, Quantity, UnitPrice, Description, testSelectList__c, Account__c
                    FROM OpportunityLineItem
                    WHERE OpportunityId = :oppId
                    ORDER BY Id
                ];

                return oppItems;

            } catch (Exception e) {
                throw new AuraHandledException(e.getMessage());
            }
        }
    }

     

     

     

    6. 화면 확인

     


     

     

    실습4번인 지금까지 lightning-datatable에 조회필드와 선택목록필드를 표시해보았다.

     

    실습5번에서는

    lighting-datatable의 속성들을 활용하여

    datatable의 수정사항들과 표시되는 data를 컨트롤하는 부분을 포스팅할 예정이다.

    반응형
Designed by Tistory.