#javascript #angular #typescript #angularfire
#javascript #angular #typescript #angularfire
Вопрос:
Я разрабатываю веб-приложение, в котором несколько компонентов имеют схожие функции. Чтобы избежать дублирования кода в разных компонентах, я создал сервис для размещения этих функций.
Однако я не уверен, как управлять подписками на общие функции, поскольку они больше не являются частью каждого компонента и по-прежнему требуют подписки в службе. В идеале я бы вообще не подписывался на службу и подписывался только в компоненте, но характер функции и ее назначение не позволяют мне этого делать. Это функции AngularFire, вложенные одна в другую. Цель этой структуры — перемещаться по различным уровням базы данных и динамически заполнять объект. Затем этот объект используется для создания раздела заголовков главной таблицы. Количество уровней (столбцов и строк с накоплением) зависит от данных, присутствующих в БД, в зависимости от запрашиваемого узла БД. Это не мой любимый способ справиться с этим, поэтому, если вы думаете о более чистом способе сделать это, обязательно. Имейте в виду, что фактический код несколько сложнее, чем приведенный ниже пример.
Например, следующий объект генерируется этим кодом, и его соответствующий раздел заголовка отображается на следующем изображении :
object = {
title: 'PW100',
sections: {
section1: {
title: 'HIGH PRESSURE ROTOR',
sections: {
section1: {
title: 'HP TURBINE',
sections : {
section1: {title: 'ASS'},
section2: {title: 'GRD'},
section3: {title: 'BAL'}
}
},
section2: {
title: 'HP ROTOR',
sections: {
section1: {title: 'ASS'},
section2: {title: 'BAL'}
}
}
}
},
section2: {
title: 'LOW PRESSURE ROTOR',
sections: {
section1: {
title: 'LP TURBINE',
sections : {
section1: {title: 'ASS'},
section2: {title: 'RIV'},
section3: {title: 'GRD'},
section4: {title: 'BAL'}
}
},
section2: {
title: 'LP ROTOR',
sections: {
section1: {title: 'ASS'},
section2: {title: 'BAL'}
}
}
}
},
section3: {
title: 'POWER TURBINE PACK',
sections: {
section1: {
title: '3RD STAGE TURBINE',
sections : {
section1: {title: 'ASS'},
section2: {title: 'RIV'},
section3: {title: 'GRD'},
section4: {title: 'BAL'}
}
},
section2: {
title: '4TH STAGE TURBINE',
sections : {
section1: {title: 'ASS'},
section2: {title: 'RIV'},
section3: {title: 'GRD'},
section4: {title: 'BAL'}
}
},
section3: {
title: 'PT PACK',
sections: {
section1: {title: 'ASS'},
section2: {title: 'BAL'}
}
}
}
}
}
};
То, что у меня было раньше, работало отлично :
Компоненты A и B раньше имели следующую структуру :
export class ComponentXXClass implements OnInit, OnDestroy {
// Variable used to unsubscribe in ngOnDestroy()
$unsubscribe = new Subject();
constructor(private afs: AngularFirestore) {}
ngOnInit(): void {
this.functionA().then(result => {
// Use promise's result (which is an object) to generate the table's headers section
});
}
ngOnDestroy(): void {
this.$unsubscribe.next();
this.$unsubscribe.complete();
}
functionA() {
const objectToPopulateOnEachLevel = {};
return new Promise((resolve) => {
this.afs.collection('collectionName')
.snapshotChanges()
.pipe(takeUntil(this.$unsubscribe))
.subscribe(level1VariableRequiredToAccessLevel2 => {
level1VariableRequiredToAccessLevel2.forEach(level1Var => {
// Store relevant information found at this level of the query
objectToPopulateOnEachLevel['level1'] = level1Var.payload.doc.data()['variableOfInterest'];
this.afs.collection(level1Var.payload.doc.ref.path '/path1')
.snapshotChanges()
.pipe(takeUntil(this.$unsubscribe))
.subscribe(level2Variable => {
level2Variable.forEach(level2Var => {
// Store relevant information found at this level of the query
objectToPopulateOnEachLevel['level2'] = level2Var.payload.doc.data()['variableOfInterest'];
});
resolve(objectToPopulateOnEachLevel);
});
});
});
});
}
}
Общая часть кода была продублирована между компонентами A и B (и многими другими в моем реальном сценарии), поэтому в итоге я сделал что-то вроде этого :
Обслуживание :
export class Service {
// Variable used to unsubscribe in components' ngOnDestroy()
$unsubscribe = new Subject();
constructor(private afs: AngularFirestore) {}
functionA(parentPath: string, childPath: string) {
const objectToPopulateOnEachLevel = {};
return new Promise((resolve) => {
this.afs.collection(parentPath)
.snapshotChanges()
.pipe(takeUntil(this.$unsubscribe))
.subscribe(level1VariableRequiredToAccessLevel2 => {
level1VariableRequiredToAccessLevel2.forEach(level1Var => {
// Store relevant information found at this level of the query
objectToPopulateOnEachLevel['level1'] = level1Var.payload.doc.data()['variableOfInterest'];
this.afs.collection(level1Var.payload.doc.ref.path '/' childPath)
.snapshotChanges()
.pipe(takeUntil(this.$unsubscribe))
.subscribe(level2Variable => {
level2Variable.forEach(level2Var => {
// Store relevant information found at this level of the query
objectToPopulateOnEachLevel['level2'] = level2Var.payload.doc.data()['variableOfInterest'];
});
resolve(objectToPopulateOnEachLevel);
});
});
});
});
}
}
Компоненты A и B :
export class ComponentXXClass implements OnInit, OnDestroy {
constructor(private _service: Service) {}
ngOnInit(): void {
this._service.functionA('parentPathString', 'childPathString').then(result => {
// Use promise's result (which is an object) to generate the table's headers section
});
}
ngOnDestroy(): void {
this._service.$unsubscribe.next();
this._service.$unsubscribe.complete();
}
}
Это работает, но не полностью. Я имею в виду, что данные отображаются правильно, но кажется, что, хотя я не вышел из компонента A (например), подписка, похоже, не работает. Находясь в компоненте A, я попытался обновить переменную в БД на узле, на который подписывается служба. Ожидаемое поведение заключается в том, чтобы эта подписка запускалась и передавала новое значение компоненту A. Например, в приведенном выше примере (объект и соответствующие ему заголовки отображаются на изображении), если я зайду в БД и изменю «РОТОР ВЫСОКОГО ДАВЛЕНИЯ» на «РОТОР БЕЗ ДАВЛЕНИЯ», я ожидаю, что подпискачтобы запустить службу, извлеките это изменение, повторно создав объект в обещании и повторно отправив его компоненту A, который затем повторно отобразит заголовки таблицы. Однако этого не происходит. Я также попытался удалить часть «отписки», чтобы узнать, был ли причиной способ, которым я обработал отписку. Результат был тот же.
Есть мысли?
Спасибо
Ответ №1:
Вы должны использовать наблюдаемые для управления состоянием данных readonly objectPopulatedSubject = new BehaviorSubject({})
, также вам необходимо создать свойство для доступа к наблюдаемому readonly objectPopulated$ = objectPopulatedSubject.asObservable()
, а также вам необходимо создать метод для обновления темы и создания нового значения для ваших подписок updateObjectPopulated(data: YourType) { this.objectPopulatedSubject.next(data)}
, а в ваших компонентах вы должны подписаться на objectPopulated $ .
Также для управления подпиской вы должны следовать следующим инструкциям: Создайте свойство подписки objectPopulatedSubscription: Subscription;
затем в вашем ngOnInit, this.objectPopulatedSubscription = this.yourservice.objectPopulated$.subscribe(data => {//put here your logic})
а также в вашем ngOnDestroy put this.objectPopulatedSubscription.unsubscribe()