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?
Eko's answer is correct. I would like to expand on this answer and do two things:
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With