Skip to content

Commit 00a463c

Browse files
committed
Fix: Multiple Self-closing hooks that directly follow each other are now correctly parsed
1 parent d1dd7bd commit 00a463c

File tree

4 files changed

+45
-4
lines changed

4 files changed

+45
-4
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
44

55
## [Unreleased]
66

7+
## [3.1.2] - 2025-02-25
8+
### Maintenance
9+
- Fix: Multiple Self-closing hooks that directly follow each other are now correctly parsed
10+
711
## [3.1.1] - 2024-10-28
812
### Maintenance
913
- Fix: HookValue.elementSnapshot now contains a deep clone of the found element
@@ -153,7 +157,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
153157
### Added
154158
- This was the initial release, so everything was added here, really.
155159

156-
[Unreleased]: https://github.com/Angular-Dynamic-Hooks/ngx-dynamic-hooks/compare/v3.1.1...HEAD
160+
[Unreleased]: https://github.com/Angular-Dynamic-Hooks/ngx-dynamic-hooks/compare/v3.1.2...HEAD
161+
[3.1.2]: https://github.com/Angular-Dynamic-Hooks/ngx-dynamic-hooks/compare/v3.1.1...v3.1.2
157162
[3.1.1]: https://github.com/Angular-Dynamic-Hooks/ngx-dynamic-hooks/compare/v3.1.0...v3.1.1
158163
[3.1.0]: https://github.com/Angular-Dynamic-Hooks/ngx-dynamic-hooks/compare/v3.0.4...v3.1.0
159164
[3.0.4]: https://github.com/Angular-Dynamic-Hooks/ngx-dynamic-hooks/compare/v3.0.3...v3.0.4

projects/ngx-dynamic-hooks/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ngx-dynamic-hooks",
3-
"version": "3.1.1",
3+
"version": "3.1.2",
44
"description": "Automatically insert live Angular components into a dynamic string of content (based on their selector or any pattern of your choice) and render the result in the DOM.",
55
"person": "Marvin Tobisch <[email protected]>",
66
"license": "MIT",

projects/ngx-dynamic-hooks/src/lib/services/core/textHookFinder.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,15 @@ export class TextHookFinder {
258258
}
259259
}
260260

261-
// Actually replace hooks with anchors (from the back, so no need to change indexes)
262-
selectorReplaceInstructions.sort((a, b) => b.startIndex - a.startIndex);
261+
// Finally replace hooks with anchors
262+
// Process in backwards order, so no need to change indexes.
263+
// Primarily sort by startIndex. If multiple startIndexes are identical (possible with a follow-up hook to a self-closing hook), secondarily sort by endIndex.
264+
selectorReplaceInstructions.sort((a, b) => {
265+
let sortResult = b.startIndex - a.startIndex;
266+
if (sortResult === 0) sortResult = b.endIndex - a.endIndex;
267+
return sortResult;
268+
});
269+
263270
for (const selectorReplaceInstruction of selectorReplaceInstructions) {
264271
const textBeforeSelector = content.substring(0, selectorReplaceInstruction.startIndex);
265272
const textAfterSelector = content.substring(selectorReplaceInstruction.endIndex);

projects/ngx-dynamic-hooks/src/tests/integration/selectorHookParser/selectorHookParserConfig.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,35 @@ describe('SelectorHookParserConfig', () => {
383383
expect(comp.hookIndex[1].componentRef!.instance.simpleArray).toEqual(['arial', 'calibri']);
384384
});
385385

386+
it('#should correctly parse multiple self-closing hooks directly after one another', () => {
387+
({fixture, comp} = prepareTestingModule(() => [
388+
provideDynamicHooks({
389+
parsers: [{
390+
component: MultiTagTestComponent,
391+
allowSelfClosing: true,
392+
parseWithRegex: true
393+
}]
394+
})
395+
]));
396+
397+
const testText = `<p>Some initial text.<multitagtest [simpleArray]="['arial', 'calibri']"/><multitagtest [numberProp]="68135"/>. Some trailing text.</p>`;
398+
comp.content = testText;
399+
comp.ngOnChanges({content: true} as any);
400+
401+
expect(fixture.nativeElement.children[0].childNodes[0].textContent).toContain('Some initial text.');
402+
expect(fixture.nativeElement.children[0].childNodes[1].tagName).toBe('MULTITAGTEST');
403+
expect(fixture.nativeElement.children[0].childNodes[1].querySelector('.multitag-component')).not.toBe(null);
404+
expect(fixture.nativeElement.children[0].childNodes[1].querySelector('.multitag-component').innerHTML.trim()).toBe('');
405+
expect(fixture.nativeElement.children[0].childNodes[2].tagName).toBe('MULTITAGTEST');
406+
expect(fixture.nativeElement.children[0].childNodes[2].querySelector('.multitag-component')).not.toBe(null);
407+
expect(fixture.nativeElement.children[0].childNodes[2].querySelector('.multitag-component').innerHTML.trim()).toBe('');
408+
expect(fixture.nativeElement.children[0].childNodes[3].textContent).toContain('. Some trailing text.');
409+
expect(Object.keys(comp.hookIndex).length).toBe(2);
410+
expect(comp.hookIndex[1].componentRef!.instance.constructor.name).toBe('MultiTagTestComponent');
411+
expect(comp.hookIndex[1].componentRef!.instance.simpleArray).toEqual(['arial', 'calibri']);
412+
expect(comp.hookIndex[2].componentRef!.instance.numberProp).toBe(68135);
413+
});
414+
386415
it('#should disallow self-closing hooks, if requested', () => {
387416
({fixture, comp} = prepareTestingModule(() => [
388417
provideDynamicHooks({

0 commit comments

Comments
 (0)