Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to modify a HTML file with angular schematics

Let's say we have an existing angular component including menu.component.html :

<ul>
  <li><a href="/home">home</a></li>
  <li><a href="/about">about</a></li>
</ul>

The goal is to add a new link with angular schematics, just after "about"

 <li><a href="/contact">contact</a></li>

Any ideas?

like image 618
user4649102 Avatar asked Jul 16 '18 15:07

user4649102


2 Answers

Eko's answer is correct. I would like to expand on this answer and do two things:

  1. Use the tree recorder to update the template
  2. use jsdom to find the location in the template to update

With jsdom and similar tools, serializing an Angular template either will not work -- or break any camelCase, [boxed], (parenthetical), #tagged -- attributes. So we will only use jsdom to find the location of where we want to update.

function updateTemplate() {
  return (tree: Tree) => {
    const buffer = tree.read(TEMPLATE_PATH);
    const content = buffer?.toString();
    if (!content) {
      throw new SchematicsException(`Template ${TEMPLATE_PATH} not found`);
    }

    // the includeLocations flag is very important here
    const dom = new JSDOM(content, { includeNodeLocations: true });
    const element = dom.window.document.querySelector('ul');
    const locations = dom.nodeLocation(element);
    if (!locations) {
      throw new SchematicsException(`<ul> could not be found in ${TEMPLATE_PATH}`);
    }

    // now we update the template using the tree recorder
    // we don't use jsdom to directly update the template
    const recorder = tree.beginUpdate(TEMPLATE_PATH);
    const listItem = `  <li><a href="/contact">contact</a></li>\n`
    recorder.insertLeft(locations.endTag.startOffset, listItem);
    tree.commitUpdate(recorder);

    return tree;
  };
}

Again, eko's answer is correct. This answer illustrates how to use jsdom and the tree recorder together to update an Angular template -- which is useful for inserting directives, events, tags and methods into elements, wrapping sections and more complex transformations.

like image 54
Martin Avatar answered Oct 12 '22 16:10

Martin


You can do something like:

export function myComponent(options: any): Rule {
  return (tree: Tree, _context: SchematicContext) => {
    const content: Buffer | null = tree.read("./menu.component.html");
    let strContent: string = '';
    if(content) strContent = content.toString();

    const appendIndex = strContent.indexOf('</ul>');
    const content2Append = '    <li><a href="/contact">contact</a></li> \n';
    const updatedContent = strContent.slice(0, appendIndex) + content2Append + strContent.slice(appendIndex);

    tree.overwrite("./menu.component.html", updatedContent);
    return tree;
  };
}

Note that this is a very primitive solution. There are DOM parser libraries in nodejs which can make your life easier (jsdom). And you can take content2Append variable as an input and clean it up for your use case.

like image 21
eko Avatar answered Oct 12 '22 16:10

eko