在角度Mat-icon中设置默认的SVG图标后备

时间:2019-08-24 16:53:23

标签: angular angular-material

我有一个带有自定义svg图标的角垫图标。我正在动态渲染svg图标。现在以防万一,如果实际的svg图标丢失了,我该如何设置默认的(静态)svg图标。

类似于img标签中的onError attr的东西。

谢谢, 西哈德

html:
<mat-icon svgIcon="{{row.username}}" //default_svg_icon_src// ></mat-icon>

component:
  this.users.forEach(user => {
    this.matIconRegistry.addSvgIcon(
      user.username,
      this.domSanitizer.bypassSecurityTrustResourceUrl("../assets/icons/"+user.username+".svg")
    );
  })

2 个答案:

答案 0 :(得分:2)

您首先需要先检查该图标是否存在,然后再将其传递给注册表,然后再传递回退图标(如果不存在)。

您可以通过http请求或创建一个新的图像元素并绑定到onloadonerror函数来完成此操作。

使用http:

的示例

app.component.html

<div *ngIf="iconsLoaded">
  <div *ngFor = "let user of users">
    <mat-icon [svgIcon]="user"></mat-icon>
    <span>{{user}}</span>
  </div>
</div>  

app.component.ts

export class AppComponent  {
  users = ['user1','user2','user3','user4']
  iconsLoaded:boolean
  constructor(private iconRegistry: MatIconRegistry, private sanitizer: DomSanitizer, private http:HttpClient){
    this.addUserIcons()
  }

  async addUserIcons(){
    for(let user of this.users){
      const iconPath = await this.getIconPathWithFallback(user)
      this.iconRegistry.addSvgIcon(user, this.sanitizer.bypassSecurityTrustResourceUrl(iconPath));
    }
    // let the dom know when all icons have been loaded and block attempt rendering before (e.g. via *ngIf)
    this.iconsLoaded=true
  }

  // use a get request to see if the svg exists, if not return a default svg
  async getIconPathWithFallback(user:string){
      const iconPath = `assets/icons/${user}.svg`
      const response = await this.http.head(iconPath,{responseType: 'blob'}).toPromise()
      // if the svg icon is found the response will have type svg. If file not found returns html
      console.log('response type',response.type)
      return response.type === 'image/svg+xml' ? iconPath : 'assets/icons/default.svg'
  }   
}

这是一个有效的堆叠闪电战:https://stackblitz.com/edit/angular-ppbzvn

输出
example output

如果您使用标准的<img>标记而不是<mat-icon>,则可能无法将图片直接绑定到onerror调用以替换网址找到了。在另一篇文章中对此进行了讨论:

how can i check if image exists in assets folder using angular 4Angular2: check file existance with http.get

答案 1 :(得分:1)

接受的答案有两个主要问题:

  1. 实际上它建议异步注册图标 - 所以消费组件必须等待注册完成,否则图标不会被渲染(当然等待对性能不利)
  2. 它不可扩展:假设我们有大量想要注册的图标 - 那么我们有大量 HTTP 请求只是为了注册这些图标。

更好的解决方案:使用拦截器!

@Injectable()
export class SvgFallbackInterceptor implements HttpInterceptor {

    intercept(req: HttpRequest<unknown>, next: HttpHandler) {
        const regex = /\.svg$/;
        const isSvg = regex.test(req.url);

        if (isSvg) {
            return next.handle(req).pipe(
                catchError(err => {
                    const defaultResponse = new HttpResponse({
                        body:       `<svg> ...[your default svg]...</svg>`,
                        headers:    err.headers,
                        status:     200, // or perhaps 302?
                        statusText: 'OK',
                        url:        err.url,
                    });

                    return of(defaultResponse);
                })
            );
        }
        else {
            return next.handle(req);
        }
    }
}

read more about Angular interceptors here