From 5eed863169134c84a45931d159e5ca69ae405dce Mon Sep 17 00:00:00 2001 From: "david.bendavid" Date: Mon, 9 Feb 2026 12:50:26 +0200 Subject: [PATCH 1/2] NewImageDir: assets public path handling --- .../services/assets-public-path.directive.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/app/services/assets-public-path.directive.ts diff --git a/src/app/services/assets-public-path.directive.ts b/src/app/services/assets-public-path.directive.ts new file mode 100644 index 00000000..c833e615 --- /dev/null +++ b/src/app/services/assets-public-path.directive.ts @@ -0,0 +1,44 @@ +import { Directive, ElementRef, Input, OnChanges, Renderer2 } from '@angular/core'; + +declare const __webpack_public_path__: string; + +@Directive({ + selector: 'img[src],source[src],link[href],script[src],a[href]', + standalone: true, +}) +export class AssetsPublicPathDirective implements OnChanges { + @Input() src?: string; + @Input() href?: string; + + constructor(private el: ElementRef, private r: Renderer2) {} + + ngOnChanges(): void { + // Handle [src] / src and [href] / href + const tag = (this.el.nativeElement.tagName || '').toLowerCase(); + const attr = tag === 'link' || tag === 'a' ? 'href' : 'src'; + const val = attr === 'href' ? (this.href ?? this.el.nativeElement.getAttribute('href')) : (this.src ?? this.el.nativeElement.getAttribute('src')); + + if (!val) return; + + const rewritten = this.rewriteIfAssets(val); + if (rewritten !== val) { + this.r.setAttribute(this.el.nativeElement, attr, rewritten); + } + } + + private rewriteIfAssets(url: string): string { + // Don’t touch absolute URLs, data:, blob:, etc. + if (/^(https?:)?\/\//i.test(url) || /^data:|^blob:/i.test(url)) return url; + + // Normalize + const clean = url.startsWith('/') ? url.slice(1) : url; + + if (clean.startsWith('assets/')) { + const base = (typeof __webpack_public_path__ === 'string' ? __webpack_public_path__ : '') || ''; + const baseFixed = base && !base.endsWith('/') ? base + '/' : base; + return baseFixed + clean; + } + + return url; + } +} From c713f83e74feea0bf792192b00ceebbed6e3c47d Mon Sep 17 00:00:00 2001 From: "david.bendavid" Date: Thu, 22 Jan 2026 13:47:57 +0200 Subject: [PATCH 2/2] Auto-prefix assets/* URLs via webpack public path --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/README.md b/README.md index 66fcd992..c5a0389d 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,68 @@ Or as Observable: ```angular2html isLoggedIn$ = this.store.select(selectIsLoggedIn); ``` +### Automatic assets public path (`assets/...`) + +If your custom module/add-on is hosted under a dynamic base URL, plain template URLs like: + +```html + +``` +may break, because the browser resolves them relative to the host page. + +To fix this, we provide: + +src/app/services/assets-public-path.directive.ts + +This directive automatically rewrites any src / href that starts with assets/ (or /assets/) to: + +```js +__webpack_public_path__ + 'assets/...' +``` +Import the directive in a component (Standalone) +Add the directive to the component imports: + +```js +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { AssetsPublicPathDirective } from '../services/assets-public-path.directive'; + +@Component({ + selector: 'custom-example', + standalone: true, + templateUrl: './example.component.html', + styleUrls: ['./example.component.scss'], + imports: [CommonModule, AssetsPublicPathDirective], +}) +export class ExampleComponent {} +``` +If the component is not standalone (NgModule-based), import and export the directive from a shared module, and include that module where your components are declared. + +Use it in HTML +After the directive is imported, you can use plain assets/... paths in templates: + + +```html + +Logo + + +Clock + + +Help +``` +Supported attributes/elements (common cases): + +img[src] + +source[src] + +script[src] + +link[href] + +a[href] ### Accessing app router