Angular - pytanie o komunikację component->back-end. Czy dobrze to robie?

0

Witam,
Zaczynam przygodę z angular2 i mam małe pytanie czy myślę w dobym kierunku czy jednak jest to za duży mix i pójść prostsza drogą.
(( obsługę wyjątków i błędów ominąłem aby skrócić kod))
Chciałbym wiedziec czy tak to się robi czy za dużo kombinuję i lepiej to skrócić że np. component będzie miał injected repo i dostęp do funkcji cruda.
Programuję hobbistycznie więc proszę o wyrozumiałość :D (chciałbym pracować nawet jako junior ale boję się że mam za małą wiedzę)
Back-end to api które piszę w Asp.net core2

Na razie napisałem tak: (Na przykładzie pobierania wszystkich użytkowników z back-endu)
Mam klase abstrakcyjną:

Repository<T extends MyItems> 

z funkcjami CRUD np:

getAll() : Observable<T[]>
    {
        return this.http.get(
            `${this.url}/${this.endpart}`, this.auth.getHeader()).pipe(map((data: any) => data.json() as T[]));
    }

Od niej tworzę klasę repo dla np modelu User: (takie repo mogę powiększyć o funkcje wymagane w konkretnym przypadku/modelu)

export class UsersRepository extends RepositoryService<User>

Następnie mam klasę service która ma dostęp do repo. np. Users.service.ts (tutaj dane odebrane z repo (User[]) będą mapowane na UserDto[] i odbierane w component:

getAll() {
        return new Promise(resolve=> {
            this.usersRepo.getAll().subscribe(
                data => {
                    var userDtos: UserDto[] = [];
                    data.forEach(
                        (user) => 
                        {
                            // mapowanie będzie docelowo lepiej rozwiązane ale narazie nie posiadam wiedzy żeby to zrobić
                            userDtos.push(<UserDto> { id: user.id, username : user.username, email : user.email }); 
                    })
                    resolve(userDtos);
                })
        })
    }

Dane odbierane i przetwarzane są przez component i dalej przekazane do template: (tutaj przykład jakby to miało wyglądać):

getAllUsers()
{
    var userDtos: UserDto[] = [];

    this.usersService.getAllTmp().then(
    (data:UserDto[]) => {
        if(data.length > 0) {
            data.forEach(
                (user) => 
                {
                    console.log("name: " + user);
                })
        }})
}

modele: (User.model.ts oraz UserDto.model.ts) skrócone do minimum.

//User
export class User extends MyItems
{
    username: string;
    email: string;
    password: string; // never stored used when request login/register
    token: string;
}
//UserDto
export class UserDto extends MyItems
{
    id: number;
    username: string;
    // UserDetails here
}

Myślałem żeby klasy UserService itp również zrobić jako generic<T> z dodatkową serializacją modeli z Model -> Dto.
Tylko nie wiem czy nie za duzo kombinuje "na około"...

Z góry dziękuję za podpowiedzi i nakierowanie w odpowiednim kierunku :D

0

Jak dla mnie za dużo kombinujesz. Takie rzeczy są ok na backendzie gdzie zazwyczaj dane z UI =/= dane z bazy.
W projekcie mamy po prostu coś takiego (przykład):

@Injectable((
export service UserService {
   getUser = (): Observable<UserModel> => {
      return this.http.get('/api/user/');
   }

   getUserDetails = (id: number): Observable<UserDetailsModel> => {
      return this.http.get(`/api/user/${id}`);
   }
}

Na szczęście nie zdarzyło się jeszcze żebym musiał tutaj coś mapować na frontendzie. Takie mapowania jak dla mnie wprowadzają dodatkowy zamęt - coś się nie zgadza przy np zapisywaniu zmian i trzeba szukać widok -> mapper -> "bóg wie co jeszcze" -> api zamiast sprawdzić "z api przyszło, że pole o nazwie Login jest złe to sprawdzę pole login w widoku".
Mapować to ew możesz jak podpinasz się pod zewnętrzne api/inny system i tam mają straszny syf albo dane z innego systemu później musisz zapisać do jeszcze innego.

        return this.http.get(
            `${this.url}/${this.endpart}`, this.auth.getHeader()).pipe(map((data: any) => data.json() as T[]));

Angularowy HttpClient domyślnie mapuje dane na json więc to jest bezsensu.

getAll() {
        return new Promise(resolve=> {
            this.usersRepo.getAll().subscribe(
                data => {
                    var userDtos: UserDto[] = [];
                    data.forEach(
                        (user) => 
                        {
                            // mapowanie będzie docelowo lepiej rozwiązane ale narazie nie posiadam wiedzy żeby to zrobić
                            userDtos.push(<UserDto> { id: user.id, username : user.username, email : user.email }); 
                    })
                    resolve(userDtos);
                })
        })
    }

Lepiej nie mieszać promisów z observable dopóki nie jest do niezbędne.

//User
export class User extends MyItems
{
    username: string;
    email: string;
    password: string; // never stored used when request login/register
    token: string;
}
//UserDto
export class UserDto extends MyItems
{
    id: number;
    username: string;
    // UserDetails here
}

Modele powinny być raczej interfejsami i odzwierciedlać to co dostajesz z API.

I taka mała porada jeszcze: możesz zrobić sobie swoją implementację http serwisu tzn serwis który będzie miał takie same metody jak HttpClient (get, put, post, delete, ...). Będziesz wtedy wywoływał ten serwis bez dodawania zawsze

this.auth.getHeader()

A jak kiedyś będzie trzeba na sztywno coś dodawać np header jakiś to zamiast w 100+ miejscach dodasz w jednym serwisie.

0

No tak myślałem że za dużo kombinowania :D Wystarczy że już mam tak na back-endzie.

Czy jest jakis "duuuży" minus mieszania promise z observable? Musiałem tak użyć ponieważ wszystko działa async. i w component musiałem czekać na "powrót" danych.
Skrócę to sobie przy najbliższej okazji.
Implementacja swojego http to tez dobry pomysł. W header przesyłam token usera do api.

Czyli to jest zbedne?

map((data: any) => data.json() as T[]))

Dzieki za rady.

0

Czy jest jakis "duuuży" minus mieszania promise z observable? Musiałem tak użyć ponieważ wszystko działa async. i w component musiałem czekać na "powrót" danych.

To zrób coś takiego:

getAll() {
        return Observable.create(observer=> {
            this.usersRepo.getAll().subscribe(
                data => {
                    var userDtos: UserDto[] = [];
                    data.forEach(
                        (user) => 
                        {
                            // mapowanie będzie docelowo lepiej rozwiązane ale narazie nie posiadam wiedzy żeby to zrobić
                            userDtos.push(<UserDto> { id: user.id, username : user.username, email : user.email }); 
                    })
                    observer.next(userDtos);
                })
        })
    }

Generalnie nic się nie stanie jak używasz obydwu wersji ale wprowadza to niepotrzebne zamieszanie. Wszystko co możesz zrobić z Promise zrobisz także w rxjs.

Czyli to jest zbedne?

Tak

0

Czyli to będzie jakby "observable do observable"?
Dzięki bardzo, to mi się akurat przyda, dopiero zacząłem angular i na razie rozkminiam co jak robić.

0

Observable który zostanie rozwiązany jak ten inny się zakończy.

0

Tak, niewiedziałem jak to ładnie opisać.
Wielkie dzięki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1