How to modify a HTML file with angular schematics How to modify a HTML file with angular schematics angular angular

How to modify a HTML file with angular schematics


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.


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.


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 :)