精通Angular 8:可以节省时间的五件事。

这篇文章是为ng-newsletter创建的,最初发布在www.newline.co

Angular有一些边缘情况,如果您不熟悉细节,有时可能会浪费您的时间。
在过去的3.5年中,我一直在与Angular合作,以下是一些我了解到的有趣事情,它们可以节省您的时间。我们将谈论:

  • HTTPInterceptors如何与功能模块一起使用

  • 查询参数在角度路由器重定向上丢失

  • :: ng-deep span-避免封装特定元素(在突出显示搜索字词的示例中)

  • 没有订阅的Http

  • 在相同的URL导航上重新加载组件

让我们深入。

您可以在Stackblitz中查看可运行的演示,并在此Github存储库中查看代码。

如果您想通过掌握Angular-Subcribe的先进技术获得有关我未来的视频价格的通知,请联系我们 (并在此处观看一些免费视频)

#1。 HTTPInterceptors如何与功能模块一起使用

曾几何时,我在codementor.io上进行了指导会议,在那里我们集成了一些单独的Angular应用程序以用作主要项目应用程序的功能模块。代码已复制到项目功能文件夹,并添加了延迟加载的模块,并添加到了主应用程序路由配置。

主要的 app.routing.ts 看起来像这样:

const routes: Routes = (
  {path: '', redirectTo: 'home', pathMatch: 'full'},
  {path: 'home', component: HomeComponent},
  { path: 'feature',
    loadChildren: './some-feature/some-feature.module#SomeFeatureModule'}
);

feature.module

@NgModule({
  declarations: (
    SomeComponentComponent
  ),
  imports: (
    CommonModule,
    SomeFeatureRoutingModule,
    HttpClientModule
  ),
  bootstrap: (
    SomeComponentComponent
  )
})
export class SomeFeatureModule { }

最后,目录结构看起来与我们的演示项目中的代码非常相似:

乍一看-它应该像一种魅力。实际上确实如此。

除了一件小事。

主应用程序组件以及功能模块组件使用标准Angular执行一些HTTP调用 HttpClientModule 特定的令牌拦截器会将“授权”标头附加到所有传出的网络请求中。

但是以某种方式,功能模块网络请求没有由主应用程序的令牌拦截器处理。

但为什么?

好吧,我不会因阴谋折磨你。问题在于,在独立开发功能时-个人 HTTPClientModule 已添加到的导入数组 feature.module.ts。它使Angular创建了另一个实例 HttpModule 具有空的自定义拦截器列表。

如何避免呢?只是注释掉 HTTPClientModule 在除主应用程序模块之外的所有其他Angular模块中。 (App.module.ts

现在,功能模块如下所示:

@NgModule({
  declarations: (
    SomeComponentComponent
  ),
  imports: (
    CommonModule,
    SomeFeatureRoutingModule,
    // HttpClientModule  <-- commented out
  ),
  bootstrap: (
    SomeComponentComponent
  )
})
export class SomeFeatureModule { }

知道特殊性可以让您实施特定的 HTTPInterceptors 用于特定功能模块。您要做的就是添加 HTTPClientModule 到特定的Angular特征模块并定义 HTTP_INTERCEPTORS 提供程序也位于同一模块中。现在,在功能模块中,仅将使用其自己的拦截器,而所有其他应用程序模块将使用根应用程序模块拦截器。

@NgModule({
  declarations: (
    SomeComponentComponent
  ),
  imports: (
    CommonModule,
    SomeFeatureRoutingModule,
    HttpClientModule // separate feture module HTTPClientModule
  ),
  bootstrap: (
    SomeComponentComponent
  ),
  providers: (
    {
      provide: HTTP_INTERCEPTORS,
      useClass: FeatureInterceptor, // Hey, we have specific module interceptor now
      multi: true
    }
  ),
})
export class SomeFeatureModule { }

让我们检查一下它是如何工作的:

您可以在Stackblitz游乐场中使用代码。此外,完整的项目代码将上传到GitHub“ Mastering Angular”存储库。

#2。查询参数在角度路由器重定向上丢失

有时我们必须为Angular SPA指定查询参数。在这种情况下,我们的应用程序URL可能类似于:

http://yourdomain.com/?serach=text&page=3

它应该运作良好。但是很多时候,Angular路由配置使用redirectTo指令将请求引导到特定的本地组件:

const routes: Routes = (
  {path: '', redirectTo: '/home', pathMatch: 'full'},
  {path: 'home', component: HomeComponent}
)

而且我们希望Angular会将URL更改为:http://yourdomain.com/home?search=text&page=3
但是实际上Angular将其更改为:http://yourdomain.com/home。

查询参数丢失。 HomeComponent将无法获取它们

怎么处理呢?

有三种方法:

  1. 最简单的。只需对所有参数使用直接的HomeComponent路由URL:

http://yourdomain.com/home?search=text&page=3

在这种情况下,不会发生重定向,因此不会丢失queryParams。

  1. 最难。侦听所有路由事件,如果发生重定向,只需复制查询参数并将其还原/分配给/ home页面即可。
import {Component, OnInit} from '@angular/core';
import {NavigationEnd, Router} from '@angular/router';
import {filter, take, tap} from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ('./app.component.scss')
})
export class AppComponent implements OnInit {
  title = 'Mastering-angular';

  constructor(private route: Router) {
  }

  ngOnInit() {
    this.route.events.pipe(
      tap(() => console.log('tap')),
      filter((event) => event instanceof NavigationEnd
        && event.urlAfterRedirects === '/home'
        && event.url.search(/?/) > -1 // look for '?' 
      ),
      take(1) // run handler once
    ).subscribe((event: NavigationEnd) => {
      console.log('Test')
      // extract queryParams as value: pair object.
      const queryParams = event.url.split('?')(1)
        .split('&')
        .reduce((acc, item) => ({...acc, (item.split('=')(0)): item.split('=')(1)}), {});

      this.route.navigate((), {
        queryParams, // reload current route and add queryParams
        queryParamsHandling: 'merge'
      });
    });
  }
}
  1. 还有一个更好的解决方案-如果仅删除路由路径中的“ https://dev.to/”符号,查询参数就不会丢失,如下所示:
const routes: Routes = (
  {path: '', redirectTo: 'home', pathMatch: 'full'},
  {path: 'home', component: HomeComponent}
)

您可以在操场上使用不同的方法,并在文章存储库中检查代码。

哪种方法最适合您?在评论中留下您的选择。

#3。 :: ng-deep span-避免封装特定元素(在突出显示搜索字词的示例中)

好的,假设您有一项任务,使搜索文本在特定段落中突出显示。

你会怎么做?最简单的方法-保存原始文本。在每次搜索时,只需替换我们要查找的文本即可 textToSearch

  getHighlightedText(searchText) {
    const regexp = new RegExp(searchText, 'gi');
    const highlightedText = originalText.replace(regexp, '$&');
    return highlightedText;
  }

并在其中突出显示 component.scss 文件我们把规则:

.highlight { background-color: yellow;}

您运行它,但是…不起作用:

您的肠胃感觉为什么不起作用?我首先想到的是,我们的CSS规则不包含在最终的捆绑包中。因此,我决定进行搜索,这是我发现的内容:

.highlight(_ngcontent-tls-c1) {
  background-color: yellow;
}

Angular将CSS封装应用于所有组件css规则。这就是为什么我们的元素没有突出显示的原因。如何解决?只需使用:: ng-deep前缀告诉Angular不应修改此特定规则:

::ng-deep .highlight {
  background-color: yellow;
}

现在,如果我们以突出显示的方式检查搜索,它将很好用:

您可以在Stackblitz页面上使用代码,也可以在GitHub存储库中检查代码。您可以在此处阅读有关:: ng-deep和样式封装的更多信息。

#4。没有订阅的Http

如果您在Twitter上监视Angular社区,那么您可能会注意到模板驱动的开发正在成为一种新趋势。

这种方法的很好的例子是:

  1. @NikPoltoratsky的Async-pipeline-一组有趣的管道,它们在模板中应用了不同的RxJS运算符,例如:

   // app.component.ts
   @Component({
     template: <code>
       {{ title$ | debounceTime:1000 | async }}
     </code>,
   })
   export class AppComponent {
     title$: Observable<string> = of('Hello, Async Pipeline!');
   }
  1. Dominic Elm的ngx-template-streams-允许以反应方式将域事件分配给组件类中的特定属性:

   //html 
   <button (*click)="clicks$">Click Me (Stream)</button>

   //ts
   import { Component, OnInit } from '@angular/core';
   import { ObservableEvent } from '@typebytes/ngx-template-streams';
   import { Observable } from 'rxjs';

   @Component({...})
   export class AppComponent implements OnInit {
     @ObservableEvent()
     clicks$: Observable<any>;

     ngOnInit() {
       // we can either manually subscribe or use the async pipe
       this.clicks$.subscribe(console.log);
     }
   }
  1. ReactiveFox的ngxf / platform,具有非常好的文档-一组减少角度代码样板的结构化指令,例如:

    *route="let route">
       {{ route }} // is ActivatedRoute instance
   
  1. 来自Michael Hladky的有趣的“ RFC:组件建议”问题主题,涉及如何使Angular更具反应性。

当我们的Angular组件在ngOnInit挂钩中执行一些网络请求时,让我们将这种方法应用于相当日常的任务。通常,代码如下所示:

export class SomeComponentComponent implements OnInit {

  data = {};

  constructor(private http: HttpClient) { }

  ngOnInit() {
    this.http.get('https://jsonplaceholder.typicode.com/todos/2')
      .subscribe((data) => {
        this.data = data;
    });
  }

}
   class="card" style="width: 18rem;">
     class="card-body">
       class="card-title">User id: {{data.userId}} 
       class="card-text">Title: {{data.title}}

class="wrapper">Raw user data: {{data | json}}

但是,正如迈克尔·赫拉基(Michael Hladky)在他的文章“如何避免在Angular中观察到的东西”中提到的那样-它不是反应式编程:)。我会说-这不是模板驱动的方法。

我们可以做得更好吗?

是的,我们可以省略中间变量/属性,并使用Angular异步管道。现在我们的代码将如下所示:

export class HttpCallComponent {
  data$ = this.http.get('https://jsonplaceholder.typicode.com/todos/2');
  constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
}
 *ngIf="data$| async as data">
   class="card" style="width: 18rem;">
     class="card-body">
       class="card-title">User id: {{data.userId}} 
       class="card-text">Title: {{data.title}}

class="wrapper">Raw user data: {{data | json}}

但是在网络404响应的情况下如何处理错误呢?让我们应用catchError运算符来处理网络请求失败-我们将显示来自其回调的错误消息,以通知用户发生了什么情况。

export class HttpCallComponent {

  data$ = this.http.get('https://jsonplaceholder.typicode.com/todos/2').pipe(
    catchError((err) => {
      this.snackBar.open('Network request is failed', 'Failed', {
        duration: 1500,
      });
      return of();
    })
  );

  constructor(private http: HttpClient, private snackBar: MatSnackBar) { }

}

是时候检查其工作原理了:

LGTM您可以在Stackblitz游乐场中使用此示例,并在文章GitHub repo中检查代码。

#5。在相同的URL导航上重新加载组件

好的,您得到了一个遗留代码,其中在ngOninit()生命周期方法中设置了主要组件逻辑。这意味着如果时间很短-如果我们需要将组件值重置为初始值-从头开始重新创建整个组件会更容易。但是问题隐藏在这里:当我们尝试导航到已激活的相同URL时,Angular不会重新实例化组件。

好,这是一个问题。首先,找到解决方案的方法-我读了一篇不错的文章“ Angular:在同一URL导航上重新获取数据”。但是,它不提供重新初始化整个组件实例的解决方案-只是捕获再次单击已激活路由的事件的方法。这不是我们想要的。

如果可以加载其他路由然后返回到当前活动的路由,该怎么办?但这可能会导致页面上出现一些视觉伪像,因此用户可以对其进行观察。我们能以某种方式在没有任何视觉伪影的情况下做到这一点吗?

答案是“是”,我在这里找到了。简而言之-解决方案代码非常简单:

this.router.navigateByUrl('/', {skipLocationChange: true})
  .then(()=>this.router.navigate((<route>)));

要应用它,我们应该改变我们的 routeLInk 功能调用 app.component.html

   class="col-12 text-center">
       (routerLink)="('/home')" >Home
       (routerLink)="('/feature')" >Feature
       (routerLink)="('/search')" >Search
       (routerLink)="('/httpOnInit')" >HttpOnInit

       class="route_link" (click)="goToRandomNUmberPage($event)" >SameRouteReload
  

并在中应用此代码 app.component.ts

  goToRandomNumberPage(event) {
    event.preventDefault();

    this.router.navigateByUrl('/', {skipLocationChange: true})
      .then(() => this.router.navigate(('/sameRoute')));
  }

现在它应该可以像我们想要的那样工作:

您可以在Stackblitz Codepen中检查它,并在github repo中查看代码。

摘要

无论您是初学者还是经验丰富的开发人员,角度城市街道都是适合所有人的安全场所,但是在黑暗的角落,有些实体在等待轮到您浪费时间。我希望本文将帮助您消除时间浪费,并快速,高质量地提供可维护的干净代码。

如果您喜欢这篇文章,请加入我的Twitter

更多阅读和观看:

  1. 谨防 Angular可以偷走您的时间。

  2. 精通RxJS:当您意想不到时会咬你的运算符和函数

  3. 角度:在相同的URL导航上重新获取数据

  4. 动手RxJS进行Web开发

  5. Angular 8中的RxJS单元测试。整个图片(免费视频价格)。

  6. “ Angular会浪费您的时间” YouTube视频系列。

资讯来源:由0x资讯编译自DEV,原文:https://dev.to/oleksandr/mastering-angular-8-five-things-that-are-good-to-know-to-save-your-time-14hk ,版权归作者所有,未经许可,不得转载
你可能还喜欢