function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
George WilliamsGeorge Williams 

Connect to Salesforce with Server-Side Controllers - No doInit in JavaScript Controller Error

I am running into an issue getting the challenge to pass.  Everything is working correctly but for whatever reason Trailhead cannot see the doInit function in my JS controller.  Here is all of my code:

CampingList.cmp:
<aura:component controller="CampingListController">
	<aura:attribute type="Camping_Item__c[]" name="items"/>
    <aura:attribute type="Camping_Item__c" name="newItem"
                    default="{ 'sobjectType': 'Camping_Item__c',
                    		   'Quantity__c': 0,
                    		   'Price__c': '0'}"/>
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    
    <div aria-labelledby="campItemForm">
        
        <!-- BOXED AREA -->
        <fieldset class="slds-box slds-theme--default slds-container--small">
        	<legend id="campItemForm" class="slds-text-heading--small slds-p-vertical--medium">
              Add Expense
            </legend>
        
            <!-- CREATE NEW EXPENSE FORM -->
            <form class="slds-form--stacked">        
              <div class="slds-form-element slds-is-required">
                  <div class="slds-form-element__control">
                      <ui:inputText aura:id="cmpLstItmName" label="Name"
                          class="slds-input"
                          labelClass="slds-form-element__label"
                          value="{!v.newItem.Name}"
                          required="true"/>
                  </div>
             </div>
        
             <div class="slds-form-element slds-is-required">
                  <div class="slds-form-element__control">
                      <ui:inputNumber aura:id="cmpLstItmQty" label="Quantity"
                          class="slds-input"
                          labelClass="slds-form-element__label"
                          value="{!v.newItem.Quantity__c}"
                          required="true"/>
        
                  </div>
              </div>
        
              <div class="slds-form-element slds-is-required">
                  <div class="slds-form-element__control">
                      <ui:inputCurrency aura:id="cmpLstItmPrice" label="Price"
                          class="slds-input"
                          labelClass="slds-form-element__label"
                          value="{!v.newItem.Price__c}"
                          placeholder="0.00"
                          required="true"/>
                  </div>
              </div>
        
              <div class="slds-form-element">
                  <ui:inputCheckbox aura:id="cmpLstItmPacked" label="Packed?"
                      class="slds-checkbox"
                      labelClass="slds-form-element__label"
                      value="{!v.newItem.Packed__c}"/>
              </div>
        
              <div class="slds-form-element">
                  <ui:button label="Create Camping List Item"
                      class="slds-button slds-button--brand"
                      press="{!c.clickCreateCmpLstItem}"/>
              </div>
        
            </form>
            <!-- / CREATE NEW EXPENSE FORM -->
        
          </fieldset>
          <!-- / BOXED AREA -->
        
        </div>
        <!-- / CREATE NEW EXPENSE --> 
    
        <div class="slds-card slds-p-top--medium">
            <header class="slds-card__header">
                <h3 class="slds-text-heading--small">Camping Items</h3>
            </header>
            
            <section class="slds-card__body">
                <div id="list" class="row">
                    <aura:iteration items="{!v.items}" var="item">
                        <c:CampingListItem item="{!item}"/>
                    </aura:iteration>
                </div>
            </section>
        </div>
</aura:component>

CampingList JS Controller:
({
    doInit : function(component, event, helper){
        var action = component.get("c.getItems");
        
        //console.log('loading data');
        
        action.setCallback(this,function(response){
            var respState = response.getState();
            
            if (component.isValid() && respState == "SUCCESS"){
                //console.log(response.getReturnValue());
           		helper.controllerHelper(component,response.getReturnValue());
            } else if (respState == "ERROR"){
            }
        });
        
        $A.enqueueAction(loadData);
    },
    
	clickCreateCmpLstItem : function(component, event, helper) {
        if (helper.validateForm(component)){
            helper.createItem(component);
        }       
	}   
})

CampingList JS Helper:
({
    controllerHelper : function(component,cmpItmList){
        component.set('v.items',cmpItmList);
   	},
    
    createItem : function(component){
        var action = component.get("c.saveItem");
        var items = component.get("v.items");
        var allItems = items;
        
        allItems.push(component.get("v.newItem"));
        
        var campItems = JSON.stringify(allItems);
        action.setParams({"cmpItms": campItems});
        
        action.setCallback(this,function(response){
            var respState = response.getState();
            
            if (component.isValid() && respState == "SUCCESS"){
		        component.set("v.items",response.getReturnValue());
            } else if (respState == "ERROR"){
            }
        });
        
        $A.enqueueAction(action);
    },
    
    validateForm : function(component){
    	var isValid = true;
        
        var cmpItmName = component.find("cmpLstItmName");
        var itmName = cmpItmName.get('v.value');
        
        var cmpItmQty = component.find("cmpLstItmQty");
        var itmQty = cmpItmQty.get('v.value');
        
        var cmpItmPrc = component.find("cmpLstItmPrice");
        var itmPrc = cmpItmPrc.get('v.value');
        
        var cmpItmPked = component.find("cmpLstItmPacked");
        var itmPked = cmpItmPked.get('v.value');
        
        if ($A.util.isEmpty(itmName)){
            isValid = false;
            cmpItmName.set("v.errors", [{message:"Item name can't be blank."}]);
        } else{ cmpItmName.set("v.errors", null); }
        
        if ($A.util.isEmpty(cmpItmQty)){
            isValid = false;
            cmpItmQty.set("v.errors", [{message:"Item amount can't be blank."}]);
        } else{ cmpItmQty.set("v.errors", null); }
        
        if ($A.util.isEmpty(cmpItmPrc)){
            isValid = false;
            cmpItmPrc.set("v.errors", [{message:"Item price can't be blank."}]);
        } else{ cmpItmPrc.set("v.errors", null); }
        
        if (isValid){
            return true;
        } else{
            return false;
        }
	}
})

CampingListController:
public with sharing class CampingListController {
    public static Map<String,Schema.SObjectField> schemaMap;
    public static String[] campItmFlds;
    
    public CampingListController(){
    }
    
    public static void setupFLSCheck(){
        campItmFlds = new String[] {'Id','Name','Quantity__c','Price__c','Packed__c'};
        
        schemaMap = Schema.SObjectType.Camping_Item__c.fields.getMap();
    }
    
    @auraenabled
    public static List<Camping_Item__c> getItems(){
        setupFLSCheck();
        for (String fld : campItmFlds){
            if (!schemaMap.get(fld).getDescribe().isAccessible()){ // Check if the user has access to view field
                throwError('You do not have access to the '+fld+ ' field on the Camping_Item__c object.' ); //Send error to client
                return null; // Suppress editor logs
            }
    	}
        
        return [select ID,Name,Quantity__c,Price__c,Packed__c from Camping_Item__c];
    }

    @auraenabled
    public static List<Camping_Item__c> saveItem(Camping_Item__c item){
        return new List<Camping_Item__c>{item};        
    }
	    
    @auraenabled
    public static List<Camping_Item__c> saveItem(String cmpItms){
        setupFLSCheck();
        
        List<SObject> cmpItms2 = convertJSONToListOfSObject(cmpItms);
        List<Camping_Item__c> cmpitms3= new List<Camping_Item__c>();
        
        for (SObject so : cmpItms2){
            cmpitms3.add((Camping_Item__c)so);
        }
       
        for (Camping_Item__c cItem : cmpitms3){
        	System.debug('Camp Item: ' + cItem.Name);
            System.debug('Camp Id: ' + cItem.Id);
        }
       
        
        for (String fld : campItmFlds){
            if (!schemaMap.get(fld).getDescribe().isAccessible()){ // Check if the user has access to view field
                throwError('You do not have access to the '+fld+ ' field on the Camping_Item__c object.' );
                return null; // Suppress editor logs
            } else if (!schemaMap.get(fld).getDescribe().isUpdateable() && fld != 'Id'){
            	throwError('You cannot update the '+fld+' field on the Camping_Item__c object.');
                return null;
            }
    	}
        
        upsert cmpItms3;
        return cmpItms3;
    }
        
    public static void throwError(string errMsg){
        try{
            throw new AuraHandledException(errMsg);
        } catch(Exception e){
            system.debug(e.getMessage());
        }
    }
    
    private static List<SObject> convertJSONToListOfSObject(String json) {
        Object[] values = (Object[])System.JSON.deserializeUntyped(json);
        
        List<SObject> newSObjectsList = new List<SObject>();
        for (Object v : values) {
            Map<String, Object> m = (Map<String, Object>)v;

            Schema.SObjectType targetType = Schema.getGlobalDescribe().get('Camping_Item__c');

            SObject o = targetType.newSObject();

            Map<String, Schema.SObjectField> fields = targetType.getDescribe().fields.getMap();
            for (String fieldName : m.keySet()) {
                // Filter out any psuedo fields such as LastNameLocal
                Schema.SObjectField fi = fields.get(fieldName);
                if (fi != null) {
                    if (fi.getDescribe().isCreateable() && fi.getDescribe().isUpdateable() && fieldName != 'Id') {
                        if(fieldName == 'Price__c'){
                            String prcStr = String.valueOf(m.get(fieldName));
                        	o.put(fieldName,Decimal.valueOf(prcStr));    
                        } else{
                        	o.put(fieldName, m.get(fieldName));
                        }
                    }
                }
            }

            newSObjectsList.add(o);
        }

        return newSObjectsList;
    }
}

Full error from the challenge: 
Challenge Not yet complete... here's what's wrong: 
The campingList JavaScript controller doesn't have a 'doInit' function or doesn't call the Apex controller to get items from the database.
George WilliamsGeorge Williams
Salesforce nees to implement post editing.  I have fixed the one issue with the 'loadData' in the JS controller.  Still failing the challenege with the same error.
George WilliamsGeorge Williams
Also fixed the updating issue in the Apex controller (hint: need to handle the ID field without going though FLS check in the JSON handling function).
George WilliamsGeorge Williams
Figure out the problem.  Trailhead wanted me to write inefficient code.  You HAVE to have the items list set in the controller or else the check will fail.  Seen this in other challenges too, its sloppy coding/design and Salesforce should fix it so that TrailHead accepts answer which do the requirements not it the code is structured the way they think is best.
Dora Babu KOTTHRU 7Dora Babu KOTTHRU 7
George, 

GOod Morning , 

({
    doInit: function(component, event, helper) {
    // Create the action
    var action = component.get("c.getItems");
    // Add callback behavior for when response is received
    action.setCallback(this, function(response) {
        var state = response.getState();
        if (component.isValid() && state === "SUCCESS") {
            component.set("v.item", response.getReturnValue());
        }
        else {
            console.log("Failed with state: " + state);
        }
    });
    // Send action off to be executed
    $A.enqueueAction(action);
},
clickCreateCamping: function(component, event, helper) {    
        if(helper.validateCampingForm(component)){
            // Create the new expense
            var newCamping = component.get("v.newItem");
            helper.createItem(component, newCamping);
        }

    }
})

Not what is wrong.  giving following error 
Challenge Not yet complete... here's what's wrong: 
The campingList JavaScript controller doesn't have a 'doInit' function or doesn't call the Apex controller to get items from the database.