model.save() not working (my face is all screwed up and 'm gritting my teeth >:( )

  • 2
  • Question
  • Updated 4 years ago
  • Answered
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,704 Points 20k badge 2x thumb

Posted 4 years ago

  • 2
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Pat can you post your whole snippet, it looks like a real doozie. I have a theory that you might be running into issues with asynchrosity and callbacks. Because a save takes a second or 2 to communicate with the server, you can run into issues when your snippet keeps on going and doesn't wait for your save to finish. You can pass a function into the save function to force it to run after the save finishes.

function myFunctionThatWillHappenAfterTheSave(){
    alert('Hello World');
}
//the function will not be run until the save finishes...
myModel.save(myFunctionThatWillHappenAfterTheSave());
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,704 Points 20k badge 2x thumb
Video uploading now. Here's the code.



var params = arguments[0],
    $ = skuid.$;

var jrModel = skuid.$M('JobReport'),
    jrRow = jrModel.getFirstRow(),
    
    // date objects for report start & end dates
    jrStartDate = skuid.time.parseSFDate(jrRow.RECON__Report_Start_Date__c),
    jrEndDate = skuid.time.parseSFDate(jrRow.RECON__Report_End_Date__c);
    
    // date strings for report start & end dates
    jrStartDateStr = jrStartDate.toISOString(),
    jrEndDateStr = jrEndDate.toISOString();
    
    // ref ids for weather logs
    jrAddress = jrRow.RECON__Job__r.RECON__AddressRef__c;
    flModel = skuid.$M('FirstJobLocation'),
    flRow = flModel.getFirstRow(),
    flId = flRow.Id;

    // Get WeatherLogs and set it's conditions to match job selections. job, start date and end date
var wlModel = skuid.$M('WeatherLogs'),
    wlModelJobCond = wlModel.getConditionByName('Job'),
    wlModelStartCond = wlModel.getConditionByName('StartDate'),
    wlModelEndCond = wlModel.getConditionByName('EndDate');
    
    // set conditions
    wlModel.setCondition(wlModelJobCond,jrRow.Id);
    wlModel.setCondition(wlModelStartCond,jrStartDate);
    wlModel.setCondition(wlModelEndCond,jrEndDate);
    
    // activate conditions
    wlModel.activateCondition(wlModelJobCond);
    wlModel.activateCondition(wlModelStartCond);
    wlModel.activateCondition(wlModelEndCond);
    
    // query model
    wlModel.updateData();
    
    // deactivate conditions
    wlModel.deactivateCondition(wlModelJobCond);
    wlModel.deactivateCondition(wlModelStartCond);
    wlModel.deactivateCondition(wlModelEndCond);

    var wldatearray = [];
    
    // Gather Dates from wlModel in array so they can be searched for in the following while loop
    for (var datakey in wlModel.data) {
        var dataobj = wlModel.data[datakey];
            for (var prop in dataobj){
                if(dataobj.hasOwnProperty(prop) && prop == 'RECON__Forecast_Date__c' ){      
                    wldatearray[wldatearray.length] = skuid.time.parseSFDate(dataobj[prop]).toISOString();
                    // console.log(wldatearray);
                }

            }
    }
    
function getWeatherReport(wrdate){
    
        // base url & url parameters from job. item url parameter to come from start/end date loop
        // forecast.io URL format:
        // https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE,TIME
    var url = 'https://api.forecast.io/forecast/',
        apiKey = '27b81979ec35eddcc6b95323b9546d78',
        latitude = jrRow.RECON__Job__r.RECON__AddressRef__r.RECON__Geocode__Latitude__s,
        longitude = jrRow.RECON__Job__r.RECON__AddressRef__r.RECON__Geocode__Longitude__s;
        
    
    //var forecastdate = wlRow.RECON__Forecast_Date__c.substring(11,0).concat('12:00:00');
    var forecastdate = wrdate.toISOString().substring(11,0).concat('12:00:00');
        
    
    var conditions=[
        {field: 'RECON__Forecast_Date__c', value: skuid.time.getSFDate(wrdate)}
        ];

    
    /* waiting for estimate ## to get proper date/time based on geocode of address. PV 2015-01-07 http://goo.gl/xQRJu
    sunriseTime = {RECON__Sunrise_Time__c: objJSON.daily.data[0].sunriseTime },
    sunsetTime = {RECON__Sunset_Time__c: objJSON.daily.data[0].sunsetTime },
    time = {RECON__Weather_Report_Time__c: objJSON.daily.data[0].time },
    precipIntensityMaxTime  = {RECON__Precip_Intensity_Max_Time__c: objJSON.daily.data[0].precipIntensityMaxTime },
    temperatureMinTime = { RECON__Temperature_Min_Time_Time__c: temperatureMinTimeCalc },
    temperatureMaxTime  = {RECON__Temperature_Max_Time__c: objJSON.daily.data[0].temperatureMaxTime },
    */
    
    function waitforsave(){
        console.lg('modelsaved');
    }

    wlModel.createRow({ additionalConditions: conditions});
    
    wlModel.save(waitforsave);
    
    getForecast();
    
    function getForecast(){

        /* waiting for estimate ## to get proper date/time based on geocode of address. PV 2015-01-07 http://goo.gl/xQRJu
        jsdate = skuid.time.parseSFDateTime(jrRow.RECON__Forecast_Date__c);
        jsdatestring = jsdate.toISOString().replace('.000Z','-0000');
        */
    
        forecastUrl = url + apiKey + '/' + latitude + ',' + longitude  + ',' +  forecastdate;
        console.log('URL: ' + forecastUrl);
        var sessionId = skuid.utils.userInfo.sessionId;
        sforce.connection.remoteFunction({
            url : forecastUrl, 
            requestHeaders: {
                "Authorization": "Bearer "+ sessionId, 
                "Content-Type": "application/json"
            }, 
            method: "GET", 
            onSuccess : function(response) { 
        
                var objJSON = JSON.parse(response);
                
                /* waiting for estimate ## to get proper date/time based on geocode of address. PV 2015-01-07 http://goo.gl/xQRJu
                var temperatureMinTimeISO = new Date(objJSON.daily.data[0].temperatureMinTime * 1000 - 32400000);
                var temperatureMinTimeCalc = skuid.time.getSFDateTime(temperatureMinTimeISO);
                */
                
                var jsforecastDate = new Date(objJSON.daily.data[0].time * 1000 - 32400000);
                var sfforecastDate = skuid.time.getSFDate(jsforecastDate);

                var fields = {
                    RECON__API_Response__c: response,
                    RECON__Daily_Summary__c: objJSON.daily.data[0].summary
                    /*
                    {field: 'RECON__Forecast_Date__c', value: sfforecastDate},
                    {field: 'RECON__Icon__c', value: objJSON.daily.data[0].icon},
                    {field: 'RECON__Moon_Phase__c', value: objJSON.daily.data[0].moonPhase},
                    {field: 'RECON__Precipitation_Intensity__c', value: objJSON.daily.data[0].precipIntensity},
                    {field: 'RECON__Precip_Intensity_Max__c', value: objJSON.daily.data[0].precipIntensityMax},
                    {field: 'RECON__Precipitation_Chance__c', value: objJSON.daily.data[0].precipProbability},
                    {field: 'RECON__Precipitation_Type__c', value: objJSON.daily.data[0].precipType},
                    {field: 'RECON__Precip_Accumulation__c', value: objJSON.daily.data[0].precipAccumulation},
                    {field: 'RECON__Daily_Temperature_Min__c', value: objJSON.daily.data[0].temperatureMin},
                    {field: 'RECON__Dewpoint__c', value: objJSON.daily.data[0].dewPoint},
                    {field: 'RECON__Wind_Speed__c', value: objJSON.daily.data[0].windSpeed},
                    {field: 'RECON__Wind_Bearing__c', value: objJSON.daily.data[0].windBearing},
                    {field: 'RECON__Cloud_Cover__c', value: objJSON.daily.data[0].cloudCover},
                    {field: 'RECON__Humidity__c', value: objJSON.daily.data[0].humidity},
                    {field: 'RECON__Pressure__c', value: objJSON.daily.data[0].pressure},
                    {field: 'RECON__Visibility__c', value: objJSON.daily.data[0].visibility},
                    {field: 'RECON__Ozone__c', value: objJSON.daily.data[0].ozone},
                    {field: 'RECON__Daily_Temperature_Max__c', value: objJSON.daily.data[0].temperatureMax}
                    */
               
                
                };
                 wlModel.updateRow(wlModel.getFirstRow(),fields);


            }, 
            
            onFailure : function(response) { 
                var pageTitle = $('#locationsPageTitle');
                var editor = pageTitle.data('object').editor;
                editor.handleMessages(
                        [
                            {
                                message: response,
                                severity: 'ERROR'
                            }
                        ]
                    );
                }
        });
    }
}
    
    //Loop through dates of the report
    while (jrStartDate <= jrEndDate){
       // console.log(jrStartDate);
       
       if (wldatearray.indexOf(jrStartDateStr) == -1){
            getWeatherReport(jrStartDate);
       }
       
       var newDate = jrStartDate.setDate(jrStartDate.getDate() + 1);
       jrStartDate = new Date(newDate);
       jrStartDateStr = jrStartDate.toISOString();
    }
 
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,704 Points 20k badge 2x thumb
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,704 Points 20k badge 2x thumb
A little cleaned up in the order of things. Vars, then functions, then inline code.

var params = arguments[0],    $ = skuid.$;

var jrModel = skuid.$M('JobReport'),
    jrRow = jrModel.getFirstRow(),
    
    // date objects for report start & end dates
    jrStartDate = skuid.time.parseSFDate(jrRow.RECON__Report_Start_Date__c),
    jrEndDate = skuid.time.parseSFDate(jrRow.RECON__Report_End_Date__c),
    
    // date strings for report start & end dates
    jrStartDateStr = jrStartDate.toISOString(),
    jrEndDateStr = jrEndDate.toISOString(),
    
    // ref ids for weather logs
    jrAddress = jrRow.RECON__Job__r.RECON__AddressRef__c;
    flModel = skuid.$M('FirstJobLocation'),
    flRow = flModel.getFirstRow(),
    flId = flRow.Id,

    // Get WeatherLogs and set it's conditions to match job selections. job, start date and end date
    wlModel = skuid.$M('WeatherLogs'),
    wlModelJobCond = wlModel.getConditionByName('Job'),
    wlModelStartCond = wlModel.getConditionByName('StartDate'),
    wlModelEndCond = wlModel.getConditionByName('EndDate');
    
function createWeatherReportRecord(wrdate){
    
    var conditions=[
        {field: 'RECON__Forecast_Date__c', value: skuid.time.getSFDate(wrdate)}
        ];

    /* waiting for estimate ## to get proper date/time based on geocode of address. PV 2015-01-07 http://goo.gl/xQRJu
    sunriseTime = {RECON__Sunrise_Time__c: objJSON.daily.data[0].sunriseTime },
    sunsetTime = {RECON__Sunset_Time__c: objJSON.daily.data[0].sunsetTime },
    time = {RECON__Weather_Report_Time__c: objJSON.daily.data[0].time },
    precipIntensityMaxTime  = {RECON__Precip_Intensity_Max_Time__c: objJSON.daily.data[0].precipIntensityMaxTime },
    temperatureMinTime = { RECON__Temperature_Min_Time_Time__c: temperatureMinTimeCalc },
    temperatureMaxTime  = {RECON__Temperature_Max_Time__c: objJSON.daily.data[0].temperatureMaxTime },
    */
    
    wlModel.createRow();//({ additionalConditions: conditions});
}

function getWeatherReportRecord(urlDate){

    /* waiting for estimate ## to get proper date/time based on geocode of address. PV 2015-01-07 http://goo.gl/xQRJu
    jsdate = skuid.time.parseSFDateTime(jrRow.RECON__Forecast_Date__c);
    jsdatestring = jsdate.toISOString().replace('.000Z','-0000');
    */

    // base url & url parameters from job. item url parameter to come from start/end date loop
    // forecast.io URL format:
    // https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE,TIME
    var url = 'https://api.forecast.io/forecast/',
        apiKey = '27b81979ec35eddcc6b95323b9546d78',
        latitude = jrRow.RECON__Job__r.RECON__AddressRef__r.RECON__Geocode__Latitude__s,
        longitude = jrRow.RECON__Job__r.RECON__AddressRef__r.RECON__Geocode__Longitude__s;
    
    forecastUrl = url + apiKey + '/' + latitude + ',' + longitude  + ',' +  urlDate;
    console.log('URL: ' + forecastUrl);
    var sessionId = skuid.utils.userInfo.sessionId;
    sforce.connection.remoteFunction({
        url : forecastUrl, 
        requestHeaders: {
            "Authorization": "Bearer "+ sessionId, 
            "Content-Type": "application/json"
        }, 
        method: "GET", 
        onSuccess : function(response) { 
    
            var objJSON = JSON.parse(response);
            
            /* waiting for estimate ## to get proper date/time based on geocode of address. PV 2015-01-07 http://goo.gl/xQRJu
            var temperatureMinTimeISO = new Date(objJSON.daily.data[0].temperatureMinTime * 1000 - 32400000);
            var temperatureMinTimeCalc = skuid.time.getSFDateTime(temperatureMinTimeISO);
            */
            
            var jsforecastDate = new Date(objJSON.daily.data[0].time * 1000 - 32400000);
            
            var fields = {
                RECON__API_Response__c: response,
                RECON__Daily_Summary__c: objJSON.daily.data[0].summary
                /*
                {field: 'RECON__Icon__c', value: objJSON.daily.data[0].icon},
                {field: 'RECON__Moon_Phase__c', value: objJSON.daily.data[0].moonPhase},
                {field: 'RECON__Precipitation_Intensity__c', value: objJSON.daily.data[0].precipIntensity},
                {field: 'RECON__Precip_Intensity_Max__c', value: objJSON.daily.data[0].precipIntensityMax},
                {field: 'RECON__Precipitation_Chance__c', value: objJSON.daily.data[0].precipProbability},
                {field: 'RECON__Precipitation_Type__c', value: objJSON.daily.data[0].precipType},
                {field: 'RECON__Precip_Accumulation__c', value: objJSON.daily.data[0].precipAccumulation},
                {field: 'RECON__Daily_Temperature_Min__c', value: objJSON.daily.data[0].temperatureMin},
                {field: 'RECON__Dewpoint__c', value: objJSON.daily.data[0].dewPoint},
                {field: 'RECON__Wind_Speed__c', value: objJSON.daily.data[0].windSpeed},
                {field: 'RECON__Wind_Bearing__c', value: objJSON.daily.data[0].windBearing},
                {field: 'RECON__Cloud_Cover__c', value: objJSON.daily.data[0].cloudCover},
                {field: 'RECON__Humidity__c', value: objJSON.daily.data[0].humidity},
                {field: 'RECON__Pressure__c', value: objJSON.daily.data[0].pressure},
                {field: 'RECON__Visibility__c', value: objJSON.daily.data[0].visibility},
                {field: 'RECON__Ozone__c', value: objJSON.daily.data[0].ozone},
                {field: 'RECON__Daily_Temperature_Max__c', value: objJSON.daily.data[0].temperatureMax}
                */
            };
            
            wlModel.updateRow(wlModel.getFirstRow(),fields);
        }, 
        
        onFailure : function(response) { 
            var pageTitle = $('#locationsPageTitle');
            var editor = pageTitle.data('object').editor;
            editor.handleMessages(
                    [
                        {
                            message: response,
                            severity: 'ERROR'
                        }
                    ]
                );
            }
    });
}


    
function waitforsave(){
    console.log('modelsaved');
}

// set conditions
wlModel.setCondition(wlModelJobCond,jrRow.Id);
wlModel.setCondition(wlModelStartCond,jrStartDate);
wlModel.setCondition(wlModelEndCond,jrEndDate);

// activate conditions
wlModel.activateCondition(wlModelJobCond);
wlModel.activateCondition(wlModelStartCond);
wlModel.activateCondition(wlModelEndCond);

// query model
wlModel.updateData();

// deactivate conditions
wlModel.deactivateCondition(wlModelJobCond);
wlModel.deactivateCondition(wlModelStartCond);
wlModel.deactivateCondition(wlModelEndCond);

var wldatearray = [];

// Gather Dates from wlModel in array so they can be searched for in the following while loop
for (var datakey in wlModel.data) {
    var dataobj = wlModel.data[datakey];
    for (var prop in dataobj){
        if(dataobj.hasOwnProperty(prop) && prop == 'RECON__Forecast_Date__c' ){      
            wldatearray[wldatearray.length] = skuid.time.parseSFDate(dataobj[prop]).toISOString();
            // console.log(wldatearray);
        }
    }
}

var forecastdate;

//Loop through dates of the report
while (jrStartDate <= jrEndDate){
   // console.log(jrStartDate);
   
   if (wldatearray.indexOf(jrStartDateStr) == -1){

        createWeatherReportRecord(jrStartDate);

        wlModel.save(waitforsave());

        forecastdate = jrStartDate.toISOString().substring(11,0).concat('12:00:00');
        
        getWeatherReportRecord(forecastdate);
        
   }
   
   var newDate = jrStartDate.setDate(jrStartDate.getDate() + 1);
   jrStartDate = new Date(newDate);
   jrStartDateStr = jrStartDate.toISOString();
}
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,704 Points 20k badge 2x thumb
Asynchronous and callbacks. I learned those the hard way today. Boiled my snippet down to absolute barebones and kept getting odd behaviour. Thought I was using the callback option properly. Didn't realize that I needed to type out "callback: function(result)..."

Barebones snippet works now. Going to bed.
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,004 Points 20k badge 2x thumb
Glad you figured it out Pat, bummer it was the hard way...

Moshe's correct, calls to model.save() or model.updateData() are asynchronous, which means that you can't have code like the following:

1 model.updateData();
2 model.deactivateCondition(condition)....

Because Line 2 will not wait for Line 1 to finish before it runs, so you'll get unexpected behavior.

Unfortunately Skuid's API's with callbacks are slightly different for updateData() and save(), but I recommend using the Promise based approach instead, which is consistent for both:

// UPDATE DATA
$.when(model.updateData())
   .done(function(result){
       var condition = model.getConditionByName('MyCondition');
        model.deactivateCondition(condition);
   })
   .fail(function(result){
        console.log('there was a problem');
   });

// SAVE
$.when(model.save())
   .done(function(result){
       if (result.totalsuccess) {
           console.log('heck yeah');
       } else {
          console.log('something went wrong...');
          console.log(result);
       }
   })
   .fail(function(result){
        console.log('there was a really bad problem, like connecting to the server');
   });
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,704 Points 20k badge 2x thumb
I'd otherwise use this suggestion if I didn't have to use sforce.connection.remoteFunction.

http://stackoverflow.com/questions/18132790/forecast-io-api-usage-with-jquery
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
This should work, just dump all of your code inside the save:

model.save({callback:function(){	
sforce.connection.remoteFunction({
        url : forecastUrl, 
        requestHeaders: {
            "Authorization": "Bearer "+ sessionId, 
            "Content-Type": "application/json"
        }, 
        method: "GET", 
        onSuccess : function(response) { 
    
            var objJSON = JSON.parse(response);
//etc....
}
});
}});
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,704 Points 20k badge 2x thumb
Wait. The callback function is run before or after the save method?
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
After
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,704 Points 20k badge 2x thumb
Great, perfect & awesome!
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Pat I would say that you're higher than intermediate already! But anyway I kind of forgot about the difference between :

model.save(callback:myCallbackFunction());

and

model.updateData(myCallbackFunction);

I would recommend this post as a simple explanation of what exactly a callback is/does:

http://www.impressivewebs.com/callback-functions-javascript/

Did you get your snippet working?
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,704 Points 20k badge 2x thumb
Thanks for the vote of confidence. Really pushing hard to get this down to the same thing as typing an email. A few months to go before that happens I'd say.

Certainly going to read that article.

I got the barebones version of it working now. Should be pretty quick to get it going.
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,704 Points 20k badge 2x thumb
TADA!!!!
The code changed so so much. Didn't really get asynchronous and callbacks til now.