Jon Pellant
Jon Pellant

Reputation: 400

insert items in a list

I search for a placeholder in my document that is a single list item (e.g., body.findText('{{list}}'). I have an array of items I want to insert into this list.

Once I have a listItem element, how do I append more listItems to it? I would expect there to be something like foundListItem.getParent().appendListItem(items[i])

I have searched for the answer to this question and have seen a few answers that require some Herculean effort to pull off. There must be an easier way! For example appending them to the bottom and figuring out where to move it to is a ridiculous answer.

Upvotes: 0

Views: 6020

Answers (2)

Jon Pellant
Jon Pellant

Reputation: 400

I was able to implement the desired behavior with this code:

function updateList(){
  var listItem = DocumentApp.getActiveDocument().getBody().getListItems()[0]
  var list = listItem.getParent()
  var index = list.getChildIndex(listItem)
  debug && Logger.log('ListItem ['+index+'] parent: '+list.getType()) // debug=true logging

  // remove listItem
  list.removeChild(listItem)

  // add new children
  var names = getList() // function retrieves a list of rows from spreadsheet.
  names.forEach(function (v){
    list.insertListItem(index++, v[1]) // the second element is the name
  })
}

Note that this does not do it with template string which is okay for me this time; however, to find the template string '{{list}}' and replace it with data I have found that this works:

function findListItem(str) {
  if (str == undefined) str = '{{list}}'
  debug && Logger.log(str)
  var a = ['one', 'two', 'three']
  var body = DocumentApp.getActiveDocument().getBody()
  var match = body.findText(str)
  if (match == null) {
    debug && Logger.log('No match')
    return
  }
  var listItem = match.getElement().getParent()
  var list = listItem.getParent()
  var idx = list.getChildIndex(listItem)
  listItem.removeFromParent()
  a.forEach(function(s) {
    list.insertListItem(idx++, s)
  })
}

Upvotes: 2

Aidan
Aidan

Reputation: 1770

It seems pretty cumbersome, Lists are made up of ListItems but there doesn't seem to be a List object. There is a ListId, but it doesn't seem to have any real function other than you can split your list up and continue the numbers. You seem to only be able to add a ListItem to the body, but there doesn't seem to be an easy way to get the index for a ListItem so that you can append after it.

I wrote a function that will iterate all the items in the body to find a ListItem with some placeholder text and return its index.

function findListItemWithText(text) {

  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();
  var index = -1;

  for (var i=0; i<body.getNumChildren(); i++) {
    var child = body.getChild(i);

    if (child.getType() ==  DocumentApp.ElementType.LIST_ITEM) {

      var listItem = child.asListItem();
      if (listItem.getText() == text) {
         index = i;
      }
    }
  }
  return index;
}

I then wrote a function that will replace a ListItem with the elements in an array:

function replaceListItem (placeholder, list) {

  var index = findListItemWithText(placeholder);
  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();

  var listItem = body.getChild(index).asListItem();

  // replace the text in the placeholder ListItem
  listItem.setText(list[0]);

  // append the rest of the list after the placeholder ListItem
  for (var i=1; i<list.length; i++) {
    body.insertListItem(index + i, list[i]);  
  }
}

You can then call this function with a placeholder and a list and if there is a ListItem with this text it will append ListItems the list at that point.

replaceListItem("{{list}}", ["One", "Two", "Three"]);

It works for numbered lists and bulleted ones. If there are two or more placeholders it will just replace the last one.

If anyone can critique this I'd be interested in hearing about more efficient ways to find and manipulate a given element as it seems like my solution is a lot more effort than I would have expected.

Upvotes: 2

Related Questions