Compare commits
15 Commits
Author | SHA1 | Date |
---|---|---|
|
2b5bee5a49 | |
|
4e70ada409 | |
|
0a03162b96 | |
|
80c0236f5e | |
|
b0153ebb86 | |
|
f6d361eb84 | |
|
ef2309f0f1 | |
|
644139b42c | |
|
64c40998b9 | |
|
5f12d19dbc | |
|
973524fc7e | |
|
8168d2023c | |
|
34ef92e03f | |
|
299aeb7d14 | |
|
01299fd1e9 |
|
@ -1,8 +1,8 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { MessagesComponent } from "./messages/messages.component";
|
import { MessagesComponent } from "./messages/messages.component";
|
||||||
import { RouterLink, RouterOutlet } from '@angular/router';
|
import { RouterLink, RouterOutlet } from '@angular/router';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
|
|
|
@ -11,6 +11,6 @@ export const appConfig: ApplicationConfig = {
|
||||||
importProvidersFrom(InMemoryWebApiModule.forRoot(InMemoryDataService, { delay: 150 })),
|
importProvidersFrom(InMemoryWebApiModule.forRoot(InMemoryDataService, { delay: 150 })),
|
||||||
provideBrowserGlobalErrorListeners(),
|
provideBrowserGlobalErrorListeners(),
|
||||||
provideZonelessChangeDetection(),
|
provideZonelessChangeDetection(),
|
||||||
provideRouter(routes)
|
provideRouter(routes),
|
||||||
]
|
]
|
||||||
};
|
};
|
|
@ -7,7 +7,7 @@ export const routes: Routes = [
|
||||||
|
|
||||||
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' }, //путь по умолчанию
|
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' }, //путь по умолчанию
|
||||||
{ path: 'heroes', component: HeroesComponent },
|
{ path: 'heroes', component: HeroesComponent },
|
||||||
{ path: 'dashboard', component: DashboardComponent },
|
{ path: 'dashboard', component: DashboardComponent },
|
||||||
{ path: 'detail/:id', component: HeroDetailComponent },
|
{ path: 'detail/:id', component: HeroDetailComponent },
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -5,3 +5,5 @@
|
||||||
{{hero.name}}
|
{{hero.name}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<app-hero-search></app-hero-search>
|
|
@ -3,10 +3,11 @@ import { Component, OnInit } from '@angular/core';
|
||||||
import { Hero } from '../hero';
|
import { Hero } from '../hero';
|
||||||
import { HeroService } from '../hero.service';
|
import { HeroService } from '../hero.service';
|
||||||
import { RouterLink } from '@angular/router';
|
import { RouterLink } from '@angular/router';
|
||||||
|
import { HeroSearchComponent } from "../hero-search/hero-search.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dashboard.component',
|
selector: 'app-dashboard.component',
|
||||||
imports: [CommonModule, RouterLink],
|
imports: [CommonModule, RouterLink, HeroSearchComponent],
|
||||||
templateUrl: './dashboard.component.html',
|
templateUrl: './dashboard.component.html',
|
||||||
styleUrl: './dashboard.component.css'
|
styleUrl: './dashboard.component.css'
|
||||||
})
|
})
|
||||||
|
@ -17,6 +18,7 @@ export class DashboardComponent implements OnInit {
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.getHeroes();
|
this.getHeroes();
|
||||||
|
console.log(this.heroes)
|
||||||
}
|
}
|
||||||
|
|
||||||
getHeroes(): void {
|
getHeroes(): void {
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* HeroSearch private styles */
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.search-result {
|
||||||
|
margin-top: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<div id="search-component">
|
||||||
|
<label for="search-box">Hero Search</label>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!--a> тест ветки </a-->
|
|
@ -0,0 +1,44 @@
|
||||||
|
//test fork
|
||||||
|
|
||||||
|
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';
|
||||||
|
import { CommonModule } from '@angular/common'; //добавил библиотеку
|
||||||
|
import { RouterLink } from '@angular/router'; //добавил библиотеку
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-hero-search',
|
||||||
|
imports: [CommonModule, RouterLink],
|
||||||
|
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) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.heroes$ = this.searchTerms.pipe(
|
||||||
|
// wait 300ms after each keystroke before considering the term
|
||||||
|
debounceTime(300),
|
||||||
|
|
||||||
|
// ignore new term if same as previous term
|
||||||
|
distinctUntilChanged(),
|
||||||
|
|
||||||
|
// switch to new search observable each time the term changes
|
||||||
|
switchMap((term: string) => this.heroService.searchHeroes(term)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push a search term into the observable stream.
|
||||||
|
search(term: string): void {
|
||||||
|
this.searchTerms.next(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,17 +1,16 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
import { HEROES } from './mock-heroes';
|
|
||||||
import { Observable, of } from 'rxjs';
|
|
||||||
import { MessageService } from './message.service';
|
import { MessageService } from './message.service';
|
||||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
import { catchError, map, tap } from 'rxjs/operators';
|
import { catchError, map, tap } from 'rxjs/operators';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
|
|
||||||
export class HeroService {
|
export class HeroService {
|
||||||
|
|
||||||
private heroesUrl = 'api/heroes'; // URL to web api
|
private heroesUrl = 'api/heroes'; // URL to web api
|
||||||
|
|
||||||
httpOptions = {
|
httpOptions = {
|
||||||
|
@ -49,6 +48,39 @@ export class HeroService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 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(id: number): Observable<Hero> {
|
||||||
|
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'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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', []))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Http operation that failed.
|
* Handle Http operation that failed.
|
||||||
* Let the app continue.
|
* Let the app continue.
|
||||||
|
@ -74,6 +106,5 @@ export class HeroService {
|
||||||
private log(message: string) {
|
private log(message: string) {
|
||||||
this.messageService.add(`HeroService: ${message}`);
|
this.messageService.add(`HeroService: ${message}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -5,6 +5,15 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 15em;
|
width: 15em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: .5rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
.heroes li {
|
.heroes li {
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -51,4 +60,31 @@
|
||||||
text-align: right;
|
text-align: right;
|
||||||
margin-right: .8em;
|
margin-right: .8em;
|
||||||
border-radius: 4px 0 0 4px;
|
border-radius: 4px 0 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-button {
|
||||||
|
padding: .5rem 1.5rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-button:hover {
|
||||||
|
color: white;
|
||||||
|
background-color: #42545C;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.delete {
|
||||||
|
position: absolute;
|
||||||
|
left: 210px;
|
||||||
|
top: 5px;
|
||||||
|
background-color: white;
|
||||||
|
color: #525252;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1px 10px 3px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.delete:hover {
|
||||||
|
background-color: #525252;
|
||||||
|
color: white;
|
||||||
}
|
}
|
|
@ -4,5 +4,17 @@
|
||||||
<a routerLink="/detail/{{hero.id}}">
|
<a routerLink="/detail/{{hero.id}}">
|
||||||
<span class="badge">{{hero.id}}</span> {{hero.name}}
|
<span class="badge">{{hero.id}}</span> {{hero.name}}
|
||||||
</a>
|
</a>
|
||||||
|
<button type="button" class="delete" title="delete hero"
|
||||||
|
(click)="delete(hero)">x</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="new-hero">Hero name: </label>
|
||||||
|
<input id="new-hero" #heroName />
|
||||||
|
|
||||||
|
<!-- (click) passes input value to add() and then clears the input -->
|
||||||
|
<button type="button" class="add-button" (click)="add(heroName.value); heroName.value=''">
|
||||||
|
Add hero
|
||||||
|
</button>
|
||||||
|
</div>
|
|
@ -3,7 +3,6 @@ import { Hero } from '../hero';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { HeroService } from '../hero.service'; //новая библиотека добавлена вместо HEROES
|
import { HeroService } from '../hero.service'; //новая библиотека добавлена вместо HEROES
|
||||||
import { HeroDetailComponent } from '../hero-detail/hero-detail.component';
|
|
||||||
import { MessageService } from '../message.service';
|
import { MessageService } from '../message.service';
|
||||||
import { RouterLink } from '@angular/router';
|
import { RouterLink } from '@angular/router';
|
||||||
|
|
||||||
|
@ -26,10 +25,23 @@ export class HeroesComponent implements OnInit {
|
||||||
this.getHeroes();
|
this.getHeroes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
getHeroes(): void {
|
getHeroes(): void {
|
||||||
this.heroService.getHeroes()
|
this.heroService.getHeroes()
|
||||||
.subscribe(heroes => this.heroes = heroes);
|
.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.id).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -29,4 +29,4 @@ export class InMemoryDataService implements InMemoryDbService {
|
||||||
genId(heroes: Hero[]): number {
|
genId(heroes: Hero[]): number {
|
||||||
return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
|
return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue