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

6

6

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.

Erkan answered 11/4, 2018 at 18:16 Comment(5)
You have to replace the card with a new card.Com
@Com But in the main function it returns the array of cards, how can I call this function/or return an array of cards using an action button?Erkan
@Mohammad, did you figure this out?Curler
Found this open feature request: issuetracker.google.com/issues/71466051Curler
It's really unfortunate that this functionality exists for openLink where you can say ` CardService.OnClose.RELOAD_ADD_ON` - but it doesn't exist for cards -:-(Quaternity
C
6

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.

Curler answered 12/5, 2018 at 21:3 Comment(6)
Is udpateCard(...) a custom method ? If yes what should it return ? A card ?Caw
Here's how I've seen it used. First, make a Card, then return CardService.newActionResponseBuilder().setNavigation(CardService.newNavigation().popToRoot().updateCard(card)).build(); Nice and simple! :(Curler
Cool. I'll give it a try.Caw
Works! This does not seem to be a refresh. This seem to destroy the card and recreate it. However, this is a nice workaround and it is working :) .Caw
This is what I've done! Thanks for your comment. Marked as the answer :)Erkan
What if I have two triggers, how can I update the card generated from the first trigger.Nicolella
C
5

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();        
  }
Castanets answered 1/9, 2021 at 14:7 Comment(0)
C
2

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());
}
Caw answered 6/6, 2018 at 6:38 Comment(0)
J
1

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.

Jimmie answered 4/2, 2020 at 18:2 Comment(0)
R
0

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());
}
Ramrod answered 30/4, 2023 at 16:56 Comment(0)
H
0

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.

Hypersonic answered 25/1 at 13:30 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Cape

© 2022 - 2024 — McMap. All rights reserved.