Get data from a server

  1. 在本教程中,您将在Angular的HttpClient的帮助下添加以下数据持久性特性。
  2. HeroService通过HTTP请求获取英雄数据。用户可以通过HTTP添加、编辑和删除英雄并保存这些更改。
  3. 用户可以通过名字搜索英雄。

一、 启用HTTP服务

  1. HttpClient是Angular用来通过HTTP与远程服务器通信的机制。

  2. 通过两个步骤使应用程序中的所有地方都可以使用HttpClient。首先,通过导入将其添加到根AppModule:

 ```<-src/app/app.module.ts (HttpClientModule import)->import { HttpClientModule }    from '@angular/common/http';```
  1. *:*接下来,仍然在AppModule中,将HttpClient添加到导入数组中:

    <-src/app/app.module.ts (imports array excerpt)->
    @NgModule({imports: [HttpClientModule,],
    })
    

二、模拟数据服务器

  1. 本教程示例使用内存中的Web API模块模拟与远程数据服务器的通信。

  2. 安装模块后,应用程序将向HttpClient发出请求并接收响应,而不知道内存中的Web API正在拦截这些请求,将它们应用到内存中的数据存储中,并返回模拟的响应。

  3. 通过使用内存中的Web API,您不必设置服务器来了解HttpClient。

    重要提示:在Angular中,内存中的Web API模块与HTTP无关。
    如果您只是阅读本教程以了解HttpClient,那么您可以跳过这一步。如果您正在编写本教程中的代码,请留在这里并立即添加内存中的Web API。
    
  4. 使用以下命令从npm安装内存中的Web API包:

    npm install angular-in-memory-web-api --save
    
  5. 在AppModule中,导入HttpClientInMemoryWebApiModule和稍后将创建的InMemoryDataService类。

    <-src/app/app.module.ts (In-memory Web API imports)->
    import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
    import { InMemoryDataService }  from './in-memory-data.service';
    
  6. 在HttpClientModule之后,将HttpClientInMemoryWebApiModule添加到AppModule import数组中,并使用InMemoryDataService配置它。

    <-src/app/app.module.ts (imports array excerpt)->
    HttpClientModule,// HttpClientInMemoryWebApiModule模块拦截HTTP请求
    // 返回模拟的响应数据
    // 在真正的服务器准备接收请求时删除它
    HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, { dataEncapsulation: false }
    )
    
  7. forRoot()配置方法接受一个InMemoryDataService类,该类启动内存中的数据库。

  8. 生成类src/app/in-memory-data.service。ts使用以下命令:

  ```ng generate service InMemoryData```
  1. 用in-memory-data.service.ts的内容代替默认内容:

    <-src/app/in-memory-data.service.ts->
    import { InMemoryDbService } from 'angular-in-memory-web-api';
    import { Hero } from './hero';
    import { Injectable } from '@angular/core';@Injectable({providedIn: 'root',
    })
    export class InMemoryDataService implements InMemoryDbService {createDb() {const heroes = [{ id: 11, name: 'Dr Nice' },{ id: 12, name: 'Narco' },{ id: 13, name: 'Bombasto' },{ id: 14, name: 'Celeritas' },{ id: 15, name: 'Magneta' },{ id: 16, name: 'RubberMan' },{ id: 17, name: 'Dynama' },{ id: 18, name: 'Dr IQ' },{ id: 19, name: 'Magma' },{ id: 20, name: 'Tornado' }];return {heroes};}// 重写genId方法以确保英雄始终具有id。// 如果heroes数组是空的,// 下面的方法返回初始数字(11)。// 如果heroes数组不是空的,则下面的方法返回最高的值// 英雄id + 1。genId(heroes: Hero[]): number {return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;}
    }
    

    in-memory-data.service.ts文件代替了 mock-heroes.ts ,现在可以安全删除了。

    当服务器准备好时,您将分离内存中的Web API,应用程序的请求将经过服务器。

三、Heroes and HTTP

  1. 在HeroService中,导入HttpClient和HttpHeaders:

    <-src/app/hero.service.ts ->
    import { HttpClient, HttpHeaders } from '@angular/common/http';
    
  2. 仍然在HeroService中,将HttpClient注入到名为http的私有属性的构造函数中。

    <-src/app/hero.service.ts->
    constructor(private http: HttpClient,private messageService: MessageService) { }
    
  3. 注意,您一直在注入MessageService,但是由于您会频繁地调用它,所以将它封装在一个私有log()方法中:

    <-src/app/hero.service.ts->
    /** Log a HeroService message with the MessageService */
    private log(message: string) {this.messageService.add(`HeroService: ${message}`);
    }
    
  4. 使用服务器上英雄资源的地址来定义表单的heroesUrl:base/:collectionName。这里base是发出请求的资源,而collectionName是 in-memory-data-service.ts 中的heroes数据对象。

    <-src/app/hero.service.ts->
    private heroesUrl = 'api/heroes';  // URL to web api
    

四、Get heroes with HttpClient

  1. 当前的HeroService.getHeroes()使用()函数的RxJS来返回一个模拟英雄数组,作为一个可观察到的<Hero[]>。

    <-src/app/hero.service.ts (getHeroes with RxJs 'of()')->
    getHeroes(): Observable<Hero[]> {return of(HEROES);
    }
    
  2. 转换该方法使用HttpClient如下:

    <-src/app/hero.service.ts->
    /** GET heroes from the server */
    getHeroes (): Observable<Hero[]> {return this.http.get<Hero[]>(this.heroesUrl)
    }
    
    1. 刷新浏览器。应该可以成功地从模拟服务器加载英雄数据。
    2. 您已经将of()交换为http.get(),应用程序在没有任何其他更改的情况下继续工作,因为两个函数都返回一个可观察到的<Hero[]>。

HttpClient methods return one value

所有HttpClient方法都返回一个可观察到的RxJS。
HTTP是一个请求/响应协议。你发出一个请求,它会返回一个响应。
一般来说,一个被观察对象可以在一段时间内返回多个值。来自HttpClient的一个可观察对象总是发出一个单独的值,然后完成,不再发出。
这个特定的HttpClient.get()调用返回一个可观察的<Hero[]>;也就是说,“一个可见的英雄数组”。实际上,它只会返回一个英雄数组。

HttpClient.get() returns response data

  1. 默认情况下,HttpClient.get()以无类型JSON对象的形式返回响应主体。应用可选的类型说明符<Hero[]>,增加了TypeScript功能,减少了编译时的错误。

  2. 服务器的数据API决定JSON数据的形状。英雄指南数据API以数组的形式返回英雄数据。

```
其他api可能会将您想要的数据隐藏在对象中。您可能需要使用RxJS map()操作符来处理可观察结果,从而挖掘出数据。
虽然这里没有讨论,但是示例源代码中包含了getHeroNo404()方法中的map()示例。
```

Error handling

  1. 事情会出错,特别是当您从远程服务器获取数据时。getheroes()方法应该能捕获错误并执行适当的操作。

  2. 为了捕获错误,您可以通过RxJS的catchError()操作符从http.get()“管道”传输观察到的结果。

  3. 从rxjs/操作符以及稍后需要的其他操作符中导入catch错误符号。

 ```<-src/app/hero.service.ts->import { catchError, map, tap } from 'rxjs/operators';```
  1. 现在使用pipe()方法扩展observable结果,并给它一个catchError()操作符。

    <-src/app/hero.service.ts->
    getHeroes (): Observable<Hero[]> {return this.http.get<Hero[]>(this.heroesUrl).pipe(catchError(this.handleError<Hero[]>('getHeroes', []))//第一个参数是被监察的函数,第二个是返回的对象);
    }
    
  2. 操作符catchError()截获一个失败的观察目标。它向错误传递一个错误处理程序,该处理程序可以对错误做它想做的事情。

    1. 下面的handleError()方法报告错误,然后返回一个无害的结果,以便应用程序继续工作。

handleError

  1. 下面的handleError()将被许多HeroService方法共享,因此它被一般化以满足不同的需求。

  2. 它不是直接处理错误,而是返回一个错误处理函数来捕获它配置的错误,该函数使用失败操作的名称和一个安全的返回值。

 ```<-src/app/hero.service.ts->/*** 处理失败的Http操作。* 让应用程序继续。* @参数操作 - 失败操作的名称* @参数结果 - 可选值作为可观察结果返回*/private handleError<T> (operation = 'operation', result?: T) {return (error: any): Observable<T> => {// TODO:将错误发送到远程日志记录基础设施console.error(error); // 改为登录控制台// TODO:更好地转换用户使用的错误this.log(`${operation} failed: ${error.message}`);// 通过返回一个空结果让应用程序继续运行。return of(result as T);};}```
  1. 在向控制台报告错误之后,处理程序构造一个用户友好的消息并向应用程序返回一个安全值,以便应用程序可以继续工作。

    1. 因为每个服务方法都返回一种不同类型的可观察结果,所以handleError()接受一个类型参数,这样它就可以返回应用程序所期望的类型的安全值。

Tap into the Observable

  1. HeroService方法将进入可观察值流,并通过log()方法将消息发送到页面底部的消息区域。

  2. 他们将使用RxJS tap()操作符来完成这些操作,该操作符查看可观察值,对这些值执行一些操作,并将它们传递下去。tap()回调不会触及值本身。

  3. 下面是getHeroes()的最终版本,带有记录操作的tap()。

  ```<-src/app/hero.service.ts->/** GET heroes from the server */getHeroes (): Observable<Hero[]> {return this.http.get<Hero[]>(this.heroesUrl).pipe(tap(_ => this.log('fetched heroes')),catchError(this.handleError<Hero[]>('getHeroes', [])));}```

Get hero by id

  1. 大多数web api都支持以下形式的get by id请求:baseURL/:id。

  2. 这里,基本URL是在Heroes和HTTP部分(api/ Heroes)中定义的heroesURL, id是您想要检索的英雄的编号。例如,api /英雄/ 11。

  3. 使用以下方法更新HeroService getHero()方法以发出请求:

  ```<-src/app/hero.service.ts->/** GET hero by id. Will 404 if id not found */getHero(id: number): Observable<Hero> {const url = `${this.heroesUrl}/${id}`;return this.http.get<Hero>(url).pipe(tap(_ => this.log(`fetched hero id=${id}`)),catchError(this.handleError<Hero>(`getHero id=${id}`)));}```
  1. 与getHeroes()有三个显著的区别:

    getHero()使用所需的英雄id构造一个请求URL。
    服务器应该使用单个英雄响应,而不是一组英雄。
    getHero()返回一个可观察到的<Hero>(“一个可观察到的Hero对象”),而不是一个可观察到的Hero数组。
    
Update heroes
  1. 在英雄详情视图中编辑一个英雄的名字。当您键入时,英雄名称将更新页面顶部的标题。但是当你点击“返回”按钮时,改变就会丢失。

  2. 如果希望更改保持不变,则必须将它们写回服务器 。

  3. 在hero detail模板的末尾,添加一个带有click事件绑定的save按钮,该按钮调用一个名为save()的新组件方法。

  ```<-src/app/hero-detail/hero-detail.component.html (save)-><button (click)="save()">save</button>```
  1. 在HeroDetail组件类中,添加以下save()方法,该方法使用hero服务updateHero()方法持久保存英雄的名字更改,然后导航回之前的视图。

    <-src/app/hero-detail/hero-detail.component.ts (save)->
    save(): void {this.heroService.updateHero(this.hero).subscribe(() => this.goBack());}
    

Add HeroService.updateHero()

  1. updateHero()方法的整体结构与getHeroes()类似,但它使用http.put()将更改后的英雄持久化到服务器上。将以下内容添加到HeroService。

    <-src/app/hero.service.ts (update)->
    /** PUT: update the hero on the server */
    updateHero (hero: Hero): Observable<any> {return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(tap(_ => this.log(`updated hero id=${hero.id}`)),catchError(this.handleError<any>('updateHero')));
    }
    
    1. put()方法接受三个参数:
      1. the URL
      2. the data to update
      3. options
  2. URL没有改变。heroes web API通过查看英雄的id知道要更新哪个英雄。

  3. heroes web API期望在HTTP保存请求中有一个特殊的头。该头位于HeroService中定义的httpOptions常量中。将以下内容添加到HeroService类。

  ```<-src/app/hero.service.ts->httpOptions = {headers: new HttpHeaders({ 'Content-Type': 'application/json' })};```
  1. 刷新浏览器,更改英雄名并保存更改。HeroDetailComponent中的save()方法会导航到前一个视图。英雄现在以更改后的名字出现在列表中。

Add a new hero

  1. 要添加一个英雄,这个应用程序只需要英雄的名字。您可以使用一个元素与一个add按钮相匹配。

  2. 在HeroesComponent模板中,在标题后面插入以下内容:

  ```<-src/app/heroes/heroes.component.html (add)-><div><label>Hero name:<input #heroName /></label><!-- (click) passes input value to add() and then clears the input --><button (click)="add(heroName.value); heroName.value=''">add</button></div>```
  1. 在响应单击事件时,调用组件的单击处理程序add(),然后清除输入字段,以便为另一个名称做好准备。将以下内容添加到HeroesComponent类:

    <-src/app/heroes/heroes.component.ts (add)->
    add(name: string): void {name = name.trim();if (!name) { return; }this.heroService.addHero({ name } as Hero).subscribe(hero => {this.heroes.push(hero);});
    }
    
  2. 当给定的名称是非空时,处理程序将从名称中创建一个类似hero的对象(只是缺少id),并将其传递给服务addHero()方法。

  3. 当addHero()保存成功时,subscribe()回调将接收新英雄并将其推入heroes列表中进行显示。

  4. 将以下addHero()方法添加到HeroService类中。

  ```<-src/app/hero.service.ts (addHero)->/** POST: add a new hero to the server */addHero (hero: Hero): Observable<Hero> {return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),catchError(this.handleError<Hero>('addHero')));}```
  1. addHero()与updateHero()有两个不同之处:

    它调用HttpClient.post()而不是put()。
    它期望服务器为新英雄生成一个id,并在Observable< hero >中返回给调用者。
    
  2. 刷新浏览器并添加一些英雄。

Delete a hero

  1. 英雄列表中的每个英雄都应该有一个删除按钮。

  2. 元素中的英雄名称之后,将以下按钮元素添加到HeroesComponent模板中。

  ```<-src/app/heroes/heroes.component.html-><button class="delete" title="delete hero"(click)="delete(hero)">x</button>```
  1. 英雄列表的HTML应该是这样的:

    <-src/app/heroes/heroes.component.html (list of heroes)-> 
    <ul class="heroes"><li *ngFor="let hero of heroes"><a routerLink="/detail/{{hero.id}}"><span class="badge">{{hero.id}}</span> {{hero.name}}</a><button class="delete" title="delete hero"(click)="delete(hero)">x</button></li>
    </ul>
    
  2. 要将删除按钮放置在hero条目的最右侧,请向hero.com ponent. CSS添加一些CSS。您将在下面的最终评审代码中找到该CSS。

  3. 将delete()处理程序添加到组件类。

  ```delete(hero: Hero): void {this.heroes = this.heroes.filter(h => h !== hero);this.heroService.deleteHero(hero).subscribe();}```
  1. 尽管组件将hero删除委托给HeroService,但它仍然负责更新自己的英雄列表。组件的delete()方法将立即从该列表中删除hero-to-delete,并预测HeroService将在服务器上成功。

  2. 该组件实际上与heroService.delete()返回的Observable没有任何关系,但是它必须订阅。

  ```如果忽略了subscribe(),服务将不会向服务器发送删除请求。通常,一个被观察对象在订阅之前什么也不做。通过暂时删除subscribe(),单击“Dashboard”,然后单击“Heroes”,您自己确认一下。您将再次看到完整的英雄列表。```
  1. 接下来,像这样向HeroService添加一个deleteHero()方法。

    <-src/app/hero.service.ts (delete)->
    /** DELETE: delete the hero from the server */
    deleteHero (hero: Hero | number): Observable<Hero> {const id = typeof hero === 'number' ? hero : hero.id;const url = `${this.heroesUrl}/${id}`;return this.http.delete<Hero>(url, this.httpOptions).pipe(tap(_ => this.log(`deleted hero id=${id}`)),catchError(this.handleError<Hero>('deleteHero')));
    }
    
  2. 注意以下要点:

    1.deleteHero()调用HttpClient.delete ()。
    2.URL是英雄资源URL加上要删除的英雄的id。
    3.您不会像发送put()和post()那样发送数据。
    4.你仍然发送httpOptions。
    
  3. 刷新浏览器并尝试新的删除功能。

Search by name

  1. 在最后的练习中,您将学习如何将Observable操作符链接在一起,这样就可以最小化类似的HTTP请求的数量,并在经济上消耗网络带宽。
    2. 您将向仪表板添加一个英雄搜索功能。当用户在搜索框中键入一个名称时,您将对根据该名称过滤的英雄进行重复的HTTP请求。您的目标是根据需要发出尽可能多的请求。

HeroService.searchHeroes()

  1. 首先向HeroService添加一个searchHeroes()方法。

    <-src/app/hero.service.ts->
    /* GET heroes whose name contains search term */
    searchHeroes(term: string): Observable<Hero[]> {if (!term.trim()) {// if not search term, return empty hero array.return of([]);}return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(tap(x => x.length ?this.log(`found heroes matching "${term}"`) :this.log(`no heroes matching "${term}"`)),catchError(this.handleError<Hero[]>('searchHeroes', [])));
    }
    
    1. 如果没有搜索项,该方法立即返回一个空数组。其余部分与getHeroes()非常相似,惟一显著的区别是URL,它包含一个查询字符串和搜索词。

Add search to the Dashboard

  1. 打开DashboardComponent模板,并将hero search元素添加到标记的底部。

    <-src/app/dashboard/dashboard.component.html->
    <h3>Top Heroes</h3>
    <div class="grid grid-pad"><a *ngFor="let hero of heroes" class="col-1-4"routerLink="/detail/{{hero.id}}"><div class="module hero"><h4>{{hero.name}}</h4></div></a>
    </div><app-hero-search></app-hero-search>
    
    1. 这个模板看起来很像HeroesComponent模板中的*ngFor中继器。

    2. 要使其工作,下一步是添加一个与匹配的选择器组件。

Create HeroSearchComponent

  1. 使用CLI创建一个HeroSearchComponent。

    ng generate component hero-search
    
  2. CLI生成三个HeroSearchComponent文件,并将该组件添加到AppModule声明中。

  3. 将生成的HeroSearchComponent模板替换为和匹配的搜索结果列表,如下所示。

    <-src/app/hero-search/hero-search.component.html->
    <div id="search-component"><h4><label for="search-box">Hero Search</label></h4><input #searchBox id="search-box" (input)="search(searchBox.value)" /><ul class="search-result"><li *ngFor="let hero of heroes$ | async" ><a routerLink="/detail/{{hero.id}}">{{hero.name}}</a></li></ul>
    </div>
    
    1. 将私有CSS样式添加到hero-search.component.css中,如下面的最终代码审查中所列。
      5. 当用户在搜索框中键入内容时,输入事件绑定使用新的搜索框值调用组件的search()方法。

AsyncPipe

  1. *ngFor重复英雄对象。注意,*ngFor迭代了一个名为heroes的列表,而不是heroes。的列表,而不是heroes。heroes是一个约定,表示英雄$是一个可观察对象,而不是一个数组。

    <-src/app/hero-search/hero-search.component.html->
    <li *ngFor="let hero of heroes$ | async" >
    
    1. 因为*ngFor不能对一个可见对象做任何事情,所以使用带异步的管道字符(|)。这自动识别了Angular的AsyncPipe并订阅了一个可观察对象,所以你不必在组件类中这样做。

编辑HeroSearchComponent类

  1. 替换生成的HeroSearchComponent类和元数据,如下所示。

    <-src/app/hero-search/hero-search.component.ts->
    import { Component, OnInit } from '@angular/core';import { Observable, Subject } from 'rxjs';import {debounceTime, distinctUntilChanged, switchMap} from 'rxjs/operators';import { Hero } from '../hero';
    import { HeroService } from '../hero.service';@Component({selector: 'app-hero-search',templateUrl: './hero-search.component.html',styleUrls: [ './hero-search.component.css' ]
    })
    export class HeroSearchComponent implements OnInit {heroes$: Observable<Hero[]>;private searchTerms = new Subject<string>();constructor(private heroService: HeroService) {}// Push a search term into the observable stream.search(term: string): void {this.searchTerms.next(term);}ngOnInit(): void {this.heroes$ = this.searchTerms.pipe(// wait 300ms after each keystroke before considering the termdebounceTime(300),// ignore new term if same as previous termdistinctUntilChanged(),// switch to new search observable each time the term changesswitchMap((term: string) => this.heroService.searchHeroes(term)),);}
    }
    
  2. 注意hero$的声明是一个可观察值:

    <-src/app/hero-search/hero-search.component.ts->
    heroes$: Observable<Hero[]>;
    
  3. 您将在ngOnInit()中设置它。在此之前,请关注searchTerms的定义。

The searchTerms RxJS subject

  1. searchTerms属性是一个RxJS主题。

    <-src/app/hero-search/hero-search.component.ts->
    private searchTerms = new Subject<string>();// Push a search term into the observable stream.
    search(term: string): void {this.searchTerms.next(term);
    }
    
  2. 主体既是可观察值的来源,又是可观察值本身。你可以订阅一个主题,就像你可以订阅任何可见的主题一样。

  3. 您还可以像search()方法那样调用它的next(value)方法,将值推入到该Observable中。

  4. 将事件绑定到文本框的输入事件将调用search()方法。

    <-src/app/hero-search/hero-search.component.html->
    <input #searchBox id="search-box" (input)="search(searchBox.value)" />
    
  5. 每当用户在文本框中输入内容时,绑定就会使用文本框值“search term”调用search()。搜索词变成了一个可观察到的稳定的搜索词流。

链接RxJS运营商

  1. 在每个用户击键之后直接向searchHeroes()传递一个新的搜索项将会创建过多的HTTP请求,消耗服务器资源并烧毁数据计划。

  2. 相反,ngOnInit()方法通过一系列RxJS操作符来传递可观察到的searchTerms,这些操作符减少了对searchHeroes()的调用数量,最终返回一个可观察到的及时的英雄搜索结果(每个都是英雄[])。

  3. 下面进一步查看代码。

<-src/app/hero-search/hero-search.component.ts->
this.heroes$ = this.searchTerms.pipe(// wait 300ms after each keystroke before considering the termdebounceTime(300),// ignore new term if same as previous termdistinctUntilChanged(),// switch to new search observable each time the term changesswitchMap((term: string) => this.heroService.searchHeroes(term)),
);
  1. 每个操作如下:

    1.debounceTime(300)将等待新字符串事件流暂停300毫秒,然后传递最新的字符串。请求的频率永远不会超过300ms。
    2.distinctUntilChanged()确保仅在过滤器文本更改时才发送请求。
    3.switchMap()为通过debounce()和distinctUntilChanged()的每个搜索词调用搜索服务。它取消和丢弃以前的搜索观察结果,只返回最新的搜索服务观察结果。
    
    1.使用switchMap操作符,每个符合条件的密钥事件都可以触发HttpClient.get()方法调用。即使请求之间有300ms的停顿,您也可能有多个HTTP请求在运行,它们可能不会按照发送的顺序返回。
    2.switchMap()保留原始的请求顺序,同时只返回来自最近的HTTP方法调用的被观察对象。先前调用的结果被取消和丢弃。
    3.请注意,取消之前的searchHeroes()观察对象实际上并不会中止一个挂起的HTTP请求。不需要的结果在到达应用程序代码之前就会被丢弃。
    
    1. 请记住,组件类并不订阅heroes$ observable。这就是模板中的AsyncPipe的工作。

Try it

  1. 再次运行应用程序。在仪表板中,在搜索框中输入一些文本。如果您输入与任何现有英雄名称匹配的字符,您将看到类似这样的内容。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KTxVaEEr-1585827578122)(E:\桌面文件\Desktop\Desktop\微信截图_20200329231510.png)]

Final code review

<-hero.service.ts->
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';import { Hero } from './hero';
import { MessageService } from './message.service';@Injectable({ providedIn: 'root' })
export class HeroService {private heroesUrl = 'api/heroes';  // URL to web apihttpOptions = {headers: new HttpHeaders({ 'Content-Type': 'application/json' })};constructor(private http: HttpClient,private messageService: MessageService) { }/** GET heroes from the server */getHeroes (): Observable<Hero[]> {return this.http.get<Hero[]>(this.heroesUrl).pipe(tap(_ => this.log('fetched heroes')),catchError(this.handleError<Hero[]>('getHeroes', [])));}/** GET hero by id. Return `undefined` when id not found */getHeroNo404<Data>(id: number): Observable<Hero> {const url = `${this.heroesUrl}/?id=${id}`;return this.http.get<Hero[]>(url).pipe(map(heroes => heroes[0]), // returns a {0|1} element arraytap(h => {const outcome = h ? `fetched` : `did not find`;this.log(`${outcome} hero id=${id}`);}),catchError(this.handleError<Hero>(`getHero id=${id}`)));}/** GET hero by id. Will 404 if id not found */getHero(id: number): Observable<Hero> {const url = `${this.heroesUrl}/${id}`;return this.http.get<Hero>(url).pipe(tap(_ => this.log(`fetched hero id=${id}`)),catchError(this.handleError<Hero>(`getHero id=${id}`)));}/* GET heroes whose name contains search term */searchHeroes(term: string): Observable<Hero[]> {if (!term.trim()) {// if not search term, return empty hero array.return of([]);}return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(tap(x => x.length ?this.log(`found heroes matching "${term}"`) :this.log(`no heroes matching "${term}"`)),catchError(this.handleError<Hero[]>('searchHeroes', [])));}Save methods ///** POST: add a new hero to the server */addHero (hero: Hero): Observable<Hero> {return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),catchError(this.handleError<Hero>('addHero')));}/** DELETE: delete the hero from the server */deleteHero (hero: Hero | number): Observable<Hero> {const id = typeof hero === 'number' ? hero : hero.id;const url = `${this.heroesUrl}/${id}`;return this.http.delete<Hero>(url, this.httpOptions).pipe(tap(_ => this.log(`deleted hero id=${id}`)),catchError(this.handleError<Hero>('deleteHero')));}/** PUT: update the hero on the server */updateHero (hero: Hero): Observable<any> {return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(tap(_ => this.log(`updated hero id=${hero.id}`)),catchError(this.handleError<any>('updateHero')));}/*** Handle Http operation that failed.* Let the app continue.* @param operation - name of the operation that failed* @param result - optional value to return as the observable result*/private handleError<T> (operation = 'operation', result?: T) {return (error: any): Observable<T> => {// TODO: send the error to remote logging infrastructureconsole.error(error); // log to console instead// TODO: better job of transforming error for user consumptionthis.log(`${operation} failed: ${error.message}`);// Let the app keep running by returning an empty result.return of(result as T);};}/** Log a HeroService message with the MessageService */private log(message: string) {this.messageService.add(`HeroService: ${message}`);}
}
<-in-memory-data.service.ts->
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { Hero } from './hero';
import { Injectable } from '@angular/core';@Injectable({providedIn: 'root',
})
export class InMemoryDataService implements InMemoryDbService {createDb() {const heroes = [{ id: 11, name: 'Dr Nice' },{ id: 12, name: 'Narco' },{ id: 13, name: 'Bombasto' },{ id: 14, name: 'Celeritas' },{ id: 15, name: 'Magneta' },{ id: 16, name: 'RubberMan' },{ id: 17, name: 'Dynama' },{ id: 18, name: 'Dr IQ' },{ id: 19, name: 'Magma' },{ id: 20, name: 'Tornado' }];return {heroes};}// Overrides the genId method to ensure that a hero always has an id.// If the heroes array is empty,// the method below returns the initial number (11).// if the heroes array is not empty, the method below returns the highest// hero id + 1.genId(heroes: Hero[]): number {return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;}
}
<-app.module.ts->
import { NgModule }       from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { FormsModule }    from '@angular/forms';
import { HttpClientModule }    from '@angular/common/http';import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService }  from './in-memory-data.service';import { AppRoutingModule }     from './app-routing.module';import { AppComponent }         from './app.component';
import { DashboardComponent }   from './dashboard/dashboard.component';
import { HeroDetailComponent }  from './hero-detail/hero-detail.component';
import { HeroesComponent }      from './heroes/heroes.component';
import { HeroSearchComponent }  from './hero-search/hero-search.component';
import { MessagesComponent }    from './messages/messages.component';@NgModule({imports: [BrowserModule,FormsModule,AppRoutingModule,HttpClientModule,// The HttpClientInMemoryWebApiModule module intercepts HTTP requests// and returns simulated server responses.// Remove it when a real server is ready to receive requests.HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, { dataEncapsulation: false })],declarations: [AppComponent,DashboardComponent,HeroesComponent,HeroDetailComponent,MessagesComponent,HeroSearchComponent],bootstrap: [ AppComponent ]
})export class AppModule { }

HeroesComponent

<-heroes/heroes.component.html->
<h2>My Heroes</h2><div><label>Hero name:<input #heroName /></label><!-- (click) passes input value to add() and then clears the input --><button (click)="add(heroName.value); heroName.value=''">add</button>
</div><ul class="heroes"><li *ngFor="let hero of heroes"><a routerLink="/detail/{{hero.id}}"><span class="badge">{{hero.id}}</span> {{hero.name}}</a><button class="delete" title="delete hero"(click)="delete(hero)">x</button></li>
</ul>
<-heroes/heroes.component.ts->
import { Component, OnInit } from '@angular/core';import { Hero } from '../hero';
import { HeroService } from '../hero.service';@Component({selector: 'app-heroes',templateUrl: './heroes.component.html',styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {heroes: Hero[];constructor(private heroService: HeroService) { }ngOnInit() {this.getHeroes();}getHeroes(): void {this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes);}add(name: string): void {name = name.trim();if (!name) { return; }this.heroService.addHero({ name } as Hero).subscribe(hero => {this.heroes.push(hero);});}delete(hero: Hero): void {this.heroes = this.heroes.filter(h => h !== hero);this.heroService.deleteHero(hero).subscribe();}}
<-heroes/heroes.component.css->
import { Component, OnInit } from '@angular/core';import { Hero } from '../hero';
import { HeroService } from '../hero.service';@Component({selector: 'app-heroes',templateUrl: './heroes.component.html',styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {heroes: Hero[];constructor(private heroService: HeroService) { }ngOnInit() {this.getHeroes();}getHeroes(): void {this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes);}add(name: string): void {name = name.trim();if (!name) { return; }this.heroService.addHero({ name } as Hero).subscribe(hero => {this.heroes.push(hero);});}delete(hero: Hero): void {this.heroes = this.heroes.filter(h => h !== hero);this.heroService.deleteHero(hero).subscribe();}}

HeroDetailComponent

<-hero-detail/hero-detail.component.html->
<div *ngIf="hero"><h2>{{hero.name | uppercase}} Details</h2><div><span>id: </span>{{hero.id}}</div><div><label>name:<input [(ngModel)]="hero.name" placeholder="name"/></label></div><button (click)="goBack()">go back</button><button (click)="save()">save</button>
</div>
<-hero-detail/hero-detail.component.ts->
import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';import { Hero }         from '../hero';
import { HeroService }  from '../hero.service';@Component({selector: 'app-hero-detail',templateUrl: './hero-detail.component.html',styleUrls: [ './hero-detail.component.css' ]
})
export class HeroDetailComponent implements OnInit {@Input() hero: Hero;constructor(private route: ActivatedRoute,private heroService: HeroService,private location: Location) {}ngOnInit(): void {this.getHero();}getHero(): void {const id = +this.route.snapshot.paramMap.get('id');this.heroService.getHero(id).subscribe(hero => this.hero = hero);}goBack(): void {this.location.back();}save(): void {this.heroService.updateHero(this.hero).subscribe(() => this.goBack());}
}

DashboardComponent

<-src/app/dashboard/dashboard.component.html->
<h3>Top Heroes</h3>
<div class="grid grid-pad"><a *ngFor="let hero of heroes" class="col-1-4"routerLink="/detail/{{hero.id}}"><div class="module hero"><h4>{{hero.name}}</h4></div></a>
</div><app-hero-search></app-hero-search>

HeroSearchComponent

<-hero-search/hero-search.component.html->
<div id="search-component"><h4><label for="search-box">Hero Search</label></h4><input #searchBox id="search-box" (input)="search(searchBox.value)" /><ul class="search-result"><li *ngFor="let hero of heroes$ | async" ><a routerLink="/detail/{{hero.id}}">{{hero.name}}</a></li></ul>
</div>
<-hero-search/hero-search.component.ts->
import { Component, OnInit } from '@angular/core';import { Observable, Subject } from 'rxjs';import {debounceTime, distinctUntilChanged, switchMap} from 'rxjs/operators';import { Hero } from '../hero';
import { HeroService } from '../hero.service';@Component({selector: 'app-hero-search',templateUrl: './hero-search.component.html',styleUrls: [ './hero-search.component.css' ]
})
export class HeroSearchComponent implements OnInit {heroes$: Observable<Hero[]>;private searchTerms = new Subject<string>();constructor(private heroService: HeroService) {}// Push a search term into the observable stream.search(term: string): void {this.searchTerms.next(term);}ngOnInit(): void {this.heroes$ = this.searchTerms.pipe(// wait 300ms after each keystroke before considering the termdebounceTime(300),// ignore new term if same as previous termdistinctUntilChanged(),// switch to new search observable each time the term changesswitchMap((term: string) => this.heroService.searchHeroes(term)),);}
}
<-hero-search/hero-search.component.css->
import { Component, OnInit } from '@angular/core';import { Observable, Subject } from 'rxjs';import {debounceTime, distinctUntilChanged, switchMap} from 'rxjs/operators';import { Hero } from '../hero';
import { HeroService } from '../hero.service';@Component({selector: 'app-hero-search',templateUrl: './hero-search.component.html',styleUrls: [ './hero-search.component.css' ]
})
export class HeroSearchComponent implements OnInit {heroes$: Observable<Hero[]>;private searchTerms = new Subject<string>();constructor(private heroService: HeroService) {}// Push a search term into the observable stream.search(term: string): void {this.searchTerms.next(term);}ngOnInit(): void {this.heroes$ = this.searchTerms.pipe(// wait 300ms after each keystroke before considering the termdebounceTime(300),// ignore new term if same as previous termdistinctUntilChanged(),// switch to new search observable each time the term changesswitchMap((term: string) => this.heroService.searchHeroes(term)),);}
}

Summary

  1. 你已经走到了人生的终点,你已经取得了很多成就。

    1.您添加了在应用程序中使用HTTP所需的依赖项。
    2.您重构了HeroService以从web API加载英雄。
    3.您扩展了HeroService以支持post()、put()和delete()方法。
    4.您更新了组件以允许添加、编辑和删除英雄。
    5.您已经配置了内存中的web API。
    6.你学会了如何使用可观察的事物。
    

“英雄之旅”教程到此结束。您已经准备好在基础部分学习更多关于Angular开发的内容,从架构指南开始。

webSocket:

1. 封装浏览器提供的兼容w3c的WebSocket对象。 
2. 格式:
webSocket<T>(urlConfigOrSource: string | WebSocketSubjectConfig<T>): WebSocketSubject<T>

Parameters:

  1. urlConfigOrSource: 将WebSocket端点作为一个url或一个带有配置和附加观察者的对象。

2. Returns: 允许通过WebSocket连接发送和接收消息。

  1. Description: 通过WebSocket与服务器通信的主题

  2. webSocket是一个工厂函数,它生成一个WebSocketSubject,可以使用它与任意端点进行webSocket连接。webSocket可以接受一个带有webSocket端点url的字符串作为参数,也可以接受一个WebSocketSubjectConfig对象作为参数来提供额外的配置,以及跟踪webSocket连接生命周期的观察者。

  3. 当WebSocketSubject被订阅时,它会尝试建立一个socker连接,除非已经建立了一个。这意味着许多订阅者将始终监听相同的socket,从而节省了资源。但是,如果WebSocketSubject有两个实例,即使这两个实例提供了相同的url,它们也会尝试建立单独的连接。当WebSocketSubject的使用者取消订阅时,仅当没有更多的订阅者仍在监听时,socket连接才会关闭。如果一段时间后用户再次开始订阅,连接将重新建立。

  4. 一旦建立了连接,无论何时来自服务器的新消息,WebSocketSubject都会将该消息作为一个值发送到流中。默认情况下,通过JSON.parse解析来自socket的消息。如果您想定制如何处理反序列化(如果有的话),您可以在WebSocketSubject中提供定制的resultSelector函数。当连接关闭时,如果没有任何错误,流将完成。如果在任何点(启动、维护或关闭连接)出现错误,流也会因为抛出的任何WebSocket API而出现错误。

  5. 由于是Subject, WebSocketSubject允许接收和发送来自服务器的消息。为了与连接的端点通信,请使用next、error和complete方法。next方法向服务器发送一个值,因此请记住,**这个值不会被预先序列化。**因此,在调用带有结果的next方法之前,必须手动调用JSON.stringify的值。还要注意,如果在next(时没有socket连接(例如没有人订阅),这些值将被缓冲,并在连接最终建立时再发送 . complete()关闭socket连接。error()执行相同的操作, 通过状态代码和字符串来通知服务器发生了错误。由于WebSocket API中需要状态代码,所以WebSocketSubject不允许像常规Subject一样将任意值传递给error方法。它需要使用一个对象来调用,该对象具有带有状态代码编号的code属性和带有描述错误细节的字符串的可选reason属性。

  6. 调用next不会影响WebSocketSubject的订阅者——他们不知道有什么东西被发送到服务器(当然,除非服务器以某种方式响应消息)。另一方面,因为调用complete会触发关闭socket连接的尝试。 如果该连接关闭,没有任何错误,流将完成,从而通知所有订阅者。由于调用错误也会关闭套接字连接,只是使用了不同的服务器状态代码,如果关闭本身没有出现错误,则订阅的Observable将不会出现错误(正如人们可能预期的那样),而是像往常一样完成。在这两种情况下(调用complete或error),如果关闭socket连接的进程导致一些错误,则stream将出错。

  7. 多路复用

  8. WebSocketSubject有一个额外的操作符,在其他主题中找不到。它被称为multiplex,用于模拟打开多个socket连接,而实际上只维护一个。例如,一个应用程序同时具有聊天面板和关于体育新闻的实时通知。因为这是两个不同的函数,所以有两个独立的连接是有意义的。甚至可能有两个单独的带有WebSocket端点的服务,它们运行在单独的机器上,只有GUI将它们组合在一起。为每个功能提供socket连接可能会导致资源过于昂贵。通常使用单个WebSocket端点作为其他服务(在本例中是聊天和体育新闻服务)的网关。即使在客户端应用程序中只有一个连接,也需要能够像操作两个独立的socket那样操作流。这消除了在网关中为给定的服务手动注册和取消注册,并过滤掉感兴趣的消息。这正是多路复用法的用途。

  9. 方法接受三个参数。前两个函数分别返回订阅和取消订阅消息。这些消息将被发送到服务器,无论结果观察对象的使用者何时订阅和取消订阅。服务器可以使用它们来验证某种消息是否应该开始或停止转发到客户机。对于上述示例应用程序,网关服务器在获得正确标识符的订阅消息后,可以决定是否连接到真正的体育新闻服务,并开始从该服务转发消息。请注意,这两条消息都将作为函数的返回发送,它们在默认情况下使用JSON.stringify,就像通过next推送的消息一样。还要记住,这些消息将在每次订阅和取消订阅时发送。这是潜在的危险,因为一个Observable的消费者可能会取消订阅,而服务器可能会停止发送消息,因为它得到了取消订阅的消息。这需要在服务器上处理,或者使用从“multiplex”返回的可观察对象上的发布。

  10. multiplex的最后一个参数是messageFilter函数,它应该返回一个布尔值。它用于过滤服务器发送到仅属于模拟WebSocket流的消息。例如,服务器可能在消息对象上用某种字符串标识符标记这些消息,如果被socket发出的对象上有这样的标识符,messageFilter将返回true。messageFilter中返回false的消息将被简单地跳过,而不会传递到流中。

  11. multiplex的返回值是一个可观察的消息,来自模拟的socket连接。注意,这不是WebSocketSubject,因此再次调用next或multiplex将失败。要将值推送到服务器,使用根WebSocketSubject。

  12. Examples:

  13. 监听来自服务器的消息

  ```import { webSocket } from "rxjs/webSocket";const subject = webSocket("ws://localhost:8081");subject.subscribe(msg => console.log('message received: ' + msg), //每当有来自服务器的消息时调用。err => console.log(err), // 调用如果在任何点WebSocket API信号某种错误。() => console.log('complete') // 当连接关闭时调用(无论什么原因)。);```
  1. 将消息推送到服务器

    import { webSocket } from "rxjs/webSocket";
    const subject = webSocket('ws://localhost:8081');subject.subscribe();
    // 请注意,至少有一个用户必须订阅所创建的subject—否则“附加”的值只会被缓冲而不会被发送,
    subject.next({message: 'some message'});
    // 一旦建立了连接,就会向服务器发送一条消息。记住,值是用JSON序列化的。stringify默认!subject.complete(); // 关闭连接subject.error({code: 4000, reason: 'I think our app just broke!'});
    // 也关闭连接,但是让服务器知道这个关闭是由一些错误引起的。
    
  2. 多路复用WebSocket

    import { webSocket } from "rxjs/webSocket";
    const subject = webSocket('ws://localhost:8081');const observableA = subject.multiplex(() => ({subscribe: 'A'}), // 当服务器收到此消息,它将开始发送消息'A'…() => ({unsubscribe: 'A'}), // ...当得到这个的时候,它就会停止。message => message.type === 'A' // 如果函数返回“true”,则消息将沿流传递。如果函数返回false,则跳过。
    );const observableB = subject.multiplex( // B也是一样。() => ({subscribe: 'B'}),() => ({unsubscribe: 'B'}),message => message.type === 'B'
    );const subA = observableA.subscribe(messageForA => console.log(messageForA));
    // 此时WebSocket连接已经建立。服务器获取'{"subscribe": "A"}'消息,并开始向'A'发送消息,我们把它写在这里。const subB = observableB.subscribe(messageForB => console.log(messageForB));
    // 因为我们已经有一个连接,我们只是发送'{"subscribe": "B"}'消息到服务器。它开始向B发送信息,我们把它写在这里。subB.unsubscribe();
    // 消息‘{‘unsubscribe’:‘B’}’被发送到服务器,服务器停止发送‘B’消息。subA.unsubscribe();
    //消息‘{‘unsubscribe’:‘A’}’使服务器停止向‘A’发送消息。由于不再有根主题的订阅者,连接关闭。
    

multiplex()

  1. 创建一个可观察对象,当订阅该对象时,将subMsg函数定义的消息发送到socket上的服务器,开始对socket上的数据进行订阅。一旦数据到达,messageFilter参数将用于为结果观察选择适当的数据。当拆卸发生时,由于取消订阅、完成或错误,unsubMsg参数定义的消息将通过WebSocketSubject发送到服务器。

  2. 格式:

    multiplex(subMsg: () => any, unsubMsg: () => any, messageFilter: (value: T) => boolean)
    

    1.subMsg: 生成要发送到服务器的订阅消息的函数。这仍然由WebSocketSubject配置中的序列化器处理。(默认为JSON序列化)

    2.unsubMsg: 一个函数,用于生成要在拆卸时发送到服务器的取消订阅消息。这仍然由WebSocketSubject配置中的序列化器处理。

    3.messageFilter: 用于为输出流从服务器选择适当消息的谓词。

WebSocketSubjectConfig (websocket另一个参数)

  1. WebSocketSubjectConfig是一个普通对象,它允许我们配置webSocket。

    interface WebSocketSubjectConfig<T> {url: stringprotocol?: string | Array<string>resultSelector?: (e: MessageEvent) => Tserializer?: (value: T) => WebSocketMessagedeserializer?: (e: MessageEvent) => TopenObserver?: NextObserver<Event>closeObserver?: NextObserver<CloseEvent>closingObserver?: NextObserver<void>WebSocketCtor?: {...}binaryType?: 'blob' | 'arraybuffer'
    }
    
  2. Description: 为webSocket提供了灵活性 。 它定义了一组属性,以在socket生命周期的特定时刻提供自定义行为。当连接打开时,我们可以使用openObserver,当连接关闭时,我们可以使用closeObserver,如果我们有兴趣监听来自server: deserializer的数据交换,它允许我们在将数据传递给socket客户端之前定制数据的反序列化策略。默认情况下,反序列化器将应用JSON.parse来自服务器的每个消息。

  3. Example: deserializer (反序列化器),这个属性的默认值是JSON.parse。但对于输入数据只有两个选项,要么是文本,要么是二进制数据。我们可以应用自定义反序列化策略,或者直接跳过默认行为。

    import { webSocket } from 'rxjs/webSocket';const wsSubject = webSocket({url: 'ws://localhost:8081',
    //应用您选择的任何转换。deserializer: ({data}) => data
    });wsSubject.subscribe(console.log);// 假设我们在服务器上有这个:ws.send(“这是来自服务器的一条消息”)
    //output
    //
    // 这是来自服务器的一条消息
    
  4. 但是对于传出的消息 , serializer(序列化器)允许我们tom应用自定义的序列化策略

    import { webSocket } from 'rxjs/webSocket';const wsSubject = webSocket({url: 'ws://localhost:8081',
    //应用您选择的任何转换。serializer: msg => JSON.stringify({channel: "webDevelopment", msg: msg})
    });wsSubject.subscribe(() => subject.next("msg to the server"));// 假设我们在服务器上有这个:ws.send(“这是来自服务器的一条消息”)
    //output
    //
    // {"channel":"webDevelopment","msg":"msg to the server"}
    
  5. closeObserver允许我们在错误发生时设置自定义错误。

import { webSocket } from 'rxjs/webSocket';const wsSubject = webSocket({url: 'ws://localhost:8081',closeObserver: {next(closeEvent) {const customError = { code: 6666, reason: "Custom evil reason" }console.log(`code: ${customError.code}, reason: ${customError.reason}`);}}
});//output
// code: 6666, reason: Custom evil reason
  1. 假设我们需要在向webSocket发送/接收消息或发送连接成功的通知之前执行某种init任务,这时openObserver是有用的。
import { webSocket } from 'rxjs/webSocket';const wsSubject = webSocket({url: 'ws://localhost:8081',openObserver: {next: () => {console.log('connetion ok');}},
});//output
// connetion ok`

Properties:

  1. url : string 要连接的服务器url

  2. protocol: string | Array 连接所用的协议

  3. resultSelector: (e: MessageEvent) => T

  4. serializer : (value: T) => WebSocketMessage 在将消息发送到服务器之前,用于从传递的值创建消息的序列化程序。默认为JSON.stringify。

  5. deserializer : (e: MessageEvent) => T 用于从服务器到达socket的消息的反序列化器。默认为JSON.parse。

  6. openObserver : NextObserver 在底层web套接字上监视打开事件何时发生的观察者。

  7. closeObserver : NextObserver 当关闭事件发生在底层webSocket上时,一个观察者将进行监视
    这是来自服务器的一条消息”)
    //output
    //
    // {“channel”:“webDevelopment”,“msg”:“msg to the server”}

    
    
  8. closeObserver允许我们在错误发生时设置自定义错误。

import { webSocket } from 'rxjs/webSocket';const wsSubject = webSocket({url: 'ws://localhost:8081',closeObserver: {next(closeEvent) {const customError = { code: 6666, reason: "Custom evil reason" }console.log(`code: ${customError.code}, reason: ${customError.reason}`);}}
});//output
// code: 6666, reason: Custom evil reason
  1. 假设我们需要在向webSocket发送/接收消息或发送连接成功的通知之前执行某种init任务,这时openObserver是有用的。
import { webSocket } from 'rxjs/webSocket';const wsSubject = webSocket({url: 'ws://localhost:8081',openObserver: {next: () => {console.log('connetion ok');}},
});//output
// connetion ok`

Properties:

  1. url : string 要连接的服务器url
  2. protocol: string | Array 连接所用的协议
  3. resultSelector: (e: MessageEvent) => T
  4. serializer : (value: T) => WebSocketMessage 在将消息发送到服务器之前,用于从传递的值创建消息的序列化程序。默认为JSON.stringify。
  5. deserializer : (e: MessageEvent) => T 用于从服务器到达socket的消息的反序列化器。默认为JSON.parse。
  6. openObserver : NextObserver 在底层web套接字上监视打开事件何时发生的观察者。
  7. closeObserver : NextObserver 当关闭事件发生在底层webSocket上时,一个观察者将进行监视
  8. binaryType : ‘blob’ | ‘arraybuffer’ 设置底层WebSocket的binaryType属性。
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Angular的学习笔记

    Angular的学习笔记 ng-app""定义Angular的使用的范围。 ng-model“变量”&#xff0c;定义变量名。 ng-bind“变量”&#xff0c;绑定变量名&#xff0c;获取该变量的数据。 ng-init“变量值&#xff1b;变量值” 初始化变量的值&#xff0c;有多个变量时&#x…...

    2024/5/4 14:55:05
  2. Angular 滚动条 angular-nice-bar

    angular-nice-bar 详细介绍 一个轻量级的Angular scrollbar. Install bower install angular-nice-bar --save Usage <link rel"stylesheet" href"bower_components/angular-nice-bar/dist/css/angular-nice-bar.css" /><script src"bo…...

    2024/4/21 3:19:00
  3. angular2 html转义,Angular2:组件输入中的转义字符(Angular2: Escape character in component's input)...

    Angular2&#xff1a;组件输入中的转义字符(Angular2: Escape character in components input)我有以下代码&#xff1a;我已经尝试了许多东西( &#xff0c; \ &#xff0c; \\ &#xff0c; \ &#xff0c;......)以逃避 in&#xff0c;但我没有设法做到这一点。 我怎样才能…...

    2024/4/21 3:19:03
  4. Angular 企业实战开发 --- Angular 深入学习

    6. 管道 Pipe 管道的作用是格式化组件模板数据。 6.1 内置管道 date 日期格式化currency 货币格式化uppercase 转大写lowercase 转小写json 格式化json 数据 {{ date | date: "yyyy-MM-dd" }}6.2 自定义管道 需求&#xff1a;指定字符串不能超过规定的长度 // …...

    2024/4/26 0:54:38
  5. angular初体验

    项目初始化&#xff1a; 1 安装脚手架&#xff1a;npm i -g angular/cli 2 初始化项目&#xff1a;ng new my-app my-app 是项目名称&#xff0c;可以修改为自己喜欢的名称 3 进入目录&#xff1a;cd my-app 4 启动项目&#xff1a;ng serve --open 目录结构&#xff1a; …...

    2024/4/25 10:10:27
  6. Angular的一些坑

    经过一段时间的摸索&#xff0c;将angular的一些坑记录下 1.模块化加载机制&#xff0c;angular默认是需要加载所有的js的&#xff0c;所以会导致首页加载的js非常多&#xff0c;哪怕压缩后&#xff0c;也不容忽视&#xff0c;可以高达2M 2.ng-show和ng-if的区别&#xff0c;从…...

    2024/4/21 3:18:55
  7. AngularJS ng-repeat 遍历数组

    demo.html&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>AngularJS</title><script src"angular.min.js"></script> <!-- 引入AngularJS框架 --> &…...

    2024/5/1 10:24:55
  8. AngularJS:如何使用自定义指令来取代ng-repeat

    引言 本文主要介绍了另一种即具有与ng-repeat 一样处理大量数据的绑定的功能&#xff0c;又具有超高的性能。 对于处理小数量&#xff0c;ng-repeat是非常有用的&#xff0c;但是如果需要处理非常大的数量集&#xff0c;还是采用自定义的方法更好一些。 也别是数据大多都是静态…...

    2024/4/20 4:28:20
  9. Angular中 判断ng-repeat 是否渲染结束与checkbox翻页的状态保存

    在项目中有一个弹出框里面是数据列表&#xff0c;需要用户在多个翻页中选择&#xff08;一条或多条都可以&#xff09;。点击确认后&#xff0c;把选择的数据带回后面的页面显示。 实现该功能&#xff0c;存在的问题&#xff1a;在翻页后保存checkbox的状态。 思路&#xff1a;…...

    2024/4/21 3:18:53
  10. AngularJS:ng表达式、ng-init作用、ng数据模型、数据遍历、ng-repeat作用

    ng表达式: 1.{{表达式}} <div class"well">{{val1*val2}}</div>2.{{$tot}} 1)视图 <div class"well">{{tot()}}</div>2)控制器 app.controller(myctl,function($scope){//控制器代码逻辑范围$scope.totfunction(){strparseInt(…...

    2024/5/2 3:03:40
  11. 如何在AngularJs中使用ng-repeat进行词典?

    本文翻译自&#xff1a;How to use ng-repeat for dictionaries in AngularJs?I know that we can easily use ng-repeat for json objects or arrays like: 我知道我们可以轻松地对json对象或数组使用ng-repeat &#xff0c;如&#xff1a; <div ng-repeat"user in…...

    2024/4/27 21:18:30
  12. AngularJs中ng-options和ng-repeat的区别与用法

    一、使用ng-options的多种格式 对于数组&#xff08;集合&#xff09;&#xff1a; label for value in array select as label for value in array label group by group for value in array label disable when disable for value in array label group by group for value…...

    2024/4/21 3:18:50
  13. Angular ng-repeat 对象和数组遍历

    直接上代码 <!DOCTYPE html> <html> <head> <meta name"description" content"[Ngrepeat in obj and arr]"> <script src"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></scrip…...

    2024/4/21 3:18:49
  14. ionic 中使用 slidebox 利用angular ng-repeat 渲染后不显示问题

    在使用ionic slidebox指令做图片轮播显示的时候&#xff0c;发现一个问题&#xff0c;ng-repeat遍历生成一个个slide块的时候&#xff0c;执行完成页面是空白的。 方法&#xff1a;1ng-repeat的数据是延迟加载过来的。当 img 没有渲染结束是不能工作的&#xff0c;你这样改一下…...

    2024/4/21 3:18:48
  15. AngularJs(ng-repeat)

    ng-repeat注意项 x in records(key, value) in myObj x in records track by $id(x) 使用ng-repeat遍历一个数组&#xff0c;当数组中有重复的元素的时候&#xff0c;angularjs会报错。 错误信息&#xff1a; Error: [ngRepeat:dupes] Duplicates in a repeater are not al…...

    2024/4/25 23:24:31
  16. angularJS1.x版本$watch过多的场景分析和优化(ng-repeat+angular-ui-select)

    1、如何检查页面$watch数量 在控制台上复制下列代码执行即可看到$watch数量: function getWatchers(root) {root angular.element(root || document.documentElement);var watcherCount 0;function getElemWatchers(element) {var isolateWatchers getWatchersFromScope(ele…...

    2024/4/20 20:09:21
  17. angular.js双层(多层)ng-repeat循环

    1.双层&#xff08;多层&#xff09;ng-repeat循环 在angular写程序中我们经常会用到ng-repeat,正常情况下我们想要使用坐标时直接使用$index即可。可是当双层或是多层ng-repeat循环时应该怎么办呢? (1)双层ng-repeat循环 <div class"parent" ng-repeat…...

    2024/4/28 14:51:51
  18. 解决AngularJS中ng-repeat不更新视图的问题

    最近写AngularJS项目中&#xff0c;遇到一个问题&#xff0c;先对数组进行赋值&#xff0c;ng--repeat正常工作&#xff0c;然后对数组进行修改&#xff0c;ng-repeat似乎没有工作&#xff0c;视图没有更新。 原因是ng-repeat会已默认值排序&#xff0c;由于我的数组中的元素有…...

    2024/4/20 20:09:23
  19. angular1坑之ng-repeat

    功能&#xff1a;有一个页面&#xff0c;输入查询条件&#xff0c;点击查询按钮后从后台返回数据并显示在页面的表格中&#xff0c;并在页面滚动到最下面时实现下拉加载。 下拉加载什么的前面的博客已经讲过了&#xff0c;这里不再讲了&#xff0c;这里主要是记录bug问题&…...

    2024/4/25 13:23:50
  20. AngularJs ng-repeat性能问题

    AngularJs 的 ng-repeat 让我们非常方便的遍历数组生成 Dom 元素&#xff0c;但是使用不当也会有性能问题。 在项目中我们使用 ng-repeat 加载完一个列表后&#xff0c;如果再次请求数据&#xff0c;然后过滤列表&#xff0c;代码可能会这么写&#xff1a; <div ng-control…...

    2024/4/21 3:18:45

最新文章

  1. Tomcat启动闪退怎么解决(文末附终极解决方案)

    AI是这么告诉我的 Tomcat启动时出现闪退问题可能由多种原因引起&#xff0c;以下是解决此类问题的一些通用方法&#xff1a; 检查环境变量&#xff1a; 确保已经正确设置了JAVA_HOME和JRE_HOME环境变量&#xff0c;并指向正确的Java安装路径。将Java的bin目录添加到系统的PATH…...

    2024/5/4 21:43:11
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. docker进行jenkins接口自动化测试持续集成实战

    文章目录 一、接口功能自动化测试项目源码讲解二、接口功能自动化测试运行环境配置1、下载jdk&#xff0c;maven&#xff0c;git&#xff0c;allure并配置对应的环境变量2、使用docker安装jenkins3、配置接口测试的运行时环境选择对应节点4、jenkins下载插件5、jenkins配置环境…...

    2024/5/4 14:09:02
  4. Linux的软链接和硬链接

    1、软链接 概念&#xff1a;给文件创建一个快捷方式&#xff0c;依赖原文件&#xff0c;和普通文件没有区别。 特性&#xff1a; 可以给存在的文件或目录创建软链接可以给不存在的文件或目录创建软链接可以跨文件系统创建软链接删除软链接不影响原文件、删除原文件会导致软链…...

    2024/4/30 17:33:33
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/1 17:30:59
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/2 16:16:39
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/3 23:10:03
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/4 18:20:48
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/4 2:59:34
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/2 9:07:46
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57