parent
596dfb88a6
commit
edd26a2b79
|
@ -53,7 +53,7 @@ describe('Tutorial part 6', () => {
|
|||
|
||||
appDashboardHref: navElts.get(0),
|
||||
appDashboard: element(by.css('app-root app-dashboard')),
|
||||
topHeroes: element.all(by.css('app-root app-dashboard > div h4')),
|
||||
topHeroes: element.all(by.css('app-root app-dashboard > div a')),
|
||||
|
||||
appHeroesHref: navElts.get(1),
|
||||
appHeroes: element(by.css('app-root app-heroes')),
|
||||
|
@ -176,7 +176,7 @@ describe('Tutorial part 6', () => {
|
|||
const numHeroes = heroesBefore.length;
|
||||
|
||||
await element(by.css('input')).sendKeys(addedHeroName);
|
||||
await element(by.buttonText('add')).click();
|
||||
await element(by.buttonText('Add hero')).click();
|
||||
|
||||
const page = getPageElts();
|
||||
const heroesAfter = await toHeroArray(page.allHeroes);
|
||||
|
@ -195,18 +195,18 @@ describe('Tutorial part 6', () => {
|
|||
// Inherited styles from styles.css
|
||||
expect(await button.getCssValue('font-family')).toBe('Arial, sans-serif');
|
||||
expect(await button.getCssValue('border')).toContain('none');
|
||||
expect(await button.getCssValue('padding')).toBe('5px 10px');
|
||||
expect(await button.getCssValue('padding')).toBe('1px 10px 3px');
|
||||
expect(await button.getCssValue('border-radius')).toBe('4px');
|
||||
// Styles defined in heroes.component.css
|
||||
expect(await button.getCssValue('left')).toBe('194px');
|
||||
expect(await button.getCssValue('top')).toBe('-32px');
|
||||
expect(await button.getCssValue('left')).toBe('210px');
|
||||
expect(await button.getCssValue('top')).toBe('5px');
|
||||
}
|
||||
|
||||
const addButton = element(by.buttonText('add'));
|
||||
const addButton = element(by.buttonText('Add hero'));
|
||||
// Inherited styles from styles.css
|
||||
expect(await addButton.getCssValue('font-family')).toBe('Arial, sans-serif');
|
||||
expect(await addButton.getCssValue('border')).toContain('none');
|
||||
expect(await addButton.getCssValue('padding')).toBe('5px 10px');
|
||||
expect(await addButton.getCssValue('padding')).toBe('8px 24px');
|
||||
expect(await addButton.getCssValue('border-radius')).toBe('4px');
|
||||
});
|
||||
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
/* AppComponent's private CSS styles */
|
||||
h1 {
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
nav a {
|
||||
padding: 5px 10px;
|
||||
padding: 1rem;
|
||||
text-decoration: none;
|
||||
margin-top: 10px;
|
||||
display: inline-block;
|
||||
background-color: #eee;
|
||||
background-color: #e8e8e8;
|
||||
color: #3d3d3d;
|
||||
border-radius: 4px;
|
||||
}
|
||||
nav a:visited, a:link {
|
||||
color: #334953;
|
||||
}
|
||||
nav a:hover {
|
||||
color: #039be5;
|
||||
background-color: #CFD8DC;
|
||||
color: white;
|
||||
background-color: #42545C;
|
||||
}
|
||||
nav a.active {
|
||||
color: #039be5;
|
||||
background-color: black;
|
||||
}
|
||||
|
|
|
@ -1,61 +1,54 @@
|
|||
/* DashboardComponent's private CSS styles */
|
||||
[class*='col-'] {
|
||||
float: left;
|
||||
padding-right: 20px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
[class*='col-']:last-of-type {
|
||||
padding-right: 0;
|
||||
|
||||
.heroes-menu {
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
max-width: 1000px;
|
||||
|
||||
/* flexbox */
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
align-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
*, *::after, *::before {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
h3 {
|
||||
text-align: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
h4 {
|
||||
position: relative;
|
||||
}
|
||||
.grid {
|
||||
margin: 0;
|
||||
}
|
||||
.col-1-4 {
|
||||
width: 25%;
|
||||
}
|
||||
.module {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #eee;
|
||||
max-height: 120px;
|
||||
min-width: 120px;
|
||||
background-color: #3f525c;
|
||||
border-radius: 2px;
|
||||
padding: 1rem;
|
||||
font-size: 1.2rem;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
min-width: 70px;
|
||||
margin: .5rem auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
/* flexbox */
|
||||
order: 0;
|
||||
flex: 0 1 auto;
|
||||
align-self: auto;
|
||||
}
|
||||
.module:hover {
|
||||
background-color: #EEE;
|
||||
cursor: pointer;
|
||||
color: #607d8b;
|
||||
}
|
||||
.grid-pad {
|
||||
padding: 10px 0;
|
||||
}
|
||||
.grid-pad > [class*='col-']:last-of-type {
|
||||
padding-right: 20px;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.module {
|
||||
font-size: 10px;
|
||||
max-height: 75px; }
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.grid {
|
||||
margin: 0;
|
||||
}
|
||||
.module {
|
||||
min-width: 60px;
|
||||
|
||||
@media (min-width: 600px) {
|
||||
a {
|
||||
width: 18%;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: black;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
<h3>Top Heroes</h3>
|
||||
<div class="grid grid-pad">
|
||||
<a *ngFor="let hero of heroes" class="col-1-4"
|
||||
<h2>Top Heroes</h2>
|
||||
<div class="heroes-menu">
|
||||
<a *ngFor="let hero of heroes"
|
||||
routerLink="/detail/{{hero.id}}">
|
||||
<div class="module hero">
|
||||
<h4>{{hero.name}}</h4>
|
||||
</div>
|
||||
{{hero.name}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ describe('DashboardComponent', () => {
|
|||
});
|
||||
|
||||
it('should display "Top Heroes" as headline', () => {
|
||||
expect(fixture.nativeElement.querySelector('h3').textContent).toEqual('Top Heroes');
|
||||
expect(fixture.nativeElement.querySelector('h2').textContent).toEqual('Top Heroes');
|
||||
});
|
||||
|
||||
it('should call heroService', waitForAsync(() => {
|
||||
|
|
|
@ -1,24 +1,19 @@
|
|||
/* HeroDetailComponent's private CSS styles */
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 3em;
|
||||
margin: .5em 0;
|
||||
color: #607D8B;
|
||||
color: #435960;
|
||||
font-weight: bold;
|
||||
}
|
||||
input {
|
||||
height: 2em;
|
||||
font-size: 1em;
|
||||
padding-left: .4em;
|
||||
padding: .5rem;
|
||||
}
|
||||
button {
|
||||
margin-top: 20px;
|
||||
font-family: Arial, sans-serif;
|
||||
margin-right: .5rem;
|
||||
background-color: #eee;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #cfd8dc;
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name"/>
|
||||
</label>
|
||||
<label for="hero-name">Hero name: </label>
|
||||
<input id="hero-name" [(ngModel)]="hero.name" placeholder="Hero name"/>
|
||||
</div>
|
||||
<button (click)="goBack()">go back</button>
|
||||
<!-- #docregion save -->
|
||||
|
|
|
@ -1,37 +1,45 @@
|
|||
/* HeroSearch private styles */
|
||||
.search-result li {
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: .5rem;
|
||||
|
||||
}
|
||||
input {
|
||||
padding: .5rem;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: #336699 auto 1px;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
}
|
||||
.search-result li a {
|
||||
border-bottom: 1px solid gray;
|
||||
border-left: 1px solid gray;
|
||||
border-right: 1px solid gray;
|
||||
width: 195px;
|
||||
height: 16px;
|
||||
padding: 5px;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.search-result li:hover {
|
||||
background-color: #607D8B;
|
||||
}
|
||||
|
||||
.search-result li a {
|
||||
color: #888;
|
||||
display: block;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
padding: .5rem;
|
||||
box-sizing: border-box;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.search-result li a:hover {
|
||||
background-color: #435A60;
|
||||
color: white;
|
||||
}
|
||||
.search-result li a:active {
|
||||
color: white;
|
||||
}
|
||||
#search-box {
|
||||
width: 200px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
|
||||
ul.search-result {
|
||||
margin-top: 0;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<div id="search-component">
|
||||
<h4><label for="search-box">Hero Search</label></h4>
|
||||
|
||||
<label for="search-box">Hero Search</label>
|
||||
<!-- #docregion input -->
|
||||
<input #searchBox id="search-box" (input)="search(searchBox.value)" />
|
||||
<!-- #enddocregion input -->
|
||||
|
|
|
@ -5,32 +5,44 @@
|
|||
padding: 0;
|
||||
width: 15em;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: .5rem;
|
||||
margin: 1rem 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.heroes li {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
background-color: #EEE;
|
||||
margin: .5em;
|
||||
padding: .3em 0;
|
||||
height: 1.6em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.heroes li:hover {
|
||||
color: #607D8B;
|
||||
background-color: #DDD;
|
||||
left: .1em;
|
||||
}
|
||||
|
||||
.heroes a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
background-color: #EEE;
|
||||
margin: .5em;
|
||||
padding: .3em 0;
|
||||
height: 1.6em;
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
width: 250px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.heroes a:hover {
|
||||
color: #607D8B;
|
||||
color: #2c3a41;
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.heroes a:active {
|
||||
background-color: #525252;
|
||||
color: #fafafa;
|
||||
}
|
||||
|
||||
.heroes .badge {
|
||||
|
@ -50,23 +62,28 @@
|
|||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #eee;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-family: Arial, sans-serif;
|
||||
.add-button {
|
||||
padding: .5rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #cfd8dc;
|
||||
.add-button:hover {
|
||||
color: white;
|
||||
background-color: #42545C;
|
||||
}
|
||||
|
||||
button.delete {
|
||||
position: relative;
|
||||
left: 194px;
|
||||
top: -32px;
|
||||
background-color: gray !important;
|
||||
position: absolute;
|
||||
left: 210px;
|
||||
top: 5px;
|
||||
background-color: white;
|
||||
color: #525252;
|
||||
font-size: 1.1rem;
|
||||
padding: 1px 10px 3px 10px;
|
||||
}
|
||||
|
||||
button.delete:hover {
|
||||
background-color: #525252;
|
||||
color: white;
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
<!-- #docregion add -->
|
||||
<div>
|
||||
<label>Hero name:
|
||||
<input #heroName />
|
||||
</label>
|
||||
<label id="new-hero">Hero name: </label>
|
||||
<input for="new-hero" #heroName />
|
||||
|
||||
<!-- (click) passes input value to add() and then clears the input -->
|
||||
<button (click)="add(heroName.value); heroName.value=''">
|
||||
add
|
||||
<button class="add-button" (click)="add(heroName.value); heroName.value=''">
|
||||
Add hero
|
||||
</button>
|
||||
</div>
|
||||
<!-- #enddocregion add -->
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
/* MessagesComponent's private CSS styles */
|
||||
h2 {
|
||||
color: red;
|
||||
color: #A80000;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
button.clear {
|
||||
font-family: Arial, sans-serif;
|
||||
.clear {
|
||||
color: #333;
|
||||
background-color: #eee;
|
||||
margin-bottom: 12px;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #cfd8dc;
|
||||
.clear:hover {
|
||||
color: #fff;
|
||||
background-color: #42545C;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<h2>Messages</h2>
|
||||
<button class="clear"
|
||||
(click)="messageService.clear()">clear</button>
|
||||
(click)="messageService.clear()">Clear messages</button>
|
||||
<div *ngFor='let message of messageService.messages'> {{message}} </div>
|
||||
|
||||
</div>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -9,7 +9,7 @@ Angular's `HttpClient`.
|
|||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For the sample app that this page describes, see the <live-example></live-example>.
|
||||
For the sample application that this page describes, see the <live-example></live-example>.
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -17,7 +17,7 @@ Angular's `HttpClient`.
|
|||
|
||||
`HttpClient` is Angular's mechanism for communicating with a remote server over HTTP.
|
||||
|
||||
Make `HttpClient` available everywhere in the app in two steps. First, add it to the root `AppModule` by importing it:
|
||||
Make `HttpClient` available everywhere in the application in two steps. First, add it to the root `AppModule` by importing it:
|
||||
|
||||
<code-example path="toh-pt6/src/app/app.module.ts" region="import-http-client" header="src/app/app.module.ts (HttpClientModule import)">
|
||||
</code-example>
|
||||
|
@ -33,7 +33,7 @@ Next, still in the `AppModule`, add `HttpClient` to the `imports` array:
|
|||
This tutorial sample mimics communication with a remote data server by using the
|
||||
[In-memory Web API](https://github.com/angular/angular/tree/master/packages/misc/angular-in-memory-web-api "In-memory Web API") module.
|
||||
|
||||
After installing the module, the app will make requests to and receive responses from the `HttpClient`
|
||||
After installing the module, the application will make requests to and receive responses from the `HttpClient`
|
||||
without knowing that the *In-memory Web API* is intercepting those requests,
|
||||
applying them to an in-memory data store, and returning simulated responses.
|
||||
|
||||
|
@ -127,7 +127,7 @@ Convert that method to use `HttpClient` as follows:
|
|||
Refresh the browser. The hero data should successfully load from the
|
||||
mock server.
|
||||
|
||||
You've swapped `of()` for `http.get()` and the app keeps working without any other changes
|
||||
You've swapped `of()` for `http.get()` and the application keeps working without any other changes
|
||||
because both functions return an `Observable<Hero[]>`.
|
||||
|
||||
### `HttpClient` methods return one value
|
||||
|
@ -197,10 +197,10 @@ has configured with both the name of the operation that failed and a safe return
|
|||
</code-example>
|
||||
|
||||
After reporting the error to the console, the handler constructs
|
||||
a user friendly message and returns a safe value to the app so the app can keep working.
|
||||
a user friendly message and returns a safe value to the application so the application can keep working.
|
||||
|
||||
Because each service method returns a different kind of `Observable` result,
|
||||
`handleError()` takes a type parameter so it can return the safe value as the type that the app expects.
|
||||
`handleError()` takes a type parameter so it can return the safe value as the type that the application expects.
|
||||
|
||||
### Tap into the Observable
|
||||
|
||||
|
@ -283,7 +283,7 @@ The hero now appears in the list with the changed name.
|
|||
|
||||
## Add a new hero
|
||||
|
||||
To add a hero, this app only needs the hero's name. You can use an `<input>`
|
||||
To add a hero, this application only needs the hero's name. You can use an `<input>`
|
||||
element paired with an add button.
|
||||
|
||||
Insert the following into the `HeroesComponent` template, just after
|
||||
|
@ -506,7 +506,7 @@ It cancels and discards previous search observables, returning only the latest s
|
|||
|
||||
Note that canceling a previous `searchHeroes()` Observable
|
||||
doesn't actually abort a pending HTTP request.
|
||||
Unwanted results are simply discarded before they reach your application code.
|
||||
Unwanted results are discarded before they reach your application code.
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -515,11 +515,11 @@ That's the job of the [`AsyncPipe`](#asyncpipe) in the template.
|
|||
|
||||
#### Try it
|
||||
|
||||
Run the app again. In the *Dashboard*, enter some text in the search box.
|
||||
Run the application again. In the *Dashboard*, enter some text in the search box.
|
||||
If you enter characters that match any existing hero names, you'll see something like this.
|
||||
|
||||
<div class="lightbox">
|
||||
<img src='generated/images/guide/toh/toh-hero-search.png' alt="Hero Search Component">
|
||||
<img src='generated/images/guide/toh/toh-hero-search.gif' alt="Hero Search field with the letters 'm' and 'a' along with four search results that match the query displayed in a list beneath the search input">
|
||||
</div>
|
||||
|
||||
## Final code review
|
||||
|
|
Loading…
Reference in New Issue