0

I have Question__c(parent) and Option__c(child) Master detail relationship. On page load I am able to pass the data to lightning component without help of any warapper class.

Now on submitting the form from lightning component, I am not able to access the child records in apex controller.

As both are Sobjects in Salesforce, I am trying to access without help of wrapper class.

Here is my code Lightning component:

<aura:attribute name="Question" type="Question__c[]"/>
<aura:iteration var="que" items="{!v.Question}">
     <aura:iteration items="{!que.Options__r}" var="opt" indexVar="index">
                    <lightning:input type="radio" label="{!opt.Name}" name="{!opt.Name}" aura:id="{!opt.Name}" value="{!opt.Is_Selected_Option__c}" checked="{!opt.Is_Selected_Option__c}"/>    
    </aura:iteration>
</aura:iteration>

JS controller:

onSubmit: function(component, event, helper) {

    var action = component.get("c.validateAnswers");
    action.setParams({'questionsList':component.get("v.Question")});
    alert('=======1======='+JSON.stringify(component.get("v.Question"))); It prints both parent and child records
    // Add callback behavior for when response is received
    action.setCallback(this, function(response) {
        var state = response.getState();
        if (state === "SUCCESS") {

        }
        else {
            console.log("Failed with state: " + state);
        }
    });

    $A.enqueueAction(action);
}

Apex controller:

@AuraEnabled
public static Integer validateAnswers(List<Question__c> questionsList){
    system.debug('=======questionsList======'+questionsList);
    //It prints only Question__c records not the child records(Options)

}
8
  • instead of passing a Question array to your method, why not use a Map<String,Object> ? it might be that List<Question__c> is only taking the Question record and ignoring the child.
    – glls
    Commented Dec 27, 2018 at 15:29
  • Are you seeing the child records when you iterate the list? system.debug may not print the whole content of the list.
    – Jayant Das
    Commented Dec 27, 2018 at 16:13
  • No child records are not coming in apex controller.
    – Manu Tej
    Commented Dec 27, 2018 at 17:07
  • Do you get an error? What is the actual behavior you observe?
    – Adrian Larson
    Commented Dec 27, 2018 at 17:20
  • 1
    Upon replicating your issue, I realize that you may be out of luck here unless you manipulate the contents from JS to Apex. Seems the child records never make it to Apex during the process of being sent from JS. In this situation, it's best to go with a wrapper class.
    – Jayant Das
    Commented Dec 27, 2018 at 21:13

2 Answers 2

1

I think you can achieve it with a different approach which I am explaining here.

You just need to get the Option record Ids which has been selected and it can be done by iterating over the Question and it's related options in the JS controller. After extracting the Ids for the selected options, you can send them to the apex method and there you can perform your server-side logic.

let optionRecordIds = [];
let lstQuestions = component.get("v.Question");
for( let que of lstQuestions ){
   for( let opt of que.Options__r ){
      if( opt.Is_Selected_Option__c ) {
         optionRecordIds.add(opt.Id);
      }
   }
}

// Pass the option record ids to the server
var action = component.get("c.validateAnswers");
action.setParams({'optionIds': optionRecordIds});
action.setCallback(this, function(response) {
    var state = response.getState();
    if (state === "SUCCESS") {

    }
    else {
        console.log("Failed with state: " + state);
    }
});

$A.enqueueAction(action);

Server-side method

@AuraEnabled
public static Integer validateAnswers(List<Id> optionIds){
   system.debug('=======Option record ids======'+optionIds);
   //Here you can query the option records along with their parent question

}
2
  • I agree with your approach. I am trying to send both parent and child records at a time to apex controller. I know we can do this using wrapper class. I am just curious to know if this is possible with out using wrapper as both are related Sobjects(so that I can reduce few variables and some less code)
    – Manu Tej
    Commented Dec 28, 2018 at 6:38
  • If you have got the child records then you could query the parent records in your apex controller and there was no need of the wrapper. But looks like you also need the Is_Correct_Answer__c field value. It was not mentioned in the question so didn't consider this scenario. Commented Dec 28, 2018 at 8:51
0

Finally I have implemented the same using wrapper class. Below is my code. Any suggestions on code improvement are welcome.

Lightning Component

<aura:attribute name="wrapper" type="InterviewController.InterviewWrapper[]" access="public" />
<aura:attribute name="Question" type="Question__c[]"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<aura:iteration var="que" items="{!v.Question}">
     <aura:iteration items="{!que.Options__r}" var="opt" indexVar="index">
                    <lightning:input type="radio" label="{!opt.Name}" name="{!opt.Name}" aura:id="{!opt.Name}" value="{!opt.Is_Selected_Option__c}" checked="{!opt.Is_Selected_Option__c}"/>    
    </aura:iteration>
</aura:iteration>   
<lightning:button label="Submit" onclick="{!c.onSubmit}"/>

JS controller On Submit button

doInit: function(component, event, helper) {
    var action = component.get("c.getQuestion");
    action.setCallback(this, function(response) {
        var state = response.getState();
        if (state === "SUCCESS") {
            component.set("v.Question", response.getReturnValue());

    });
    $A.enqueueAction(action);
},    
onSubmit: function(component, event, helper) {
    var action = component.get("c.validateAnswers");
    var interviewWrapper = [];

    for(var i=0; i<component.get("v.Question").length; i++){
        var intWrap = {} ;
        intWrap.questionObj = component.get("v.Question")[i];
        var options = component.get("v.Question")[i].Options__r;
        intWrap.optionsList = [];
        for(var j=0; j<options.length; j++){
            intWrap.optionsList.push(component.get("v.Question")[i].Options__r[j]);
        }
        interviewWrapper.push(intWrap);
    }
    component.set("v.wrapper",interviewWrapper)
    action.setParams({'questionJson':JSON.stringify(component.get("v.wrapper"))});
    // Add callback behavior for when response is received
    action.setCallback(this, function(response) {
        var state = response.getState();
        if (state === "SUCCESS") {
        }
        else {
            console.log("Failed with state: " + state);
        }
    });
    // Send action off to be executed
    $A.enqueueAction(action);
}

Apex COntroller:

@AuraEnabled
public static List<Question__c> getQuestion() {
    List<Question__c> question=[SELECT Name, QuestionText__c,HelpText__c,(SELECT Name,Is_Selected_Option__c,Is_Correct_Answer__c FROM Options__r) FROM Question__c ORDER BY Name];
    return question;
}
@AuraEnabled
public static Integer validateAnswers(String questionJson){
    List<InterviewWrapper> questionsList = (List<InterviewWrapper>)JSON.deserialize(questionJson, List<InterviewWrapper>.class);
    Integer correctAnswer = 0;
    Integer totalQuestions = questionsList.size();
    for(InterviewWrapper que : questionsList){
        for(Option__c option : que.optionsList){
            if(option.Is_Correct_Answer__c && option.Is_Selected_Option__c ) {
                correctAnswer++;
            }               
        }
    }
    return correctAnswer;
}
//wrapper
public with sharing class InterviewWrapper{
   @AuraEnabled public Question__c questionObj;
   @AuraEnabled public List<Option__c> optionsList;
1
  • If you were using the Wrapper to set the "Question" attribute value then you could easily get rid of the loop where you are constructing the wrapper in onSubmit function. Commented Dec 28, 2018 at 8:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .