How to modify a HTML file with angular schematics
Asked Answered
T

3

11

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?

Titular answered 16/7, 2018 at 15:31 Comment(2)
How much have you looked at how to build your own schematic? Have you gone through articles such as this: medium.com/@tomastrajan/…Disposed
I went through many articles. I found examples of modifying specific elements like a constructor or declaration in a module. here is an example link.Titular
C
18

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.

Chilcote answered 28/7, 2018 at 12:41 Comment(2)
Unfortunately jsdom doesn't support angular template syntax and I've been trying to search for a template parser that can help me add and manipulate a template any suggestions would be greatly appreciated!Wartburg
@saKins see my answer in how to use jsdom along with the tree recorder to update Angular templates.Pomace
P
8

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.

Pomace answered 30/4, 2020 at 7:34 Comment(1)
FYI, the simple htmlparser library seems to be able to read HTML without breaking the Angular-specific attributes. you can fairly easily modify the DOM. Rewriting it as a string is not as simple though.Calabresi
S
0

Update : With the latest 1.0.0rc3 cheerio version, the xmlMode does self close tags, and this kills your template ! So instead, go with 0.22.0 cheerio version and lose the xmlMode option !


I'm testing shematics and I need to update angular component templates as well. Here is a useful article :

https://medium.com/humanitec-developers/update-and-insert-auto-generated-code-to-existing-typescript-html-and-json-files-with-angular-9b37bdadf041

Now, be careful, with the latest versions of cheerio, you should use the xmlMode option to load your component template, or you will have a whole HTML document instead :

https://github.com/cheeriojs/cheerio/issues/1031#issuecomment-368307598

You would end up adding some HTML to an Angular template this way :

function addComponent(): Rule {
  return (tree: Tree) => {
    const filePath = './src/app/app.component.html';
    const content: Buffer | null = tree.read(filePath);
    let strContent: string = '';
    if(content) strContent = content.toString('utf8');
    const $ = cheerio.load(strContent, {
      xmlMode: true
    });
    const contentToInsert = `<my-comp></my-comp>`;
    $.root().append(contentToInsert);
    tree.overwrite(filePath, $.html());
    return tree;
  };
}

I hope this can help :)

Ssr answered 30/3, 2020 at 8:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.