如何有条件地在锚点上使用 [href] 或 [routerLink]?

问题描述 投票:0回答:8

我需要将相同的锚链接有条件地指向本地或外部资源。我试过了

<a [href]="outside?externalUrl:null"  [routerLink]="outside?[]:['/route',id]" >test</a>

但这不起作用。我没有收到任何错误,但它指向同一本地页面并忽略外部 URL。有什么想法吗?

另一种选择是构建链接,但我找不到任何文档如何访问服务内的

routerLink

编辑:我知道我可以使用

*ngIf
克隆整个链接,但我不想这样做,我的链接包含一个带有很多选项的视频标签

angular angular-routing
8个回答
40
投票

最简单的方法是使用

*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}`]);
  }
}

23
投票

对于条件 href,在 attr 前面添加。在 href 对我有用之前,使用 null 作为值,如下所示:

[attr.href]="!item.subMenu ? item.url : null"

3
投票

您可以通过在指令中注入 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>


1
投票

对于我的用例,

<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);
  });
});

0
投票

经过大量调查,我对这个问题实施了不同的方法,因为我的

<a href>...</a>
内部包含代码(例如可点击的div)。例如,我不想使用
ngIf
,因为这迫使我复制 div 的内部代码。这就是我的解决方案:

组件 HTML

<div>
   <a [href]="getRouterLink()" >
     <div class="section">
     <!-- stuff inside the div -->
     </div>   
   </a>
</div>

组件 JS

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
(有或没有
[ ]
),它总是附加基本网址。虽然不漂亮,但是很实用。


0
投票
[attr.href]="link || null"

0
投票

0
投票

@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; };
    }
© www.soinside.com 2019 - 2024. All rights reserved.