0

I have a custom project object, which is associated with Task object. Enhanced Notes are enabled in Project and Task objects. There are scenarios that Project may or may not have tasks, and notes can be at Project or Task level. I am trying to build a report card that hope to report all the projects with their respective tasks (if any) and respective Notes (if any) and the notes under the tasks (if any). So i have come up with two SOQL queries:

-- this returns all the projects and their associated tasks

 myLstProject = [Select id, sfdc_project_name__c,SFDC_Status_Description__c, priority__c,priority_Color__c, (select id, owner.name,subject from tasks) from sfdc_project__c];

--- this returns the notes with linked entity ids (could be project ids or task ids), the lstLinkedEntityID is the list built from the #1 query's resultset ( project ids and the task ids ).

myContentDocLink = [select contentdocumentlink.id, contentdocument.title, contentdocument.description ,linkedentity.name, linkedentity.id 
            from contentdocumentlink where linkedentityid in: lstLinkedEntityID
            and sharetype='V' and contentdocument.filetype='SNOTE'];

I can iterator and display the first query data nicely, but i am not sure how to embed the second query result to the first one and show the notes respectively to the project/task. Is there a way in lightning component that nested iteration takes dynamic key for its parent iteration and auto filter out the data?

I was thinking about using Map of List, but I don't have any luck. Also it doesn't seem i can combine those two queries nicely. If it is in Oracle/SQL Server, it can be easily done. Any suggestion is highly appreciated!

private static List<ID> myLstLinkedEntityID = new list<Id>();
private static List<sfdc_project__c> myLstProject = new List<sfdc_project__c> ();
private static  List<ContentDocumentLink> myContentDocLink= new List<ContentDocumentLink>();


private static  void  getNotesHelper (List<ID> lstLinkedEntityID)
{


    myContentDocLink = [select contentdocumentlink.id, contentdocument.title, contentdocument.description ,linkedentity.name, linkedentity.id 
        from contentdocumentlink where linkedentityid in: lstLinkedEntityID
        and sharetype='V' and contentdocument.filetype='SNOTE'];

    system.debug('in getNotesHelper size: ' + myContentDocLink.size());

   // return myContentDocLink;
}

@AuraEnabled
public static List<SFDC_Project__C> getProjectAndTasks()
{
    myLstProject = [Select id, sfdc_project_name__c,SFDC_Status_Description__c, priority__c,priority_Color__c, (select id, owner.name,subject from tasks) from sfdc_project__c];

    for (sfdc_project__c p: myLstProject)
    {
        myLstLinkedEntityID.add(p.id);

        for (Task t: p.tasks)
        {
            myLstLinkedEntityID.add(t.id);

        }
    }
    system.debug(myLstLinkedEntityID);

    getNotesHelper(myLstLinkedEntityID);


    return myLstProject;
}


@AuraEnabled
public static Map<ID, List<String>> fetchNotes()
{
    Map<ID, List<String>> objectNoteMap = new Map<ID, List<String>> ();

    system.debug('document link size is: ' + myContentDocLink.size());

    if (myContentDocLink !=null)
    {
        for (ContentDocumentLink cl: myContentDocLink )
        {
              List<String> content = new List<String> ();
                  String title = cl.ContentDocument.title;
                String description = cl.ContentDocument.Description;
                 content.add(title);
                content.add(description);



            if(!objectNoteMap.containsKey(cl.LinkedEntity.ID))
            {
                system.debug('Key: ' + cl.LinkedEntity.ID);
                objectNoteMap.put(cl.LinkedEntity.ID, content);
                                system.debug('New Key ' + content);
            }
            else
            {
                objectNoteMap.get(cl.LinkedEntity.ID).add(title);
                objectNoteMap.get(cl.LinkedEntity.ID).add(description);
                system.debug('Existing Key ' + title );
            }
        }

    }

    system.debug ('Map: ' + objectNoteMap);

    return objectNoteMap;

}

--- lightning component, where I need one more iteration to show the notes under project/task accordingly

<lightning:layout >
    <lightning:layoutitem>
         <aura:Iteration items="{!v.lstProject}" var="item">
           <lightning:card title="{!item.SFDC_Project_Name__c}" iconName="standard:scan_card"  >
             <c:DisplayNoteItem item="{!item}" /> 
            <aura:Iteration items="{!item.Tasks}" var="task" indexVar="index">
                 <li>Task subject: {!task.Subject} </li>
            </aura:Iteration>
                        </lightning:card>
        </aura:Iteration> 

    </lightning:layoutitem>
5
  • Can you add the code snippet about what you tried with Maps and Lists? – Pranay Jaiswal Jul 18 '19 at 19:06
  • Thank you Pranay. i put it as answer as it is too long as comment. please let me know what you think. – XL Zhang Jul 18 '19 at 19:13
  • I mean edit this question and put code here? – Pranay Jaiswal Jul 18 '19 at 19:16
  • Sorry, kind of new to the site.. i edited the question. – XL Zhang Jul 18 '19 at 19:18
  • so pretty much i have the project/task id in the objectNoteMap, is there a way in the first and second iteration to add if statement to test whether the key is in the objectNoteMap, if yes then just show those pair in objectNoteMap under that iteration? I am very new to lightning component. not sure if it is possible. – XL Zhang Jul 18 '19 at 19:29
1

When Salesforce Data Structure is not enough for your complex requirement/SOQL , you can create your own data structures.

Main Object is Project , Project can have multiple tasks and Notes . Tasks can have multiple Notes.

Let's create a couple of Data Structure in the form of Inner Classes to suit our requirement.

public class ProjectWrapper{

        @AuraEnabled
        public sfdc_project__c project;
        @AuraEnabled
        public List<TaskWrapper> tasks;
        @AuraEnabled
        public List<ContentDocumentLink> notes = new List<ContentDocumentLink>();

        ProjectWrapper(sfdc_project__c project , List<TaskWrapper> tasks){
            this.project = project;
            this.tasks = tasks;

        }


    }

    public class TaskWrapper{

        @AuraEnabled
        public Task task;
        @AuraEnabled
        public List<ContentDocumentLink> notes = new List<ContentDocumentLink>();

        TaskWrapper(Task task){
            this.task = task;
        }


    }

Now all you have to do is populate List in your Apex Method and in Aura just iterate over it.

 public static List<ProjectWrapper> getProjectWithTaskAndNotes(){


    List <sfdc_project__c> myLstProject = [SELECT id, sfdc_project_name__c,SFDC_Status_Description__c, priority__c,priority_Color__c,
                    (select id, owner.name,subject FROM tasks) 
                    FROM  sfdc_project__c];


     // Gather All LinkedEntityId For ContentDocumentLink Query 
    List<Id> linkedEnityIdList = new List<Id>();

    //Create Maps for Future Wrapper
    Map<Id , ProjectWrapper> idVsProjectWrapper = new Map<Id,ProjectWrapper>();
    Map<Id , TaskWrapper> idVsTaskWrapper = new Map<Id,TaskWrapper>();


    for(sfdc_project__c pj : myLstProject){
        linkedEnityIdList.add(pj.Id);

        List<TaskWrapper> taskWrapperList = new List<TaskWrapper>();
        for(Task tsk : pj.tasks){
            linkedEnityIdList.add(tsk.Id);
            TaskWrapper tw = new TaskWrapper(tsk);
            taskWrapperList.add(tw)
            idVsTaskWrapper.put(tsk.Id,tw);

        }
    }
    idVsProjectWrapper.put(pj.Id , new ProjectWrapper(pj , taskWrapperList)  );

    //Querry ContentDocumentLink
    List<ContentDocumentLink > contentDocumentLinkList = [SELECT contentdocumentlink.id, contentdocument.title,
                                                        contentdocument.description ,linkedentity.name, linkedentityId 
                                                        FROM contentdocumentlink 
                                                        WHERE linkedentityid in: lstLinkedEntityID
                                                        AND sharetype='V' 
                                                        AND contentdocument.filetype='SNOTE'];

    //Now Link The CDL to TaskWrappers and ProjectWrappers

    for(ContentDocumentLink cdl : contentDocumentLinkList){
        if(cdl.linkedentityid.getsobjecttype() == 'sfdc_project__c'){ 
            idVsProjectWrapper.get(cdl.linkedentityid).notes.add(cdl);
        }else{
            idVsTaskWrapper.get(cdl.linkedentityid).notes.add(add);
        }
    }

    return idVsProjectWrapper.values();
}

And in Aura, just iterate over your complex data-structure.

    <aura:Iteration items="{!v.lstProject}" var="item">
       <lightning:card title="{!item.project.SFDC_Project_Name__c}" iconName="standard:scan_card"  >

         <aura:iteration items="{!item.notes}" var="noteP" >
            <c:DisplayNoteItem item="{!noteP}" /> 
         </aura:iteration>


        <aura:Iteration items="{!item.tasks}" var="task" >
             <li>Task subject: {!task.task.Subject} </li>
             <aura:iteration items="{!task.notes}" var="notet" >
                <c:DisplayNoteItem item="{!notet}" /> 
             </aura:iteration>
        </aura:Iteration>
        </lightning:card>            
    </aura:Iteration> 
6
  • Thank you! let me digest your answer and let you know. – XL Zhang Jul 18 '19 at 20:10
  • Hi Pranay, this is great response! i am so impressed that you got my questions so accurately and provided the answer so thorough and logical. One more question though, when i define the aura attribute for ProjectWrapper i got error: <aura:attribute name="lstProject" type="ProjectWrapper[]" /> – XL Zhang Jul 19 '19 at 12:37
  • @XLZhang if it's Inner class then it should be OuterClass.ProjectWrapper[] – Pranay Jaiswal Jul 19 '19 at 12:40
  • The answer is perfect. I only made a minor change, one is the typo idVsTaskWrapper.put(ts.Id,tw); and the other is that i moved the block of code to populate the notes outside the loop of the project list. Thanks a lot! – XL Zhang Jul 19 '19 at 13:31
  • Pranay, if you don't mind can you shed some light on how i can enable to navigation of the objects (like allows the user to click on it and redirect the user to the record detail page). – XL Zhang Jul 19 '19 at 13:32

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.