精通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将无法获取它们
怎么处理呢?
有三种方法:
- 最简单的。只需对所有参数使用直接的HomeComponent路由URL:
http://yourdomain.com/home?search=text&page=3
在这种情况下,不会发生重定向,因此不会丢失queryParams。
- 最难。侦听所有路由事件,如果发生重定向,只需复制查询参数并将其还原/分配给/ 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'
});
});
}
}
- 还有一个更好的解决方案-如果仅删除路由路径中的“ 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社区,那么您可能会注意到模板驱动的开发正在成为一种新趋势。
这种方法的很好的例子是:
-
@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!');
}
-
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);
}
}
-
ReactiveFox的ngxf / platform,具有非常好的文档-一组减少角度代码样板的结构化指令,例如:
*route="let route">
{{ route }} // is ActivatedRoute instance
- 来自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
:
并在中应用此代码 app.component.ts
:
goToRandomNumberPage(event) {
event.preventDefault();
this.router.navigateByUrl('/', {skipLocationChange: true})
.then(() => this.router.navigate(('/sameRoute')));
}
现在它应该可以像我们想要的那样工作:
凉
您可以在Stackblitz Codepen中检查它,并在github repo中查看代码。
摘要
无论您是初学者还是经验丰富的开发人员,角度城市街道都是适合所有人的安全场所,但是在黑暗的角落,有些实体在等待轮到您浪费时间。我希望本文将帮助您消除时间浪费,并快速,高质量地提供可维护的干净代码。
如果您喜欢这篇文章,请加入我的Twitter
更多阅读和观看:
-
谨防 Angular可以偷走您的时间。
-
精通RxJS:当您意想不到时会咬你的运算符和函数
-
角度:在相同的URL导航上重新获取数据
-
动手RxJS进行Web开发
-
Angular 8中的RxJS单元测试。整个图片(免费视频价格)。
-
“ Angular会浪费您的时间” YouTube视频系列。