Salesforce Real Time Project Scenario: To-Do List in Lightning

Hello Everyone! I am starting the Salesforce Real Time Project Scenario Series in Lightning where we will implement end-to-end real time Salesforce project scenarios.

The main motivation behind this is – Lots of people, especially new to Salesforce technology ask me about the real-time project scenarios to practice or the place where they can learn about to get that experience of building something on their own from scratch. As a developer, I know why it is important. No matter how many concepts we learn, it’s of no use if we can’t use that in a real-time scenario or project. Building such scenarios gives a lot of confidence to work on the actual project or even to crack the interviews.

These posts will explain the scenario in detail and then the complete implementation from scratch. You can start building yourself and then check the implementation if you get stuck anywhere.

Salesforce Real Time Project Scenario: To-Do List in Lightning

In this post, we will implement Real Time Salesforce Project Scenario: To-Do List Management in Lightning.

Salesforce Real Time Project Scenario in Lightning:

Create a Lightning Aura Component that will display To-Do List in one section. The user should be able to click on the to-do task to mark it complete. The completed tasks will be displayed in another section. The user should be able to create a new task from the component.

Technical Requirement:

  • Create a Custom Object named Notes to store the Tasks.
  • Create two Custom Fields on Notes object – Title and Status.
  • Create an Aura Component to display a list of to-do and completed tasks.
  • Create an Apex Class and methods to fetch the to-do and completed tasks, create a new task, and mark tasks as completed.

This is how our implementation will look:

Salesforce Real Time Project Scenario: To-Do Task Management in Lightning
Salesforce Real Time Project Scenario: To-Do Task Management in Lightning

Note: I will use the Notes and Tasks words here and there. Please consider both as the same thing for this implementation.

Implementation

Let’s get into the implementation of our first Real Time Salesforce Project Scenario.

First, create Custom Object and Fields as mentioned in the requirement.

Apex Class

Once that is done, we need to create an Apex Class to fetch the to-do and completed tasks, create a new task, and mark tasks as completed. It should look something like below:

NotesController.cls

public class NotesController {
    
    @AuraEnabled
    public static list<Notes__c> fetchNotes(){
        return [SELECT Id, Title__c, Status__c FROM Notes__c LIMIT 10];
    }
    
    @AuraEnabled
    public static Boolean completeTask(Id recordId){
        list<Notes__c> lstNotes = [SELECT Status__c FROM Notes__c WHERE Id = :recordId];
        
        if(lstNotes != null && !lstNotes.isEmpty()){
            for(Notes__c note : lstNotes){
                note.Status__c = 'Completed';
            }
        }
        
        try{
            update lstNotes;
            return true;
        }catch(DMLException e){
            System.debug(e);
            return false;
        }
    }
    
    @AuraEnabled
    public static Notes__c createNoteRecord(String strTitle){
        Notes__c note = new Notes__c();
        note.Title__c = strTitle;
        note.Status__c = 'To Do';
        insert note;
        return note; 
    }
}

Where,

  • fetchNotes() method simply queries the Tasks.
  • completeTask(Id recordId) method marks the Task as completed based on the recordId parameter.
  • createNoteRecord(String strTitle) method creates a New Task.

Aura Component

Once we are done with Apex Controller, its time to develop our Aura Component.

We need following things in component:

  • Two attributes of a type list to store to-do tasks and completed tasks.
  • Two boolean variables to show the modal to create a new note and to show the spinner.
  • Init handler that will call Apex Class method fetchNotes().
  • <lightning:card> to display two sections (div tags), one to display to-do tasks and another to display completed tasks. Use <aura:iteration> in both the sections to loop through the list to display tasks.
  • Simple div tag to display the Title of Note. We will apply Sticky Notes type CSS to make it look good. Add a checkbox for each Note div that will be used to mark that task complete.
  • Create a modal that will be displayed after a button click, with <lightning:input> tag to get the task TItle from the user and a button to call controller method to create a new record.
  • Use <aura:if> and boolean attributes to hide/show the spinner and modal.

This is how our component looks like:

MyNotes.cmp

<aura:component implements="flexipage:availableForAllPageTypes" access="global" controller="NotesController" >
    
    <aura:attribute type="Boolean" name="openNewModal" default="false"/>
    <aura:attribute type="Boolean" name="showCard" default="false"/>
    <aura:attribute type="String" name="strTitle" default=""/>
    
    <aura:attribute type="List" name="lstToDoNotes" />
    <aura:attribute type="List" name="lstCompletedNotes" />
    
    
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <div style="height: 550px">
        <lightning:card title="Manage Notes">
            <aura:set attribute="actions">
                <lightning:button label="New" onclick="{!c.openCreateModal}"/>
            </aura:set>        
            <aura:if isTrue="{!v.showCard}">
                <div class="slds-p-around_small">
                    <div class="slds-text-heading_small">TO-DO TASKS</div><br/>
                    <div class="flex-container">
                        <aura:iteration items="{!v.lstToDoNotes}" var="note">
                            <div class="box">
                                {!note.Title__c} <br/>
                                <lightning:input type="checkbox" name="{!note.Id}" checked="false"
                                                 class="completeCheckbox" onchange="{!c.markComplete}"/>
                            </div>
                        </aura:iteration>
                    </div>
                    
                    <hr></hr>
                    
                    <div class="slds-text-heading_small">COMPLETED TASKS</div><br/>
                    <div class="flex-container">
                        <aura:iteration items="{!v.lstCompletedNotes}" var="note">
                            <div class="box completed">
                                {!note.Title__c}
                            </div>
                        </aura:iteration>
                    </div> 
                </div>
                <aura:set attribute="else">
                    <lightning:spinner alternativeText="Loading" size="large" />
                </aura:set>
            </aura:if>
            
            <aura:if isTrue="{!v.openNewModal}">
                <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">
                            <lightning:button class="slds-modal__close" label="" iconName="utility:close" onclick="{!c.closeModal}"/>
                            <h2 id="modal-heading-01" class="slds-modal__title slds-hyphenate">Create New Note</h2>
                        </header>
                        <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
                            <lightning:input name="title" label="Enter Task: " value="{!v.strTitle}"/>
                        </div>
                        <footer class="slds-modal__footer">
                            <lightning:button label="Cancel" title="Neutral action" onclick="{!c.closeModal}"/>
                            <lightning:button variant="brand" label="Save" title="Brand action" onclick="{!c.createNote}"/>
                        </footer>
                    </div>
                </section>
                <div class="slds-backdrop slds-backdrop_open"></div>
            </aura:if>
        </lightning:card>
    </div>
</aura:component>

JavaScript Controller

The MyNotes Controller will have all the methods that will be called from Aura Component, which will call the Helper methods.

MyNotesController.js

({
	doInit : function(component, event, helper) {
		helper.doInitHelper(component, event, helper);
	},
    
    openCreateModal : function(component, event, helper) {
		helper.openCreateModalHelper(component, event, helper);
	},
    
    closeModal : function(component, event, helper) {
		component.set("v.openNewModal", false);
	},
    
    createNote : function(component, event, helper) {
		helper.createNoteHelper(component, event, helper);
	},
    
    markComplete : function(component, event, helper) {
		helper.markCompleteHelper(component, event, helper);
	},
})

JavaScript Helper

JavaScript Helper will have the following methods:

  • doInitHelper: To get the Notes by calling an Apex method and setting lstToDoNotes and lstCompletedNotes attributes. This method will display the tasks in to do and completed section.
  • openCreateModalHelper: To open the modal to create a task.
  • createNoteHelper: To call the Apex method to create a Note record. This will also update the lstToDoNotes attribute once the record is created successfully.
  • markCompleteHelper: Calls Apex method to update Status of Note as completed. If the response is successful, removes the record fromlstToDoNotes attribute and add it to the lstCompletedNotes attribute.

MyNotesHelper.js

({
	doInitHelper : function(component, event, helper) {
		let action = component.get("c.fetchNotes");
        action.setCallback(this, function(response){
            
            let state = response.getState();
            if (state === "SUCCESS") {
                let lstNotes = response.getReturnValue();
                if(lstNotes){
                    let lstToDo = [];
                    let lstCompleted = [];
                    
                    for(let note of lstNotes){
                        if(note.Status__c == 'To Do'){
                            lstToDo.push(note);
                        }else if(note.Status__c == 'Completed'){
                            lstCompleted.push(note);
                        }
                    }
                    component.set("v.lstToDoNotes", lstToDo);
                    component.set("v.lstCompletedNotes", lstCompleted);
                    component.set("v.showCard", true);
                }
            }
        });
        $A.enqueueAction(action);
	},
    
    openCreateModalHelper : function(component, event, helper) {
		component.set("v.openNewModal", true);
	},
    
    createNoteHelper : function(component, event, helper) {
	let strTitle = component.get("v.strTitle");
        let action = component.get("c.createNoteRecord");
        action.setParams({
            "strTitle" : strTitle
        });
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                let note = response.getReturnValue();
                let lstToDos = component.get("v.lstToDoNotes");
                lstToDos.push(note);
                component.set("v.lstToDoNotes",lstToDos);
                component.set("v.openNewModal", false);
            }
        });
        $A.enqueueAction(action);
	},
    
    markCompleteHelper : function(component, event, helper) {
        component.set("v.showCard", false);
		let selectedCheckbox = event.getSource();
        let recordId = selectedCheckbox.get("v.name");
        let action = component.get("c.completeTask");
        action.setParams({
            "recordId" : recordId
        });
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                let boolSuccess = response.getReturnValue();
                if(boolSuccess){
                    let lstToDo = component.get("v.lstToDoNotes");
                    let lstCompleted = component.get("v.lstCompletedNotes");
                    for(let i in lstToDo){
                        if(lstToDo[i].Id == recordId){
                            lstCompleted.unshift(lstToDo[i]);
                            lstToDo.splice(i,1);
                        }
                    }
                    component.set("v.lstToDoNotes", lstToDo);
                	component.set("v.lstCompletedNotes", lstCompleted);
                    component.set("v.showCard", true);
                }
                
            }
        });
        $A.enqueueAction(action);
	},
})

Also Read:

CSS

And finally the CSS that makes our Notes looks good.

MyNotes.css

.THIS {
}

.THIS .box{
    border-radius: 10px;
    background-color: #faf59b;
    padding: 15px;
    width: 148px;
    height: 148px;
    display: inline-block;
    margin: 3px;
    font-size: 15px;
    text-align: center; 
    position: relative;
}

.THIS .completed{
    background-color: #aafac3;
}

.THIS .flex-container {
    display: flex;
    flex-flow: row wrap;
    width: 800px;
}

.THIS .slds-button__icon_left{
    margin-right: 0px;
   
}
.THIS .completeCheckbox{
    position: absolute;
    bottom: 5px;
    right: 0;
}

That is all from this post. Just add your component to any Lightning Page and check your implementation working. If you don’t want to miss new implementations, please Subscribe here. And let me know in the comments section if you would like to have more Salesforce Real Time Project Scenario implementations in Lightning.

Bonus with this Salesforce Real Time Project Scenario

If you have gone through the complete posts, below is the same implementation using Lightning Web Components. And it has a small bonus implementation. Rather than clicking on the Checkbox to mark the task Completed, we are dragging a to-do task into completed tasks to mark it completed. Do check it out.

See you in the next implementation!

2 thoughts on “Salesforce Real Time Project Scenario: To-Do List in Lightning”

Leave a Comment