Моя цель - вызвать строго типизированные данные профиля пользователя из firestore firebase и заполнить шаблон компонента профиля пользователя, используя угловую оболочку приложения, чтобы предварительно заполнить область просмотра, пока данные БД извлекаются и разрешаются с использованием метода углового маршрутизатора resol ()..
Проблема, с которой я столкнулся, заключается в том, что я не могу разобрать данные шаблона в конце процесса, потому что форма объекта данных отличается от ожидаемой.
Я ожидаю:
{
displayName: 'some name',
photoUrl: 'some url',
uid: 'some user id'
...
}
Но я получаю:
{name: 'projects/projectName/database/(default)/documents/users/uid',
fields:
displayName: {stringValue: 'some name'}
photoUrl: {stringValue: 'some url'}
uid: {stringValue: 'some user id'}
...
}
Я использую модель данных () для заполнения БД, когда пользовательрегистрирует и получает данные, когда пользователь получает доступ к своему профилю.Я использую «службу пользователя» для получения данных из базы данных и заполнения функции getProfileDataWithShell, которая передаст dataObservable обработчику маршрутов и поставщику оболочки перед тем, как закончить пост-обработку в компоненте user-profile.page.В компоненте user-profile.page данные принимаются в виде активированного маршрута (от преобразователя) и проходят через ряд условий для обработки содержимого оболочки на основе обещаний и данных базы данных на основе наблюдаемых.Наконец, результирующая наблюдаемая подписывается и возвращает объект профиля типа UserProfileModel.
Resolver Route и поставщик оболочки. Определитель маршрутов принимает dataObservable и передает его в метод Angular Resolve ().Я не думаю, что здесь происходит что-то еще.
Поставщик оболочки использует объект поведения, который получает shellModel из обещания, которое содержит все предварительно заполненные данные и передается в представление компонента черезкеш оболочки (app-shell).Субъект поведения ожидает, а затем получает dataObservable, который используется для заполнения остальной части шаблона по мере завершения наблюдаемой.ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ ДЛЯ ЦЕЛЕЙ DEV. НАСТРОЙКА ЗАДЕРЖКИ 2 СЕКУНДЫ ДЛЯ МОДЕЛИРОВАНИЯ ЗАДЕРЖКИ СЕТИ.
Если бы все, что мне нужно было сделать, это создать другую переменную и повторно проанализировать ее в компоненте профиля пользователя, я бы почти былсчастлив в этот момент.Но поскольку данные строго типизированы с использованием типа, все мои параметры синтаксического анализа тесно связаны с тем, что находится в этой модели.ОДНАКО, когда я наблюдаю результирующий объект (в инструментах разработчика Chrome), он имеет тип в разделе «Но я получаю» выше.Я могу видеть этот тип в течение всего процесса, начинающегося с предметного объекта поведения.
Это приложение предназначено для приложений Ionic 4, использующих Angular 7, Firebase 6 и RXJS 6, на вершине Cordova Android.На данном этапе платформа Android не в курсе, что означает, что вышеупомянутые вещи влияют на сборку Android и наоборот.Любая помощь будет принята с благодарностью.
------------------ Модель профиля пользователя -----------------------
```export class UserProfileModel {
uid: string;
email: string;
photoUrl?: string;
displayName?: string;
membership?: string;
job?: string;
likes?: string;
followers?: string;
following?: string;
about?: string;
constructor(readonly isShell: boolean) { }
}
```
------------------- user.service.ts ------------------------
```
@Injectable()
export class UserService {
private _profileDataWithShellCache: ShellProvider<UserProfileModel>;
constructor(private http: HttpClient, private afAuth: AngularFireAuth) { }
public getProfileDataWithShell(): Observable<UserProfileModel> {
this.userId = this.afAuth.auth.currentUser.uid;
// Use cache if we have it.
if (!this._profileDataWithShellCache) {
// Initialize the model specifying that it is a shell model
const shellModel: UserProfileModel = new UserProfileModel(true);
const dataObservable = this.http.get<UserProfileModel>(this.baseFsAPIUrl + this.userId + apiKey);
this._profileDataWithShellCache = new ShellProvider(
shellModel,
dataObservable
);
}
return this._profileDataWithShellCache.observable;
```
---------------- распознаватель маршрута ----------------------------------
```
@Injectable()
export class UserProfileResolver implements Resolve<any> {
constructor(private userService: UserService) { }
resolve() {
// Get the Shell Provider from the service
const shellProviderObservable = this.userService.getProfileDataWithShell();
// Resolve with Shell Provider
const observablePromise = new Promise((resolve, reject) => {
resolve(shellProviderObservable);
});
return observablePromise;
}
}
```
---------------------- поставщик оболочки ---------------------------------
```
import { Observable, BehaviorSubject, forkJoin, of } from 'rxjs';
import {first, delay, finalize, take} from 'rxjs/operators';
import { environment } from '../../environments/environment';
export class ShellProvider<T> {
private _observable: Observable<T>;
private _subject: BehaviorSubject<T>;
private networkDelay = (environment && environment.shell && environment.shell.networkDelay) ? environment.shell.networkDelay : 0;
// To debug shell styles, change configuration in the environment.ts file
private debugMode = (environment && environment.shell && environment.shell.debug) ? environment.shell.debug : false;
constructor(shellModel: T, dataObservable: Observable<T>) {
// tslint:disable-next-line:max-line-length
const shellClassName = (shellModel && shellModel.constructor && shellModel.constructor.name) ? shellModel.constructor.name : 'No Class Name';
// tslint:disable-next-line:no-console
console.time('[' + shellClassName + '] ShellProvider roundtrip - first one on BS shellModel');
// Set the shell model as the initial value
this._subject = new BehaviorSubject<T>(shellModel);
dataObservable.pipe(
take(1), // Prevent the need to unsubscribe because .first() completes the observable
// finalize(() => console.log('dataObservable COMPLETED'))
);
const delayObservable = of(true).pipe(
delay(this.networkDelay),
// finalize(() => console.log('delayObservable COMPLETED'))
);
// Put both delay and data Observables in a forkJoin so they execute in parallel so that
// the delay caused (on purpose) by the delayObservable doesn't get added to the time the dataObservable takes to complete
const forkedObservables = forkJoin(
delayObservable,
dataObservable
)
.pipe(
// finalize(() => console.log('forkedObservables COMPLETED'))
)
.subscribe(([delayValue, dataValue]: [boolean, T]) => {
if (!this.debugMode) {
this._subject.next(dataValue);
// tslint:disable-next-line:no-console
console.timeEnd('[' + shellClassName + '] ShellProvider roundtrip');
}
});
this._observable = this._subject.asObservable();
}
public get observable(): Observable<T> {
return this._observable;
}
}
```
---------------------------- компонент user-profile.page --------------------
```
import { Component, OnInit, HostBinding } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { UserProfileModel } from './user-profile.model';
@Component({
selector: 'app-user-profile',
templateUrl: './user-profile.page.html',
styleUrls: [
'./styles/user-profile.page.scss',
'./styles/user-profile.shell.scss',
'./styles/user-profile.ios.scss',
'./styles/user-profile.md.scss'
],
})
export class UserProfilePage implements OnInit {
profile: UserProfileModel;
@HostBinding('class.is-shell') get isShell() {
return (this.profile && this.profile.isShell) ? true : false;
}
constructor(private route: ActivatedRoute) { }
ngOnInit(): void {
if (this.route && this.route.data) {
// We resolved a promise for the data Observable
const promiseObservable = this.route.data;
console.log('Route Resolve Observable => promiseObservable: ', promiseObservable);
if (promiseObservable) {
promiseObservable.subscribe(promiseValue => {
const dataObservable = promiseValue['data'];
console.log('Subscribe to promiseObservable => dataObservable: ', dataObservable);
if (dataObservable) {
dataObservable.subscribe(observableValue => {
const pageData: UserProfileModel = observableValue;
// tslint:disable-next-line:max-line-length
console.log('Subscribe to dataObservable (can emmit multiple values) => PageData (' + ((pageData && pageData.isShell) ? 'SHELL' : 'REAL') + '): ', pageData);
// As we are implementing an App Shell architecture, pageData will be firstly an empty shell model,
// and the real remote data once it gets fetched
if (pageData) {
this.profile = pageData;
}
});
} else {
console.warn('No dataObservable coming from Route Resolver promiseObservable');
}
});
} else {
console.warn('No promiseObservable coming from Route Resolver data');
}
} else {
console.warn('No data coming from Route Resolver');
}
}
}
```
-------------------- шаблон user-profile.page ----------------------------
```
<ion-header no-border>
<ion-toolbar>
<ion-buttons slot="start">
<ion-menu-button></ion-menu-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="user-profile-content">
<ion-row class="user-details-section">
<ion-col class="user-image-wrapper">
<app-aspect-ratio [ratio]="{w: 1, h: 1}">
<app-image-shell class="user-image" animation="spinner" [src]="profile.photoUrl"></app-image-shell>
</app-aspect-ratio>
</ion-col>
<ion-col class="user-info-wrapper">
<ion-row class="user-data-row">
<ion-col size="9">
<h3 class="user-name">
<app-text-shell [data]="profile.displayName"></app-text-shell>
</h3>
<h5 class="user-title">
<app-text-shell [data]="profile.job"></app-text-shell>
</h5>
</ion-col>
<ion-col class="membership-col">
<span class="user-membership">
<app-text-shell [data]="profile.membership"></app-text-shell>
</span>
</ion-col>
</ion-row>
<ion-row class="actions-row">
<ion-col class="main-actions">
<ion-button class="call-to-action-btn" size="small" color="primary">Follow</ion-button>
<ion-button class="call-to-action-btn" size="small" color="medium">Message</ion-button>
</ion-col>
<ion-col class="secondary-actions">
<ion-button class="more-btn" size="small" fill="clear" color="medium">
<ion-icon slot="icon-only" name="more"></ion-icon>
</ion-button>
</ion-col>
</ion-row>
</ion-col>
</ion-row>
<ion-row class="user-stats-section">
<ion-col class="user-stats-wrapper" size="4">
<span class="stat-value">
<app-text-shell [data]="profile.likes"></app-text-shell>
</span>
<span class="stat-name">Likes</span>
</ion-col>
<ion-col class="user-stats-wrapper" size="4">
<span class="stat-value">
<app-text-shell [data]="profile.followers"></app-text-shell>
</span>
<span class="stat-name">Followers</span>
</ion-col>
<ion-col class="user-stats-wrapper" size="4">
<span class="stat-value">
<app-text-shell [data]="profile.following"></app-text-shell>
</span>
<span class="stat-name">Following</span>
</ion-col>
</ion-row>
<div class="user-about-section">
<h3 class="details-section-title">About</h3>
<p class="user-description">
<app-text-shell animation="bouncing" lines="4" [data]="profile.about"></app-text-shell>
</p>
</div>
</ion-content>
```
```
I am expecting:
{
displayName: 'some name',
photoUrl: 'some url',
uid: 'some user id'
...
}
But I'm getting:
{name: 'projects/projectName/database/(default)/documents/users/uid',
fields:
displayName: {stringValue: 'some name'}
photoUrl: {stringValue: 'some url'}
uid: {stringValue: 'some user id'}
...
}
```
Я не получаю никаких сообщений об ошибках, что странно.Опять же, если кто-нибудь может помочь мне понять:
Почему я не получаю ожидаемый формат объекта в компоненте user-profile.page, то есть в шаблоне?
Почему я не могу получить доступ к полям: парам значений данных в формате «Но я получаю:»?
Я был бы очень признателен.