【问题标题】:Setting default svg icon fallback in angular mat-icon在角度 mat-icon 中设置默认 svg 图标后备
【发布时间】:2019-08-24 16:53:23
【问题描述】:

我有一个带有自定义 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")
    );
  })

【问题讨论】:

    标签: angular angular-material


    【解决方案1】:

    在将图标传递给注册表之前,您首先需要检查该图标是否存在,如果不存在则传递您的备用图标。

    您可以通过 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

    输出

    如果您要使用标准的&lt;img&gt; 标签而不是&lt;mat-icon&gt;,则可以直接绑定到onerror 调用以在找不到图像时替换url。此处的另一篇文章对此进行了讨论:

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

    【讨论】:

    • 尝试了 http 请求,但没有得到任何响应。 this.users.forEach(user =&gt; { let iconName = user.username; if(!this.checkAssetExists(iconName)) { iconName = "default"; } this.matIconRegistry.addSvgIcon( user.username, this.domSanitizer.bypassSecurityTrustResourceUrl("../assets/icons/"+iconName+".svg") ); })
    • async checkAssetExists(iconName) { try { await this.http.get("../assets/icons/"+iconName+".svg", {responseType: 'text'}).toPromise() return true } catch(error) { return false } }
    • 很奇怪,虽然我意识到一个额外的警告是,即使是失败的请求也会返回一个文本响应 (html)。已经创建了一个有效的 stackblitz 并将更新上面的答案
    • 我的错..!我直接在检查布尔条件时使用了 Promise。现在我在 promise 对象上尝试了 then 方法来获取实际的布尔值并且它起作用了。谢谢!
    【解决方案2】:

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

    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

    【讨论】:

      猜你喜欢
      • 2019-04-07
      • 2020-02-29
      • 2019-03-17
      • 1970-01-01
      • 2016-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-12
      相关资源
      最近更新 更多