Как заказать элементы-заполнители в Google Slides для использования в скрипте Google Apps

Обновлено: Окончательное решение было добавлено в качестве ответа, но я отметил ответ Танаике как решение, поскольку он указал мне в правильном направлении.

Я настраиваю Google AppScript для автоматизации некоторых отчетов. Я получаю некоторые данные, а затем хочу создать слайды по мере необходимости для отчета по всем данным.

Презентация, которую я использую в качестве шаблона, имеет разные макеты из трех столбцов с заполнителями подзаголовков для заголовков столбцов и заполнителями тела для текста столбца.

Предположим, макеты слайдов аналогичны рисунку ниже.

Используя замещающий текст для этих полей, я смог в некоторой степени понять расположение слайдов.

function getPlaceholders(layout) {
  layout.getPlaceholders().forEach((e, i) => {
    Logger.log("[%s] %s %s", i, e.getPageElementType(), p.getDescription());
  });
}

Напечатает что-то вроде [0.0] SHAPE Header 1, где Column 1 header — это альтернативный текст, который я установил для этого заполнителя.

Но я столкнулся с 2 проблемами:

  • Порядок элементов-заполнителей не соответствует визуальному порядку слайда.
    На первом слайде порядок заголовков и тела — 2-0-1.
  • Похоже, что в макетах есть некоторые ошибки, из-за которых некоторые поля не распознаются как заполнители, хотя на слайде в этих полях отображается текст заполнителя по умолчанию.
    Похоже, что копирование полей из предыдущего слайда вызывает эту проблему, и мне нужно вручную повторить все поля для каждого макета.

Сценарий ниже добавит только 2 слайда. На первом слайде поля будут заполнены.

Заголовок 0:2 Заголовок 0:0 Заголовок 0-1 Тело 0-2 Тело 0-0 Тело 0-1

Второй слайд не содержит текста в полях, поскольку сценарии, похоже, не могут их обнаружить.

function test(g) {
  let styles = [
    "CUSTOM_1",
    "CUSTOM_1_2",
    "CUSTOM_1_2_1",
    "CUSTOM_1_2_1_1"
  ]

  styles.forEach((style, index) => {
    let newslide = g.addSlide(g.getLayout(style))

    Logger.log("[%s] %s", index, style);
    newslide.getPlaceholders().forEach((p, i) => {
        // Won't print alt text as those aren't carried from template layouts to actual slides
        Logger.log("  [%s] %s %s %s", i, p.getPageElementType(), p.getTitle(), p.getDescription());
      });
    
    for (let i = 0; i < 3; i++) {
      newslide.getPlaceholder(SlidesApp.PlaceholderType.SUBTITLE, i).asShape().getText().setText(`Header ${index}-${i}`);
      newslide.getPlaceholder(SlidesApp.PlaceholderType.BODY, i).asShape().getText().setText(`Body ${index}-${i}`);
    }
  });
}

Это сообщит, например.

10:04:39 AM Info    [0.0] CUSTOM_1
10:04:39 AM Info      [0.0] SHAPE  
10:04:39 AM Info      [1.0] SHAPE  
10:04:39 AM Info      [2.0] SHAPE  
10:04:39 AM Info      [3.0] SHAPE  
10:04:39 AM Info      [4.0] SHAPE  
10:04:39 AM Info      [5.0] SHAPE  
10:04:39 AM Info      [6.0] SHAPE  
10:04:40 AM Info    [1.0] CUSTOM_1_2
10:04:40 AM Info      [0.0] SHAPE  
10:04:40 AM Info      [1.0] SHAPE  
10:04:40 AM Info      [2.0] SHAPE  
10:04:40 AM Info      [3.0] SHAPE  
10:04:40 AM Info      [4.0] SHAPE  
10:04:40 AM Info      [5.0] SHAPE  
10:04:40 AM Info      [6.0] SHAPE  
10:04:40 AM Error   
TypeError: Cannot read properties of null (reading 'asShape')
(anonymous) @ App.gs:115
test    @ App.gs:106
main    @ App.gs:132

Похоже, заполнители на втором слайде не распознаются как типы SlidesApp.PlaceholderType.SUBTITLE.

Поэтому я изменил цикл for следующим образом:

for (let i = 0; i < 3; i++) {
  newslide.getPlaceholders()[2*i].asShape().getText().setText(`Header ${index}-${i}`);
  newslide.getPlaceholders()[2*i+1].asShape().getText().setText(`Body ${index}-${i}`);
}

Однако это по-прежнему не дает хороших слайдов, поскольку порядок элементов по-прежнему нелогичен.

Слайд 1

Заголовок 0:2 Заголовок 0:0 Заголовок 0-1 Тело 0-2 Тело 0-0 Тело 0-1

Слайд 2

Тело 1-0 Заголовок 1-1 Заголовок 1-0 Заголовок 1-2 Тело 1-2 Нажмите, чтобы добавить текст

Слайд 3

Заголовок 2-1 Заголовок 2-0 Тело 2-0 Нажмите, чтобы добавить текст Заголовок 2-2 Тело 2-2

Слайд 4

Заголовок 3-1 Заголовок 3-0 Тело 3-0 Нажмите, чтобы добавить текст Заголовок 3-2 Тело 3-2

Чего я хочу добиться, так это иметь возможность программно заполнять эти слайды в логическом порядке. Т.е. (в псевдокоде)

let index = 0;
let slide;

report.forEach((r) => {
  if (index == 0) { slide = addSlide(layout_x) };

  slide.placeholder(type=subhead)[index].setText(r.title);
  slide.placeholder(type=body)[index].setText(r.content);

  index = (index + 1) % 3;
});

ТЛ;ДР;

Как настроить шаблон презентации так, чтобы получить последовательный и логичный порядок элементов-заполнителей?

Я должен извиниться за мое плохое знание английского языка. К сожалению, я не могу понять ваш вопрос. Показанные вами 4 таблицы, от «Слайд 1» до «Слайд 4», существуют на 1-4 страницах вашего слайда Google? И это входная ситуация? Если я правильно понимаю, могу ли я спросить вас о деталях a consistent and logical order of placeholder elements? Во-первых, мне хотелось бы правильно понять Ваш вопрос.

Tanaike 06.05.2024 13:27

@Tanaike Четыре таблицы представляют собой четыре слайда, созданные сценарием. Я отредактировал свое сообщение, добавив рисунок, который поможет вам визуализировать то, что представляют собой таблицы. Я ищу способ программно сказать: объект 1 из отчета, поместить заголовок в подзаголовок вверху слева и текст в теле вверху справа, в то время как объект 2 находится посередине, а объект 3 справа. Для obj4 создается новый слайд, содержимое помещается в левый столбец и т. д.

BlueCacti 06.05.2024 14:07

Спасибо за ответ. Судя по вашему ответу и обновленному вопросу, я подумал о подходе к решению вашей проблемы. В вашей ситуации как насчет управления ими с помощью названия и/или описания каждой фигуры в макете? Например, установите заголовок «Подзаголовок1» в левом верхнем углу «Макет слайда 1 (CUSTOM_1)». При этом вы можете выполнить поиск по форме «subhead1» с помощью скрипта. В моем случае я использую заголовок и описание для управления изображениями в Google Slides. Если это не было включено в ожидаемое вами направление, прошу прощения.

Tanaike 07.05.2024 02:24

Я пробовал это, используя текстовое описание Alt в макете (редакторе шаблонов), но они не копируются на слайд после создания слайда на основе этого слайда. Могу ли я использовать другое свойство?

BlueCacti 07.05.2024 09:25

Спасибо за ответ. Что касается I have tried this using the Alt text description in the Layout (Template Editor), but those aren't copied to the slide once a slide is created based on that slide., можете ли вы предоставить образцы слайдов для правильного воспроизведения?

Tanaike 07.05.2024 09:27

@Tanaike Я добавил несколько скриншотов. Там вы можете увидеть заполнитель субтитров и альтернативный текст. Они определены в макете слайда («Слайд» > «Редактировать тему»). Альтернативный текст можно получить через Presentation.getLayouts()[i].getPlaceholders()[i].getDescrip‌​tion();Developers.google.com/apps-script/reference/slides/… / Developers.google.com/apps-script/reference/slides/… / Developers. google.com/apps-script/reference/slides/…

BlueCacti 07.05.2024 09:34

Спасибо за ответ. На основе вашего ответа я хотел бы подготовить образец слайда Google с вашей ситуацией и протестировать его.

Tanaike 07.05.2024 09:48

В качестве другого подхода вместо заголовка и описания я предложил использовать в качестве ответа идентификатор объекта. Пожалуйста, подтвердите это. Если вы не ожидали такого подхода, я еще раз прошу прощения.

Tanaike 07.05.2024 15:06
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
8
106
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В качестве другого подхода вместо заголовка и описания хотелось бы предложить использовать ID объектов в макете. Если для макета задан слайд, идентификатор объекта макета можно увидеть как значение родительского объектаId. Когда объекты (например, их форма) извлекаются из макета, можно получить идентификатор объекта, заголовок и описание. Используя их, можно получить каждый идентификатор объекта, соответствующий каждому заголовку. Таким образом, даже если макет установлен на слайд, происхождение вставленных объектов можно узнать с помощью родительского идентификатора объекта. Если это отразить в простом примере сценария, это будет выглядеть следующим образом.

1. Пример макета

Пожалуйста, подготовьте следующий индивидуальный макет. И установите заголовок каждой фигуры как «subtitle1», «body1», «subtitle2», «body2».

2. Установите макет

Установите созданный пользовательский макет на первую страницу Google Slides. Это происходит следующим образом.

3. Пример сценария

Чтобы правильно вставить текст в каждый заполнитель, используется следующий пример сценария. Пожалуйста, установите имя пользовательского макета на layoutName. И, пожалуйста, установите заголовок и вставленный текст в качестве ключа и значения соответственно.

В этом примере тексты «образец заголовка 1», «образец тела 1», «образец заголовка 2», «образец тела 2» вставляются в заполнители «subtitle1», «body1», «subtitle2», «body2». ", соответственно.

function myFunction() {
  // Please set your custom layout name.
  const layoutName = "CUSTOM";

  // Please set the title and the inserted text as the key and value, respectively.
  const object = {
    subtitle1: "sample title 1",
    body1: "sample body 1",
    subtitle2: "sample title 2",
    body2: "sample body 2",
  };

  // Create an object including the parent object ID and the inserting text.
  const s = SlidesApp.getActivePresentation();
  const layout = s.getLayouts().find(e => e.getLayoutName() == layoutName);
  const newObject = layout.getPageElements().reduce((o, e) => (o[e.getObjectId()] = object[e.getTitle()] || "", o), {});

  // Insert the texts to each object of the placeholder.
  const slide = s.getSlides()[0]; // As a sample, 1st page is used.
  slide.getShapes().forEach(s => {
    const parentObjectId = s.getParentPlaceholder().getObjectId();
    if (newObject[parentObjectId]) {
      s.getText().setText(newObject[parentObjectId]);
    }
  });
}

4. Тестирование

Когда этот сценарий запускается в описанной выше ситуации ввода, получается следующий результат.

Примечание:

  • Это простой пример сценария для объяснения моего подхода. Поэтому, пожалуйста, измените это в соответствии с вашей реальной ситуацией.

  • В этом примере, чтобы упростить управление, я использовал идентификатор объекта и заголовок заполнителя. Однако, если вы уже знаете идентификаторы объектов всех заполнителей, я думаю, вы можете напрямую создать object в приведенном выше скрипте. В этом случае const newObject = ,,, использовать не обязательно.

Ссылка:

Спасибо за помощь! Я скоро попробую. Быстрый вопрос: где мне установить заголовок элемента. Это в разделе «Дополнительные параметры» в разделе «Альтернативный текст»?

BlueCacti 07.05.2024 15:26

@BlueCacti Спасибо за ответ. Насчет Quick question: where do I set the element's title. Is it under the Advanced Options under Alt text?, да. Вы можете установить заголовок фигуры в «Дополнительных параметрах» в «Замещающий текст» в разделе «Параметры формата».

Tanaike 07.05.2024 15:43

Потрясающий! Внес несколько изменений (также опубликую отредактированную версию в качестве ответа) и заставил ее работать 🎉

BlueCacti 08.05.2024 12:53

Это отредактированная копия функции, которую я в конечном итоге получил. Еще раз спасибо @Tanaike за то, что указал мне правильное направление.

Вероятно, я мог бы хранить ссылки на объекты TextRange вместо объектов Placeholder в функции reduce(), чтобы избежать дублирования .asShape().getText().

/**
 * Create the report slides
 * @param {Google} google Google instance
 * @param {Report} report Report instance
 */
function createReportSlides(google, report, project) {
  let styles = [
    "CUSTOM_1",       // layout 0
    "CUSTOM_1_2",     // layout 1
    "CUSTOM_1_2_1",   // layout 2
    "CUSTOM_1_2_1_1", // layout 3
  ]
  let iCounter = 0; // Item counter
  let curTitle = ""; // Current title

  // Uses global variable for list of topics ('title': str, 'items': [])
  TOPICS.forEach((t, i) => {
    let title = t.title;
    let slide;

    if (title != curTitle) {
      /**
       * @todo Add topic overview slide. Copy/move the correct slide or add?
       */
      curTitle = title;
      iCounter = 0;
      let objects = {};
    }

    t.items.forEach((item) => {
        let summaries = report.getItemSummary(project, title, item);

        if (summaries.length > 0) {
          if (iCounter == 0) {
            slide = google.addSlide(google.getLayout(styles[i]));
            objects = slide.getPlaceholders().reduce((o, e) => (
              // Store reference to placeholders based on Alt Title
              // Alt Title is Header_x or Body_x or empty
              o[e.asShape().getParentPlaceholder().getTitle() || "_"] = e, o), {}
            );
          }

          objects[`Header_${lCounter}`].asShape().getText().setText(item);
          objects[`Body_${lCounter}`].asShape().getText().setText(summaries.join("\v")); // \v = vertical tab (same as shift+return)

          iCounter = (iCounter+1)%3;
        }
    });
  });
}

Другие вопросы по теме