0

I am new to Lightning. I am trying to build a lightning component which would display the weather report. I am able to get the values in my debug log from my apex class. However, the same values are not populating on my Lightning component. Please refer to the codes below:

Apex class:

public virtual class WeatherUtil {
private static final String API_KEY = 'a032cd22e82569598abe1dced7625435';

public class Weather {
    public String imageUrl { get; set; }        // image.url
    public String imageTitle { get; set; }      // image.title
    public String imageLink { get; set; }       // image.link

    public String displayLocation { get; set; } // display_location.full
    public String weather {get;set;}
    public String observationTime { get; set; } // observation_time
    public String currentWeather { get; set; }  // weather
    public String currentTempF { get; set; }    // temp_f
    public String currentTempC { get; set; }    // temp_c
    public String currentWind { get; set; }     // wind_string
    public String feelsLikeF { get; set; }      // feelslike_f
    public String feelsLikeC { get; set; }      // feelslike_c
    public String pressure {get;set;}
    public String isDay {get;set;}

    public String weatherIconUrl { get; set; }  // icon_url
}

// this method is to be used in Lightning Components (non record based view)
@AuraEnabled
public static String getLocalWeather(String state, String cityName) {
    WeatherUtil.Weather weather = new WeatherUtil.Weather();
    String result = makeCallout();
    System.debug('result 100000' +result);

    // return the serialized weather wrapper class, or null
    return result.equalsIgnoreCase('success!') ? JSON.serialize(weather) : null;
   //return result;

}
@AuraEnabled
// Method to perform callouts
public static String makeCallout(){
    String State = 'West Bengal';
    String city = 'Kolkata';
    WeatherUtil.Weather weather1 = new WeatherUtil.Weather();
    // define a response to caller
    String outcomeMsg;

    // define basic information for later, store these in a protected custom setting

    // String endpoint = 'http://api.weatherstack.com'; // be sure this is configured in "Remote Site Settings"
   // String resource = '/api/' + API_KEY + '/conditions/q/' + state + '/' + cityName.replace(' ', '_') + '.json';

   String endpoint = 'http://api.weatherstack.com/current?access_key =a032cd22e82569598abe1dced7625435&query=Kolkata';


    //String endpoint = 'http://api.weatherstack.com/forecast?access_key=a032cd22e82569598abe1dced7625435&query=Kolkata';
   // String endpoint = 'http://api.weatherstack.com/autocomplete?access_key=a032cd22e82569598abe1dced7625435&query=Kolkata';
    String method = 'GET';  


   // System.debug('resource '+resource);
   // System.debug('Endpoint & Resource:::' + endpoint + resource);

    // check to ensure a callout can be performed using the Limits class. 
    // This is 100 callouts per execution as of Spring 16 release
    if (Limits.getCallouts() >= Limits.getLimitCallouts()) {
        System.debug('Testing 200000');
        outcomeMsg = 'Maximum number of callouts has been reached.';

        // configure and perform the callout
    } else {
        System.debug('Testing 300000');
        // define transaction variables
        HttpRequest req = new HttpRequest();
        HttpResponse res = new HttpResponse();
        Http h = new Http();

        // Configure the request
        req.setEndpoint(endpoint);
        req.setMethod(method);
        req.setTimeout(120000);
        // Configure standard headers
        req.setHeader('Accept', '*/*');

        // This tells the API that we are sending and receiving the data as a JSON object 
        req.setHeader('Content-Type', 'application/json');
        System.debug('req :::::'+req);

        // Additional headers may be needed / Refer to the API documentation. 
        // Use a service like runscope.com to test everything ahead of time.

        // Set the body json with the description parameter, basically a string with a key value pair construction.
        // This will look very different for each integration resource.  
        // Some APIs don't use a body to take the request, 
        // they may simply take additional resources "/resource/order/Ord#" in the URI 
        // or parameters "resource/?orderId=133" in the URI

        // this integration doesn't require this, however yours might so I've left this here as an example
        //req.setBody('{"text" : "' + description + '"}');

        // Attempt the callout - create return error on exception
        try {

            // Perform callout and set response
            res = h.send(req);
            System.debug('res '+res);
            //System.debug('res.getBody() '+res.getBody());
            System.debug('resBody '+res.getBody());
            System.debug('resStatus '+res.getStatus());
            JSONParser parser = JSON.createParser(res.getBody());
            System.debug('parser :::'+parser);

            //System.debug('resHttp '+res.HttpResponse());
            // check response 
            if ((res.getStatusCode() == 200 || res.getStatusCode() == 201) && res.getBody() != null) {
                System.debug('Testing 500000');
                // Deserialize the response untyped
                Map<String, Object> untypedMap = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
                System.debug('untypedMap '+untypedMap);
                System.debug('untypesMapcontains :::'+untypedMap.containsKey('current_observation'));
                System.debug('untypesMapcontains1 :::'+untypedMap.containsKey('response'));
                System.debug('untypesMapcontains2 :::'+untypedMap.containsKey('results'));
                System.debug('untypesMapcontains3 :::'+untypedMap.containsKey('current_location'));
                System.debug('untypesMapcontains5 :::'+untypedMap.containsKey('request'));
                System.debug('untypesMapcontains3 :::'+untypedMap.containsKey('current'));
                System.debug(res.getBody());
                // Check success of deserialization
                if (untypedMap.containsKey('current')) {
                    System.debug('Entered in if');
                    // The deserialized response contains the expected key!
                    outcomeMsg = 'Success!';

                    Map<String,Object> currentWeather = (Map<String,Object>)untypedMap.get('current');
                    System.debug('currentWeather :::::'+currentWeather);

                   if (currentWeather.containsKey('image')) {
                        Map<String,Object> image = (Map<String,Object>)currentWeather.get('image');

                        weather1.imageUrl = (String)image.get('url');                                                // image.url
                        weather1.imageTitle = (String)image.get('title');                                            // image.title
                        weather1.imageLink = (String)image.get('link');                                              // image.link
                    }

                    if (currentWeather.containsKey('display_location')) {
                        Map<String,Object> displayLocation = (Map<String,Object>)currentWeather.get('display_location');

                        weather1.displayLocation = (String)displayLocation.get('full');                              // display_location.full
                    }

                    weather1.observationTime = (String)currentWeather.get('observation_time');                       // observation_time
                    weather1.currentWeather = (String)currentWeather.get('weather');    
                    System.debug('weather.currentWeather :::'+weather1.currentWeather);
                    weather1.currentWind = (String)currentWeather.get('wind_string');                                // wind_string

                    // round the temperatures before storing them
                    //Decimal tempF = (Decimal)currentWeather.get('temp_f');
                  // weather1.currentTempF = String.valueOf(tempF.round());                                           // temp_f

                    Decimal tempC = (Decimal)currentWeather.get('temperature');
                    System.debug('tempC :::'+tempC);
                    weather1.currentTempC = String.valueOf(tempC.round());     
                   System.debug('weather1.currentTempC :::'+weather1.currentTempC);
                   //System.debug('Decimal.valueOf((String)currentWeather.ge;::::' +Decimal.valueOf((String)currentWeather.get('feelslike')));
                    Decimal feelsLikeF = (Decimal)currentWeather.get('feelslike');
                   if(feelsLikeF != null){
                    weather1.feelsLikeF = String.valueOf(feelsLikeF.round());                                        // feelslike_f
                   }
                    Decimal pressure = (Decimal)currentWeather.get('pressure');
                    weather1.pressure = String.valueOf(pressure.round());
                    System.debug('weather1.pressure :::'+weather1.pressure);

                    String isDay = (String)currentWeather.get('is_day');
                    weather1.isDay = String.valueOf(isDay);
                    System.debug('weather1.isDay :::'+weather1.isDay);


                   weather1.weatherIconUrl = (String)currentWeather.get('icon_url');                                // icon_url


                } else if (untypedMap.containsKey('response') && ((Map<String,Object>)(untypedMap.get('response'))).containsKey('results')) {
                    System.debug('Entered in elseif');
                    // do something with search results
                } else {
                     System.debug('Entered in else');
                    outcomeMsg = 'Error: Verify key not found in response';
                }
            } else {
                // callout failed
                outcomeMsg = 'Error: Callout failed. Please review the debug log for additional details.';
            }

        } catch (DMLexception e) {
            // Unexpected exceptions will be caught here, like a deserialization error.
            outcomeMsg = 'Error: An exception has been encountered while calling out to Integration:  ' + e.getMessage();
        }
    }

    // Return the response
    return outcomeMsg;
}

}

Helper class:

({
getLocalWeather: function(component, recID) {
    var spinner = component.find('spinner');
    $A.util.removeClass(spinner, "slds-hide");

    if (recID) {
        var action = component.get("c.getLocalWeather");
        action.setParams({
            "recordId": recID
        });
    }
    action.setCallback(this, function(response) {
        this.doLayout(response, component);
    });
    $A.enqueueAction(action);
},
doLayout: function(response, component) {
    var spinner = component.find('spinner');
    var data = JSON.parse(response.getReturnValue());
    var warning = component.find('warning');

    if (data) {
        $A.util.addClass(warning, 'slds-hide');
        // for testing diff temps
        //data['feelsLikeF'] = '900';
        component.set("v.weather", data);
        console.log("weather data: ", data);
    } else {
        component.set("v.errorMessage", 'No weather received');            
        $A.util.removeClass(warning, 'slds-hide');
    }

    $A.util.addClass(spinner, "slds-hide");
}
})

JS CONTROLLER:

({
doInit: function(component, event, helper) {
    var main = component.find('main');
    var recID = component.get("v.recordId");

    if (recID) {
        helper.getLocalWeather(component, recID);
    }
},
showDetails: function (component, event, helper) {
    var weatherDetails = component.find('weatherDetails');
    if (weatherDetails) {
        $A.util.toggleClass(weatherDetails, 'slds-hide');
    }
}
})

LIGHTNING COMPONENT:

<aura:component controller="WeatherUtil" implements="forceCommunity:availableForAllPageTypes,flexipage:availableForAllPageTypes,force:hasRecordId" access="global">
<aura:attribute name="recordId" type="Id"/>
<aura:attribute name="errorMessage" type="String" />
<aura:attribute name="weather" type="Object"/>


<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

<div class="slds-box" aura:id="main">
    <div aura:id="panelList">
        <header>
            <h2 class="slds-text-heading--small slds-m-bottom--small">Local Weather</h2>
        </header>
    </div>
    <div style="position:relative;">
        <aura:if isTrue="{!v.weather}">
            <ul class="slds-list--vertical slds-has-dividers--top-space">
                <li class="slds-list__item" onclick="{!c.showDetails}">
                    <h3 class="slds-text-heading--small slds-m-bottom--x-small">{!v.weather1.displayLocation}</h3>
                    <p>
                        <img src="{!v.weather.weatherIconUrl}"/>
                        {!v.weather1.currentWeather}
                    </p>
                    <p>
                        {!v.weather1.currentTempC}&deg;C<aura:if isTrue="{!v.weather.currentTempF != v.weather.feelsLikeF}">, feels like {!v.weather.feelsLikeF}&deg;F</aura:if>
                    </p>
                    <div class="slds-hide" aura:id="weatherDetails">
                        <ul class="slds-list--vertical">
                            <li class="slds-list__item">{!v.weather1.currentWind}</li>
                            <li class="slds-list__item">{!v.weather1.observationTime}</li>
                        </ul>
                    </div>
                </li>
            </ul>
            <aura:set attribute="else">
                <aura:if isTrue="{!v.errorMessage}">
                    <p class="slds-hide" aura:id="warning">{!v.errorMessage}</p>
                    <aura:set attribute="else">
                        No results found...
                    </aura:set>
                </aura:if>
            </aura:set>
        </aura:if>
        <div class="slds-spinner_container slds-hide" aura:id="spinner">
            <div class="slds-spinner--brand slds-spinner slds-spinner--medium" aria-hidden="false" role="alert">
                <div class="slds-spinner__dot-a"></div>
                <div class="slds-spinner__dot-b"></div>
            </div>
        </div>
    </div>
</div>

2
  • 1
    welcome to SFSE. Please see How to Ask on getting the most from the Community. IN particular, you've dumped way too much code here. Reduce it down to the smallest needed to reproduce the issue.
    – cropredy
    Feb 7, 2020 at 6:01
  • Thanks for welcoming as well as your reply and suggestion @cropredy. I appreciate it. Actually, I was not getting a way around to show my apex controller values, so I thought of posting the complete code of lightning component, helper, js controller and my apex controller.
    – Kumar
    Feb 7, 2020 at 11:18

2 Answers 2

1

The official documentation for this is here. You can also read this blog posting on the same subject.

Use @AuraEnabled for variables that you want to return to the lightning component, so in your case:

public class Weather {
   @AuraEnabled
   public String imageUrl { get; set; }        // image.url
   @AuraEnabled
   public String imageTitle { get; set; }      // image.title
   @AuraEnabled
   public String imageLink { get; set; }       // image.link
   @AuraEnabled
   public String displayLocation { get; set; } // display_location.full
   @AuraEnabled
   public String weather {get;set;}
   @AuraEnabled
   public String observationTime { get; set; } // observation_time
   @AuraEnabled
   public String currentWeather { get; set; }  // weather
   @AuraEnabled
   public String currentTempF { get; set; }    // temp_f
   @AuraEnabled
   public String currentTempC { get; set; }    // temp_c
   @AuraEnabled
   public String currentWind { get; set; }     // wind_string
   @AuraEnabled
   public String feelsLikeF { get; set; }      // feelslike_f
   @AuraEnabled
   public String feelsLikeC { get; set; }      // feelslike_c
   @AuraEnabled
   public String pressure {get;set;}
   @AuraEnabled
   public String isDay {get;set;}
   @AuraEnabled
   public String weatherIconUrl { get; set; }  // icon_url
}
1
  • Sure..Let me change my code as per your suggestion.
    – Kumar
    Feb 7, 2020 at 11:20
0

In addition to the point raised by @Rijwan Mohammed in the other answer, this isn't the only issue in the code. Your Apex code won't work as you expect because your "makeCallout" method instantiates and populates its own WeatherUtil.Weather instance, so the values collected in makeCallout are lost. It also uses hard-coded state and city.

Try something like:

// this method is to be used in Lightning Components (non record based view)
@AuraEnabled
public static String getLocalWeather(String state, String cityName) {
    WeatherUtil.Weather weather = new WeatherUtil.Weather();
    String result = makeCallout(state, city, weather);
    System.debug('result 100000' +result);

    // return the serialized weather wrapper class, or null
    return result.equalsIgnoreCase('success!') ? JSON.serialize(weather) : null;
}

@AuraEnabled
// Method to perform callout and return status (but not details)
public static String makeCallout() {
    return makeCallout('West Bengal', 'Kolkata', new WeatherUtil.Weather());
}

// The actual callout execution
private static String makeCallout(String state, String city, WeatherUtil.Weather weather1) {
    // Remove the local "state", "city" and "weather1" variable declarations
    // and initialization/instantiation, but otherwise do what your
    // current makeCallout function does

    // define a response to caller
    String outcomeMsg;

    // define basic information for later, store these in a protected custom setting
    ...
}
5
  • Sure..Thank you. Let me change my code as per your suggestion and check for the outcome.
    – Kumar
    Feb 7, 2020 at 11:20
  • Thanks Rijwan, phil, cropredy. I made the changes as per your suggestions and it worked. I am now able to get the weather report on my lightning component.
    – Kumar
    Feb 10, 2020 at 9:19
  • Great to hear. Please upvote and mark best answer as appropriate.
    – Phil W
    Feb 10, 2020 at 10:26
  • Thank you all...it was helpful and i got to learn new things..
    – Kumar
    Feb 13, 2020 at 10:44
  • Hello Phil W, Rijwan & cropredy. With your help and suggestion, I was able to get the weather report by entering the city and clicking on Submit. However, I am stuck on the new requirement of the same task. My Lightning component should show when the date is changed and instead of entering the city manually, the city address should come from Opportunity object which is a parent object of Order object. My Lightning Component for displaying the weather forecast is on the Order object. I am not able to find a way or SOQL which works here. I tried some ways but it didn't went through.
    – Kumar
    Feb 14, 2020 at 13:19

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