Mohammed AlBanna
Mohammed AlBanna

Reputation: 2398

How can I relaunch/update/refresh the card again using Apps Script

I'm having a nightmare doing a lot of scenarios using Apps Script, but nothing works! I have a function that makes a GET request returns an array of cards. Now, sometimes I need this card refreshes again to fetch the new content.

function listTemplatesCards(){
  var getAllTemplates = getTemplates();
  var allTemplates = getAllTemplates.templates;
  var theUserSlug = getAllTemplates.user_slug;
  var templateCards = [];

  //There are templates
  if(allTemplates.length > 0){
    allTemplates.forEach(function(template){
      templateCards.push(templateCard(template, theUserSlug).build());
    });
    return templateCards;
  }
}

This function is called on onTriggerFunction. Now, if I moved to another card and I wanted to back again to the root but in clean and clear way, I use this but it doesn't work:

  //Move the user to the root card again
  var refreshNav = CardService.newNavigation().popToRoot();
  return CardService.newActionResponseBuilder().setStateChanged(true).setNavigation(refreshNav).build();

Simply, what I want is once the user clicks on Refresh button, the card refreshes/updates itself to make the call again and get the new data.

Upvotes: 6

Views: 6572

Answers (6)

M. Adam Klemann
M. Adam Klemann

Reputation: 1

I tried the solutions above and was frustrated. Then I keyed on what @hhsb said that, "cards are not refreshed but updated with new cards." I just called my initial card function and it rebuilt the card. Sin falta, no problemo.

 "gmail": {
      "contextualTriggers": [
        {
          "unconditional": {},
          "onTriggerFunction": "createSettingCard"
        }
      ],
      "primaryColor": "#B00024",
      "secondaryColor": "#002757"
    }

And then my function that is called when a field changes:

function handleFieldUpdate(e){
  var userInfo = PropertiesService.getUserProperties();
  userInfo.setProperty('sigData-type', e.formInput.whichType);
//... do all the background stuff that needs to happen when a field is updated...
  
  //Call initial function to redraw the card.
  return createSettingCard();  //<-- call the initial card building
}

This worked for me. I'd be open to feedback on why this isn't how it should be done.

Upvotes: 0

eli geske
eli geske

Reputation: 596

Here is a full working example of a card updating after a button click with a random number showing the update:

Example App Screenshot

appscript.json

{
  "timeZone": "Etc/GMT",
  "exceptionLogging": "STACKDRIVER",
  "oauthScopes": [
    "https://www.googleapis.com/auth/gmail.addons.execute"
  ],
  "gmail": {
    "name": "Demo Card Refresh",
    "logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/receipt_black_24dp.png",
    "homepageTrigger": {
      "runFunction": "myFunction"
    }
  },
  "runtimeVersion": "V8"
}

Code.gs

function myFunction(e) {
    return myCard().build();
}

function myCard() {
  return CardService
    .newCardBuilder()
    .setHeader(
      CardService.newCardHeader().setTitle('Refresh ID: ' + Math.random(9999999999))
    )
    .addSection(
      CardService.newCardSection()
        .setHeader('Section')
        .addWidget(
          CardService.newTextButton()
            .setText('Refresh')
            .setOnClickAction(
                CardService.newAction()
                  .setFunctionName('myAction')
            )
        )
    )
}

function myAction() {
  return CardService.newNavigation().updateCard(myCard().build());
}

Upvotes: 0

discoStew
discoStew

Reputation: 247

I struggled with this for over a day, improving on Glen Little's answer so that its a bit more clear.

I have my root card to be refreshed defined in a funciton called: onHomepage.

I update the appscript.json manifest to set the homepageTrigger and onTriggerFunction to return the function that builds my root card.

"gmail": {
      "homepageTrigger": {
          "enabled": true,
          "runFunction":"onHomepage"
        },
      "contextualTriggers":[
        {
          "unconditional":{},
          "onTriggerFunction": "onHomepage"
        }
      ]  
      }

Then it is as simple as building a gotoRoot nav button function that will always refresh the root page.

  function gotoRootCard() {
    var nav = CardService.newNavigation()
    .popToRoot()
    .updateCard(onHomepage());
    
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();        
  }

Upvotes: 5

blabes
blabes

Reputation: 11

A bad hack that works for my Gmail add-on:

return CardService.newActionResponseBuilder()
  .setStateChanged(true) // this doesn't seem to do much.  Wish it would reload the add-on
  .setNotification(CardService.newNotification()
                   .setText('Created calendar event')
  )
  // HACK!  Open a URL which closes itself in order to activate the RELOAD_ADD_ON side-effect
  .setOpenLink(CardService.newOpenLink() 
               .setUrl("https://some_site.com/close_yoself.html")
               .setOnClose(CardService.OnClose.RELOAD_ADD_ON))
  .build();

The contents of close_yoself.html is just:

<!DOCTYPE html>
<html><body onload="self.close()"></body></html>

So, it looks like Google has considered and solved this issue for an ActionResponse which uses OpenLink, but not for one using Navigation or Notification. The hack above is definitely not great as it briefly opens and closes a browser window, but at least it refreshes the add-on without the user having to do so manually.

Upvotes: 1

hhsb
hhsb

Reputation: 582

As far as gmail addons are considered, cards are not refreshed but updated with new cards. And it is pretty simple.

//lets assume you need a form to be updated
function updateProfile() {
    //ajax calls
    //...

    //recreate the card again.
    var card = CardService.newCardBuilder();
    //fill it with widgets
    //....

    //replace the current outdated card with the newly created card.
    return CardService.newNavigation().updateCard(card.build());
}

Upvotes: 2

Glen Little
Glen Little

Reputation: 7128

The only way I've found to do this is to always use a single card for the root. In the main function (named in the appscript.json onTriggerFunction), return only a single card, not an array of cards. You can then use popToRoot().updateCard(...) and it works.

Upvotes: 6

Related Questions