我需要将相同的锚链接有条件地指向本地或外部资源。我试过了
<a [href]="outside?externalUrl:null" [routerLink]="outside?[]:['/route',id]" >test</a>
但这不起作用。我没有收到任何错误,但它指向同一本地页面并忽略外部 URL。有什么想法吗?
另一种选择是构建链接,但我找不到任何文档如何访问服务内的
routerLink
编辑:我知道我可以使用
*ngIf
克隆整个链接,但我不想这样做,我的链接包含一个带有很多选项的视频标签
最简单的方法是使用
*ngIf / else
:
<ng-container *ngIf="outside; else internalBlock">
<a [href]="externalUrl">External</a>
</ng-container>
<ng-template #internalBlock>
<a [routerLink]="['/route', id]">Internal</a>
</ng-template>
编辑#1:(丑陋的解决方法)
既然你不想用
*ngIf
(我还是不明白为什么),你可以这样做:
模板:
<a href="javascript:void(0)" (click)="handleClick(outside, '/route', id, externalUrl)">Link</a>
组件:
handleClick(outside: boolean, internalUrl: string, internalId: string, externalUrl: string): void {
if (outside) {
window.location.href = externalUrl;
// You can also use Location class of Angular
} else {
this.router.navigate([`${internalUrl}/${internalId}`]);
}
}
对于条件 href,在 attr 前面添加。在 href 对我有用之前,使用 null 作为值,如下所示:
[attr.href]="!item.subMenu ? item.url : null"
您可以通过在指令中注入 RouterLinkWithHref
来访问
routerLink 实例。
指令:
import { ElementRef, Optional, Input, Directive, OnChanges } from '@angular/core';
import { RouterLinkWithHref } from '@angular/router';
@Directive({
selector: '[externalLink]'
})
export class ExternalLinkDirective implements OnChanges {
@Input() externalLink: string;
constructor(
private el: ElementRef,
@Optional() private link: RouterLinkWithHref
) {}
ngOnChanges(){
if (!this.link || !this.externalLink) {
return;
}
this.el.nativeElement.href=this.link.href=this.externalLink;
// Replace onClick
this.link.onClick = (...args: any[]) => {
return true;
}
}
}
用途:
<!-- Ignore router link and use external link -->
<a routerLink="/some/path" externalLink="https://google.com">Link</a>
对于我的用例,
<a>
标签内的内容有一些嵌套的html,所以我必须为此创建一个自定义组件。
该组件的作用基本上是
ngTemplateOutlet
<a>
指令或 routerLink
的 href
标签。route-or-redirect.component.ts
import {
Component,
ContentChild,
Directive,
Input,
OnInit,
TemplateRef,
} from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
export interface Bookmark {
id: string;
title: string;
imgUrl: string;
}
@Directive({
selector: '[appLinkContent]',
})
export class RouteOrRedirectLinkContentDirective {}
@Component({
selector: 'app-router-or-redirect',
templateUrl: './router-or-redirect.component.html',
styleUrls: ['./router-or-redirect.component.scss'],
})
export class RouteOrRedirectComponent implements OnInit {
@Input() route = '/';
@Input() set externalLink(link: string) {
this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(link);
}
@Input() redirect = false;
@Input() bookmark!: Bookmark;
@ContentChild(RouteOrRedirectLinkContentDirective, { read: TemplateRef })
linkContent: TemplateRef<RouteOrRedirectLinkContentDirective> | undefined;
safeUrl: SafeResourceUrl | undefined;
constructor(private sanitizer: DomSanitizer) {}
ngOnInit(): void {}
}
route-or-redirect.component.html
<a [routerLink]="route" *ngIf="!redirect; else redirectLink">
<ng-container
*ngTemplateOutlet="linkContent; context: { $implicit: bookmark }"
></ng-container>
</a>
<ng-template #redirectLink>
<a [attr.href]="safeUrl">
<ng-container
*ngTemplateOutlet="linkContent; context: { $implicit: bookmark }"
></ng-container>
</a>
</ng-template>
route-or-redirect.component.ts
import { Component, OnInit, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { RouterLinkWithHref, RouterModule } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import {
Bookmark,
RouteOrRedirectComponent,
RouteOrRedirectLinkContentDirective,
} from './router-or-redirect.component';
@Component({
selector: 'app-test',
template: `
<app-router-or-redirect class="default-no-content"></app-router-or-redirect>
<app-router-or-redirect class="default-with-content">
<div *appLinkContent>test link</div>
</app-router-or-redirect>
<app-router-or-redirect
class="use-route"
route="/test"
externalLink="localhost:4200"
>
<div *appLinkContent>test link</div>
</app-router-or-redirect>
<app-router-or-redirect
class="use-redirect"
route="/test"
externalLink="localhost:4200"
[redirect]="true"
>
<div *appLinkContent>test link</div>
</app-router-or-redirect>
<app-router-or-redirect
class="link-with-context"
route="/test"
externalLink="localhost:4200"
[redirect]="true"
[bookmark]="bookmark"
>
<div *appLinkContent="let bookmark">
<img [attr.src]="bookmark.imgUrl" />
<h3>{{ bookmark.title }}</h3>
</div>
</app-router-or-redirect>
`,
styles: [],
})
export class TestRouterOrRedirectComponent implements OnInit {
bookmark: Bookmark = {
id: '1',
title: 'Test Link',
imgUrl: 'https://placeimg.com/640/480/any',
};
constructor() {}
ngOnInit(): void {}
}
describe('RouterOrRedirectComponent', () => {
let component: TestRouterOrRedirectComponent;
let fixture: ComponentFixture<TestRouterOrRedirectComponent>;
let defaultWithNoContent: RouteOrRedirectComponent;
let defaultWithContent: RouteOrRedirectComponent;
let useRoute: RouteOrRedirectComponent;
let useRedirect: RouteOrRedirectComponent;
beforeEach(async(() => {
TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [RouterModule, RouterTestingModule],
declarations: [
TestRouterOrRedirectComponent,
RouteOrRedirectComponent,
RouteOrRedirectLinkContentDirective,
],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestRouterOrRedirectComponent);
component = fixture.componentInstance;
defaultWithNoContent = fixture.debugElement.children[0].componentInstance;
defaultWithContent = fixture.debugElement.children[1].componentInstance;
useRoute = fixture.debugElement.children[2].componentInstance;
useRedirect = fixture.debugElement.children[3].componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should default to link with route point to /', () => {
const link = fixture.debugElement
.query(By.css('.default-no-content'))
.query(By.directive(RouterLinkWithHref));
expect(link).toBeTruthy();
expect(link.attributes.href).toBe('/');
});
it('should default to link with content and route points to /', () => {
const link = fixture.debugElement
.query(By.css('.default-with-content'))
.query(By.directive(RouterLinkWithHref));
expect(link.attributes.href).toBe('/');
expect(link.nativeElement.textContent).toBe('test link');
});
it('should use routerLink for <a> tag if "redirect" binding is not specified', () => {
const link = fixture.debugElement
.query(By.css('.use-route'))
.query(By.directive(RouterLinkWithHref));
expect(link.attributes.href).toBe('/test');
expect(link.nativeElement.textContent).toBe('test link');
});
it('should default "redirect" binding to false', () => {
expect(useRoute.redirect).toBe(false);
});
it('should use href for <a> tag if "redirect" is true', () => {
const link = fixture.debugElement
.query(By.css('.use-redirect'))
.query(By.css('a'));
expect(useRedirect.redirect).toBe(true);
expect(link.query(By.directive(RouterLinkWithHref))).toBeNull();
expect(link.nativeElement.href).toBe('localhost:4200');
expect(link.nativeElement.textContent).toBe('test link');
expect(useRoute.redirect).toBe(false);
});
it('should use the bound value as the link template context', () => {
const link = fixture.debugElement
.query(By.css('.link-with-context'))
.query(By.css('a'));
expect(link.query(By.css('h3')).nativeElement.textContent).toContain(
component.bookmark.title
);
expect(
link.query(By.css('img')).nativeElement.getAttribute('src')
).toContain(component.bookmark.imgUrl);
expect(useRoute.redirect).toBe(false);
});
});
经过大量调查,我对这个问题实施了不同的方法,因为我的
<a href>...</a>
内部包含代码(例如可点击的div)。例如,我不想使用 ngIf
,因为这迫使我复制 div 的内部代码。这就是我的解决方案:
<div>
<a [href]="getRouterLink()" >
<div class="section">
<!-- stuff inside the div -->
</div>
</a>
</div>
Component({
selector: 'app-home-section',
templateUrl: './home-section.component.html',
styleUrls: ['./home-section.component.scss']
})
export class HomeSectionComponent implements OnInit {
@Input() link: string;
constructor(private router: Router) { }
ngOnInit() {
}
isRouterLink() {
if (this.link) {
let firstLinkChar = this.link.charAt(0);
let isSlash = firstLinkChar == '/';
return isSlash;
}
return false;
}
getRouterLink() {
let url = this.isRouterLink() ? window.location.origin + this.link : 'http://' + this.link;
return url;
}
}
这是使其工作更简单的唯一方法,因为即使我将“www.example.com”直接放入
href
(有或没有[ ]
),它总是附加基本网址。虽然不漂亮,但是很实用。
[attr.href]="link || null"
@taras-d 的
directive
不再适用于较新版本的 Angular。 单击链接以更改两个角度屏幕之间的路径后,路由器会擦除 href 而不会触发 ngOnChanges。如果有人想使用指令,我添加了替代解决方案。
如果您仍然想使用指令,我已经修改了他的解决方案。由于它使用
ngDoCheck
,不幸的是它被频繁触发。因此,我添加了一些 if
子句来防止更改过于频繁
@Directive({
selector: '[externalLink]'
})
export class ExternalLinkDirective implements DoCheck {
@Input() externalLink: string;
constructor(
private el: ElementRef,
@Optional() private link: RouterLinkWithHref
) {}
ngDoCheck(){
if(this.externalLink && this.el.nativeElement.href != this.externalLink) {
this.el.nativeElement.href = this.externalLink;
}
if(this.link && this.externalLink != this.link.href) {
this.bindHrefToRouter();
}
}
private bindHrefToRouter() {
if(!this.externalLink) {//if externalLink is null/not supplied, nothing to do here
return;
}
if(this.link) { //if a [routerLink] directive was supplied but empty, we force it to follow the deeplink
this.link.href = this.externalLink;
this.link.onClick = () => { return true; };
}