La mayoría de los complementos basados en tarjetas se compilan con varias tarjetas que representan diferentes "páginas" de la interfaz del complemento. Para tener una experiencia del usuario efectiva, debes usar la navegación simple y natural entre tarjetas en tu complemento.
Originalmente, en los complementos de Gmail, las transiciones entre diferentes tarjetas de la IU se manejaban mediante el envío y la extracción de tarjetas desde y hacia una sola pila de tarjetas, con la tarjeta superior de la pila mostrada por Gmail.
Los complementos de Google Workspace incluyen páginas principales y tarjetas no contextuales. Para alojar tarjetas contextuales y no contextuales, los complementos de Google Workspace tienen una pila de tarjetas interna para cada uno. Cuando se abre un complemento en un host, el homepageTrigger
correspondiente se activa para crear la primera tarjeta de la página principal en la pila (la tarjeta azul de la página principal en el siguiente diagrama).
Si no se define un homepageTrigger
, se crea y muestra una tarjeta predeterminada en la pila no contextual. Esta es una tarjeta raíz.
Tu complemento puede crear tarjetas no contextuales adicionales y enviarlas a la pila (las "tarjetas" azules en el diagrama) a medida que el usuario navega por tu complemento. La IU del complemento muestra la tarjeta superior de la pila, por lo que enviar nuevas tarjetas a la pila cambia la visualización, y mostrar tarjetas de la pila muestra las tarjetas anteriores.
Si tu complemento tiene un activador contextual definido, cuando el usuario ingresa ese contexto, se activa el activador. La función de activador compila la tarjeta contextual, pero la pantalla de la IU se actualiza según el DisplayStyle
de la tarjeta nueva:
- Si el
DisplayStyle
esREPLACE
(el valor predeterminado), la tarjeta contextual (la tarjeta contextual "naranja oscura" en el diagrama) reemplaza la tarjeta que se muestra actualmente. Esta acción inicia de manera eficaz una nueva pila de tarjetas contextuales en la parte superior, y esta es la tarjeta raíz de la pila contextual. - Si el
DisplayStyle
esPEEK
, la IU crea, en cambio, un encabezado de visualización que aparece en la parte inferior de la barra lateral del complemento, que se superpone a la tarjeta actual. El encabezado de la vista previa muestra el título de la tarjeta nueva y proporciona los controles del botón de usuario que le permiten decidir si desea verla o no. Si hacen clic en el botón Ver, la tarjeta reemplaza la tarjeta actual (como se describió anteriormente conREPLACE
).
Puedes crear tarjetas contextuales adicionales y enviarlas a la pila (las “tarjetas enviadas” amarillas en el diagrama). La actualización de la pila de tarjetas cambia la IU del complemento para mostrar la tarjeta superior. Si el usuario sale de un contexto, se quitan las tarjetas contextuales de la pila y la pantalla se actualiza a la página principal o la tarjeta no contextual de la parte superior.
Si el usuario ingresa en un contexto en el que tu complemento no define un activador contextual, no se crea ninguna tarjeta nueva, y la tarjeta actual permanece visible.
Las acciones de Navigation
que se describen a continuación solo actúan en tarjetas del mismo contexto; por ejemplo, popToRoot()
desde una tarjeta contextual solo muestra todas las demás tarjetas contextuales y no afecta a las tarjetas de la página principal.
Por el contrario, el botón
siempre está disponible para que el usuario navegue desde tus tarjetas contextuales a las no contextuales.Métodos de navegación
Puedes crear transiciones entre tarjetas si las agregas o quitas de las pilas. La clase Navigation
proporciona funciones para enviar y extraer tarjetas de las pilas. A fin de crear una navegación eficaz con las tarjetas, configura los widgets para usar acciones de navegación. Puedes enviar o mostrar varias tarjetas de forma simultánea, pero no puedes quitar la tarjeta inicial de la página principal que se envía primero a la pila cuando se inicia el complemento.
Para navegar a una tarjeta nueva en respuesta a la interacción de un usuario con un widget, sigue estos pasos:
- Crea un objeto
Action
y asócialo con una función de devolución de llamada que definas. - Llama a la función de controlador de widget adecuada para configurar el
Action
en ese widget. - Implementa la función de devolución de llamada que lleva a cabo la navegación. A esta función
se le asigna un objeto de evento de acción
como argumento, y debe realizar lo siguiente:
- Crea un objeto
Navigation
para definir el cambio de tarjeta. Un solo objetoNavigation
puede contener varios pasos de navegación, que se realizan en el orden en que se agregan al objeto. - Compila un objeto
ActionResponse
con la claseActionResponseBuilder
y el objetoNavigation
. - Muestra el objeto
ActionResponse
compilado.
- Crea un objeto
Cuando compilas controles de navegación, usa las siguientes funciones de objeto Navigation
:
Function | Descripción |
---|---|
Navigation.pushCard(Card) |
Envía una tarjeta a la pila actual. Esto requiere compilar la tarjeta por completo primero. |
Navigation.popCard() |
Quita una tarjeta de la parte superior de la pila. Es equivalente a hacer clic en la flecha hacia atrás en la fila del encabezado del complemento. Esta acción no quita las tarjetas raíz. |
Navigation.popToRoot() |
Quita todas las tarjetas de la pila, excepto la tarjeta raíz. Básicamente, restablece esa pila de tarjetas. |
Navigation.popToNamedCard(String) |
Muestra tarjetas de la pila hasta que llega a una tarjeta con el nombre dado o la tarjeta raíz de la pila. Puedes asignarles nombres a las tarjetas mediante la función CardBuilder.setName(String) . |
Navigation.updateCard(Card) |
Hace un reemplazo local de la tarjeta actual y la actualiza en la IU. |
Prácticas recomendadas de navegación
Si la interacción o el evento de un usuario deben volver a renderizar tarjetas en el mismo contexto, usa los métodos Navigation.pushCard()
, Navigation.popCard()
y Navigation.updateCard()
para reemplazar las tarjetas existentes. Si una interacción o un evento de un usuario deben volver a procesar tarjetas en un contexto diferente, usa ActionResponseBuilder.setStateChanged()
para forzar la nueva ejecución de tu complemento en esos contextos.
Los siguientes son ejemplos de navegación:
- Si una interacción o un evento cambia el estado de la tarjeta actual (por ejemplo,
agregar una tarea a una lista de tareas), usa
updateCard()
. - Si una interacción o un evento proporciona más detalles o le pide al usuario que realice una acción adicional (por ejemplo, hacer clic en el título de un elemento para ver más detalles o presionar un botón para crear un nuevo evento de Calendario), usa
pushCard()
a fin de mostrar la página nueva y permite que el usuario salga de la página nueva con el botón Atrás. - Si una interacción o un evento actualiza el estado de una tarjeta anterior (por ejemplo, actualizar el título de un elemento con la vista de detalles), usa algo como
popCard()
,popCard()
,pushCard(previous)
ypushCard(current)
para actualizar la tarjeta anterior y la actual.
Actualizando tarjetas
Los complementos de Google Workspace permiten a los usuarios volver a ejecutar la función activadora de Apps Script registrada en el manifiesto para actualizar la tarjeta. Los usuarios activan esta actualización mediante un elemento de menú complementario:
Esta acción se agrega automáticamente a las tarjetas generadas por las funciones de activación homepageTrigger
o contextualTrigger
, como se especifica en el archivo de manifiesto del complemento (las "raíces" de las pilas de tarjetas contextuales y no contextuales).
Cómo devolver varias tarjetas
La función de página principal o activador contextual se usa para compilar y mostrar un solo objeto Card
o un arreglo de objetos Card
que muestra la IU de la aplicación.
Si solo hay una tarjeta, se agrega a la pila no contextual o contextual como la tarjeta raíz, y la IU de la aplicación host la muestra.
Si el arreglo que se muestra incluye más de un objeto Card
compilado, la aplicación host muestra una tarjeta nueva, que contiene una lista del encabezado de cada tarjeta. Cuando el usuario hace clic en cualquiera de esos encabezados, la IU muestra la tarjeta correspondiente.
Cuando el usuario selecciona una tarjeta de la lista, esa tarjeta se envía a la pila actual y la aplicación host la muestra. El botón
lleva al usuario a la lista de encabezados de la tarjeta.Esta disposición de tarjetas “plana” puede funcionar bien si tu complemento no necesita ninguna transición entre las tarjetas que creas. Sin embargo, en la mayoría de los casos, es recomendable definir directamente las transiciones de las tarjetas y hacer que la página principal y las funciones del activador contextual muestren un solo objeto de tarjeta.
Ejemplo
A continuación, se muestra un ejemplo que muestra cómo construir varias tarjetas con botones de navegación que salten entre ellas. Estas tarjetas se pueden agregar a la pila contextual o no contextual si se envía la tarjeta que muestra createNavigationCard()
dentro o fuera de un contexto en particular.
/**
* Create the top-level card, with buttons leading to each of three
* 'children' cards, as well as buttons to backtrack and return to the
* root card of the stack.
* @return {Card}
*/
function createNavigationCard() {
// Create a button set with actions to navigate to 3 different
// 'children' cards.
var buttonSet = CardService.newButtonSet();
for(var i = 1; i <= 3; i++) {
buttonSet.addButton(createToCardButton(i));
}
// Build the card with all the buttons (two rows)
var card = CardService.newCardBuilder()
.setHeader(CardService.newCardHeader().setTitle('Navigation'))
.addSection(CardService.newCardSection()
.addWidget(buttonSet)
.addWidget(buildPreviousAndRootButtonSet()));
return card.build();
}
/**
* Create a button that navigates to the specified child card.
* @return {TextButton}
*/
function createToCardButton(id) {
var action = CardService.newAction()
.setFunctionName('gotoChildCard')
.setParameters({'id': id.toString()});
var button = CardService.newTextButton()
.setText('Card ' + id)
.setOnClickAction(action);
return button;
}
/**
* Create a ButtonSet with two buttons: one that backtracks to the
* last card and another that returns to the original (root) card.
* @return {ButtonSet}
*/
function buildPreviousAndRootButtonSet() {
var previousButton = CardService.newTextButton()
.setText('Back')
.setOnClickAction(CardService.newAction()
.setFunctionName('gotoPreviousCard'));
var toRootButton = CardService.newTextButton()
.setText('To Root')
.setOnClickAction(CardService.newAction()
.setFunctionName('gotoRootCard'));
// Return a new ButtonSet containing these two buttons.
return CardService.newButtonSet()
.addButton(previousButton)
.addButton(toRootButton);
}
/**
* Create a child card, with buttons leading to each of the other
* child cards, and then navigate to it.
* @param {Object} e object containing the id of the card to build.
* @return {ActionResponse}
*/
function gotoChildCard(e) {
var id = parseInt(e.parameters.id); // Current card ID
var id2 = (id==3) ? 1 : id + 1; // 2nd card ID
var id3 = (id==1) ? 3 : id - 1; // 3rd card ID
var title = 'CARD ' + id;
// Create buttons that go to the other two child cards.
var buttonSet = CardService.newButtonSet()
.addButton(createToCardButton(id2))
.addButton(createToCardButton(id3));
// Build the child card.
var card = CardService.newCardBuilder()
.setHeader(CardService.newCardHeader().setTitle(title))
.addSection(CardService.newCardSection()
.addWidget(buttonSet)
.addWidget(buildPreviousAndRootButtonSet()))
.build();
// Create a Navigation object to push the card onto the stack.
// Return a built ActionResponse that uses the navigation object.
var nav = CardService.newNavigation().pushCard(card);
return CardService.newActionResponseBuilder()
.setNavigation(nav)
.build();
}
/**
* Pop a card from the stack.
* @return {ActionResponse}
*/
function gotoPreviousCard() {
var nav = CardService.newNavigation().popCard();
return CardService.newActionResponseBuilder()
.setNavigation(nav)
.build();
}
/**
* Return to the initial add-on card.
* @return {ActionResponse}
*/
function gotoRootCard() {
var nav = CardService.newNavigation().popToRoot();
return CardService.newActionResponseBuilder()
.setNavigation(nav)
.build();
}