0

My idea of implementation is based on - Evaluation of approach to navigate between 3 LWC pages

I have 2 pages currently. Each page is a component. The parent component(InterviewProcess) contains these two child components, and I am traversing between them using next / previous button.

Page 1 : LWC - <c-initiate-interview>

<template>
        <div class="slds-grid slds-wrap">
            <div class="slds-col">
                <div class="slds-m-top_medium slds-m-bottom_x-large">
                    <div class="slds-p-around_medium lgc-bg">
                        <lightning-input type="text" name="firstName" label="First Name" placeholder="type here..." onchange={handleFormInputChange} required>
                        </lightning-input>
                    </div>
                    <div class="slds-p-around_medium lgc-bg">
                        <lightning-input type="text" name="lastName" label="Last Name" placeholder="type here..." onchange={handleFormInputChange} required>
                        </lightning-input>
                    </div>
                    <div class="slds-p-around_medium lgc-bg">
                        <lightning-input type="text" name="company" label="Current Company" placeholder="type here..." onchange={handleFormInputChange}>
                        </lightning-input>
                    </div>
                    <div class="slds-p-around_medium lgc-bg">
                        <lightning-textarea name="notes" label="Notes" placeholder="type here..." onchange={handleFormInputChange}></lightning-textarea>
                    </div>
                    <div class="slds-p-around_medium lgc-bg">
                        <lightning-input type="file" label="Attachment" accept="image/png, image/jpg, .zip" onchange={handleFormInputChange}></lightning-input>
                    </div>
                </div>
            </div>
            <div class="slds-col">
                <div class="slds-m-top_medium slds-m-bottom_x-large">
                    <div class="slds-p-around_medium lgc-bg">
                        <lightning-dual-listbox name="roles" label="Roles" source-label="Available"
                            selected-label="Selected" field-level-help="Select role for the Interview" options={options}
                            onchange={handleFormInputChange}></lightning-dual-listbox>
                    </div>
                    <div class="slds-p-around_medium lgc-bg">
                        <lightning-combobox name="level" label="Difficulty Level" value="" placeholder="Select Level"
                            options={levels} onchange={handleFormInputChange} required></lightning-combobox>
                    </div>
                </div>
            </div>
        </div>
</template>

JS:

import { LightningElement, wire } from 'lwc';

import { NavigationMixin, CurrentPageReference } from 'lightning/navigation';
export default class InitiateInterview extends LightningElement {

    roles = [];
    level = 'beginner';
    firstName;
    lastName;
    company = '';
    notes = '';

    // Files
    fileName;
    fileContent = '';

    get options() {
        return [
            { label: 'Developer', value: 'Developer' },
            { label: 'Technical Architect', value: 'Technical Architect' },
            { label: 'Solution Architect', value: 'Solution Architect' },
            { label: 'Business Analyst', value: 'Business Analyst' },
            { label: 'Scrum Master', value: 'Scrum Master' },
            { label: 'PM', value: 'PM' },
        ];
    }

    get levels() {
        return [
            { label: 'Beginner', value: 'Beginner' },
            { label: 'Intermediate', value: 'Intermediate' },
            { label: 'Advanced', value: 'Advanced' },
        ];
    }

    async handleFormInputChange(event) {
        switch (event.target.name) {
            case 'firstName':
                this.firstName = event.target.value;
                break;
            case 'lastName':
                this.lastName = event.target.value;
                break;
            case 'company':
                this.company = event.target.value;
                break;
            case 'notes':
                this.notes = event.target.value;
                break;
            case 'roles':
                this.roles = event.target.value;
                break;
            case 'level':
                this.level = event.target.value;
                break;
        }

        if (event.target.files) {
            this.fileName = event.target.files[0].name;
            this.fileContent = await this.readFile(event.target.files[0]);
        }

        this.createInitiateInterviewEvent();
    }


    readFile(file) {
        return new Promise((resolve, reject) => {
            const fileReader = new FileReader();
            fileReader.onload = () => {
                resolve(fileReader.result.split(/base64,/)[1]);
            };
            fileReader.onerror = () => {
                reject(fileReader.error);
            };
            fileReader.readAsDataURL(file);
        });
    }

    createInitiateInterviewEvent() {
        let isValid = false;
        if ((typeof this.firstName != 'undefined' && this.firstName.length != 0) &&
            typeof this.lastName != 'undefined' && this.lastName.length != 0) {
            isValid = true;
        }
        const nextEvnt = new CustomEvent('initiateinterview', {
            detail: {
                page: "1", firstName: this.firstName, lastName: this.lastName, company: this.company, reqValidation: isValid,
                notes: this.notes, roles: this.roles, level: this.level, fileName: this.fileName, file: this.fileContent
            }
        });
        this.dispatchEvent(nextEvnt);
    }
}

Page 2: <c-interview-questions>

<template>
    Interview Question Page
</template>

JS

import { LightningElement } from 'lwc';
export default class InterviewQuestions extends LightningElement {}

Parent Component: InterviewProcess.html

<template>
    <lightning-card title="Interview" icon-name="standard:insights">
        <template if:true={isInitiateInterviewPage}>
            <c-initiate-interview oninitiateinterview={initiateInterview}></c-initiate-interview>
        </template>
        <template if:true={isInterviewQuesPage}>
            <c-interview-questions></c-interview-questions>
        </template>
        <footer role="contentinfo" class="slds-p-around_medium">
            <div class="slds-align_absolute-center ">
                <lightning-button variant="brand" label="Cancel" title="Cancel" onclick={handleCancel}
                    icon-position="right" class="slds-m-left_x-small"></lightning-button>
                <lightning-button variant="brand" label="Previous" title="Previous" onclick={handlePrevious}
                    icon-position="right" class="slds-m-left_x-small"></lightning-button>
                <lightning-button variant="brand" label="Next" title="Next" onclick={handleNext} icon-position="right"
                    class="slds-m-left_x-small"></lightning-button>
            </div>
        </footer>
    </lightning-card>
</template>

JS:

import { LightningElement } from 'lwc';
import createInterview from '@salesforce/apex/InitiateInterviewController.createInterview';
import { ShowToastEvent } from 'lightning/platformShowToastEvent'
import { reduceErrors } from 'c/ldsUtils';
export default class InterviewProcess extends LightningElement {

    interviewInputs;
    isInterviewQuesPage = false;
    isInitiateInterviewPage = true;
    page = 1;
    initiateInterview(event) {
        console.log('handling event in parent=> ' + JSON.stringify(event.detail));
        this.interviewInputs = event.detail;
    }

    handlePrevious(event) {
        this.isInitiateInterviewPage = true;
        this.isInterviewQuesPage = false;
        **// pre populate the values of previous page**
    }

    handleNext() {
        if (this.interviewInputs) {
            if (this.interviewInputs.reqValidation) {
                createInterview({ candidateData: JSON.stringify(this.interviewInputs) })
                    .then(result => {
                        this.isInterviewQuesPage = true;
                        this.isInitiateInterviewPage = false;
                        this.dispatchEvent(
                            new ShowToastEvent({
                                title: result,
                                message: result,
                                variant: 'success'
                            })
                        );
                    })
                    .catch(error => {
                        this.error = error;
                        this.dispatchEvent(
                            new ShowToastEvent({
                                title: 'Error Occured',
                                message: reduceErrors(error).join(', '),
                                variant: 'error'
                            })
                        );
                    });
            } else {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Please fill in the required fields',
                        variant: 'error'
                    })
                );
            }

        }
    }
}

Apex Controller: InitiateInterviewController.createInterview , Basically gets the JSON String and deserialize it. I plan to insert/update the Contact and Interview Object using the JSOn string and return some output [TBD].

Problem here: When I traverse from Page 2 to Page 1 , I am looking at an approach that will help me populate the values that user entered in first page. If I have created/updated a new contact when user clicked Next on page 1 then I would like to pre populate those values when user wants to go back to previous page.

enter image description here

enter image description here

On clicking previous , I am exploring an approach to pre populate the values that was selected before/ that the apex controller inserted/updated before.

enter image description here

1

You could pass the prior form data as an object as an API param and then use that to populate the value field of you next form.

@api interview; // the param passed from the prior screen to this component

@track interviewObject; // what we will be updating before passing to the next component

connectedCallback(){
  this.interviewObject = interview; // doing this so that the object is editable by this component
}

HTML:

<template>
 ....
  <lightning-input-field field-name="some_field__c" value={interviewObject.fieldName} ></lightning-input-field>

</template>

If doing through a lightning-input you can still set the values to a generic JavaScript object and pass to the Apex through an imperative call. First for the apex to identify it as an SObject you only need to give an attribute and it can be inserted properly.

const record = {
    attributes : {
        type : "Contact"
    },
    ...this.interviewObject
};

When you pass it to the back end in your imperative it will need to be serialized to JSON then deserialized as an SObject and it can be inserted accordingly without issue

JS:

async mySaveMethod(evt){ 


    const record = {
        attributes : {
            type : "Contact"
        },
        // If doing this from a record -edit-form use evt.detail.fields
        ...this.interviewObject
    };

    const saveResult = await myImperativeMethod( { record: JSON.stringify( record )} )
    .catch( error => //... Do something with the error here );

    if(saveResult){
        // Do some stuff with the save result 
    }
}

Apex:

@AuraEnabled
public static SObject myImperativeMethod( String record ){
    SObject data = JSON.deserialize( record, SObject.class );
    insert data;

    return data;
}

You can do this for any form the same way and will be fine. I prefer to use the lightning-input-field since it handles the work of field values and rendering things like lookups and picklist, dependent picklist, etc without me having to manage those custom since it really isn't necessary. Also the lightning-record-edit-form will allow you to maintain control of the submit events, error response messaging and shifting between your views on success easier and without more custom components than necessary.

  • Actually I could not use lightning-input-field because data is coming from two different object - Contact and Interview. Is there a way I could work with the current code? – SfdcBat Apr 16 '20 at 23:45
  • Well since these two are in separate forms, you could still use that but capture the fields on the submit, stop the submit event, and store it through an imperative method in LWC after you've gathered the data that you want. The input field would just help to reduce the amount of custom code needed to render what you want. – Ronnie Apr 17 '20 at 4:16
  • With fields being marked as required, it would ensure as well the required inputs are present before allowing a submit event as well. I've done multiple objects in a single view from a single submit many times before, you just need to be in control of the submission process. – Ronnie Apr 17 '20 at 4:17
  • 1
    Thank you for the details. I will try this approach and get back to you as soon as I can. Could we pls keep this conversation open? – SfdcBat Apr 17 '20 at 15:11
  • 1
    yes - if more is needed, start a chat so this doesn't get too long – Ronnie Apr 17 '20 at 16:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.