Merge pull request #3867 from FinnStutzenstein/baseRepo
first work for BaseRepo
This commit is contained in:
commit
34412c7d9e
127
client/src/app/site/base-repository.ts
Normal file
127
client/src/app/site/base-repository.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import { OpenSlidesComponent } from '../openslides.component';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { BaseViewModel } from './base-view-model';
|
||||||
|
import { BaseModel, ModelConstructor } from '../shared/models/base.model';
|
||||||
|
import { CollectionStringModelMapperService } from '../core/services/collectionStringModelMapper.service';
|
||||||
|
|
||||||
|
export abstract class BaseRepository<V extends BaseViewModel, M extends BaseModel> extends OpenSlidesComponent {
|
||||||
|
/**
|
||||||
|
* Stores all the viewModel in an object
|
||||||
|
*/
|
||||||
|
protected viewModelStore: { [modelId: number]: V } = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores subjects to viewModels in a list
|
||||||
|
*/
|
||||||
|
protected viewModelSubjects: { [modelId: number]: BehaviorSubject<V> } = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable subject for the whole list
|
||||||
|
*/
|
||||||
|
protected viewModelListSubject: BehaviorSubject<V[]> = new BehaviorSubject<V[]>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param baseModelCtor The model constructor of which this repository is about.
|
||||||
|
* @param depsModelCtors A list of constructors that are used in the view model.
|
||||||
|
* If one of those changes, the view models will be updated.
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
protected baseModelCtor: ModelConstructor<M>,
|
||||||
|
protected depsModelCtors: ModelConstructor<BaseModel>[]
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
// Populate the local viewModelStore with ViewModel Objects.
|
||||||
|
this.DS.getAll(baseModelCtor).forEach((model: M) => {
|
||||||
|
this.viewModelStore[model.id] = this.createViewModel(model);
|
||||||
|
this.updateViewModelObservable(model.id);
|
||||||
|
});
|
||||||
|
this.updateViewModelListObservable();
|
||||||
|
|
||||||
|
// Could be raise in error if the root injector is not known
|
||||||
|
this.DS.changeObservable.subscribe(model => {
|
||||||
|
if (model instanceof this.baseModelCtor) {
|
||||||
|
// Add new and updated motions to the viewModelStore
|
||||||
|
this.viewModelStore[model.id] = this.createViewModel(model as M);
|
||||||
|
this.updateAllObservables(model.id);
|
||||||
|
} else {
|
||||||
|
const dependencyChanged: boolean = this.depsModelCtors.some(ctor => {
|
||||||
|
return model instanceof ctor;
|
||||||
|
});
|
||||||
|
if (dependencyChanged) {
|
||||||
|
// if an domain object we need was added or changed, update viewModelStore
|
||||||
|
this.getViewModelList().forEach(viewModel => {
|
||||||
|
viewModel.updateValues(model);
|
||||||
|
});
|
||||||
|
this.updateAllObservables(model.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Watch the Observables for deleting
|
||||||
|
this.DS.deletedObservable.subscribe(model => {
|
||||||
|
if (model.collection === CollectionStringModelMapperService.getCollectionString(baseModelCtor)) {
|
||||||
|
delete this.viewModelStore[model.id];
|
||||||
|
this.updateAllObservables(model.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract createViewModel(model: M): V;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper function to return one viewModel
|
||||||
|
*/
|
||||||
|
protected getViewModel(id: number): V {
|
||||||
|
return this.viewModelStore[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper function to return the viewModel as array
|
||||||
|
*/
|
||||||
|
protected getViewModelList(): V[] {
|
||||||
|
return Object.values(this.viewModelStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the current observable for one viewModel
|
||||||
|
*/
|
||||||
|
public getViewModelObservable(id: number): Observable<V> {
|
||||||
|
if (!this.viewModelSubjects[id]) {
|
||||||
|
this.viewModelSubjects[id] = new BehaviorSubject<V>(this.viewModelStore[id]);
|
||||||
|
}
|
||||||
|
return this.viewModelSubjects[id].asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the Observable of the whole store
|
||||||
|
*/
|
||||||
|
public getViewModelListObservable(): Observable<V[]> {
|
||||||
|
return this.viewModelListSubject.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the ViewModel observable using a ViewModel corresponding to the id
|
||||||
|
*/
|
||||||
|
protected updateViewModelObservable(id: number): void {
|
||||||
|
if (this.viewModelSubjects[id]) {
|
||||||
|
this.viewModelSubjects[id].next(this.viewModelStore[id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update the observable of the list
|
||||||
|
*/
|
||||||
|
protected updateViewModelListObservable(): void {
|
||||||
|
this.viewModelListSubject.next(this.getViewModelList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers both the observable update routines
|
||||||
|
*/
|
||||||
|
protected updateAllObservables(id: number): void {
|
||||||
|
this.updateViewModelListObservable();
|
||||||
|
this.updateViewModelObservable(id);
|
||||||
|
}
|
||||||
|
}
|
39
client/src/app/site/base-view-model.ts
Normal file
39
client/src/app/site/base-view-model.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { BaseModel } from '../shared/models/base.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for view models. alls view models should have titles.
|
||||||
|
*/
|
||||||
|
export abstract class BaseViewModel {
|
||||||
|
public abstract updateValues(update: BaseModel): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the title for the detail view.
|
||||||
|
* @param translate
|
||||||
|
*/
|
||||||
|
public abstract getTitle(translate: TranslateService): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the title for the list view.
|
||||||
|
* @param translate
|
||||||
|
*/
|
||||||
|
public getListTitle(translate: TranslateService): string {
|
||||||
|
return this.getTitle(translate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the title for the projector.
|
||||||
|
* @param translate
|
||||||
|
*/
|
||||||
|
public getProjector(translate: TranslateService): string {
|
||||||
|
return this.getTitle(translate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the title for the agenda list view.
|
||||||
|
* @param translate
|
||||||
|
*/
|
||||||
|
public getAgendaTitle(translate: TranslateService): string {
|
||||||
|
return this.getTitle(translate);
|
||||||
|
}
|
||||||
|
}
|
@ -90,7 +90,7 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
} else {
|
} else {
|
||||||
// load existing motion
|
// load existing motion
|
||||||
this.route.params.subscribe(params => {
|
this.route.params.subscribe(params => {
|
||||||
this.repo.getViewMotionObservable(params.id).subscribe(newViewMotion => {
|
this.repo.getViewModelObservable(params.id).subscribe(newViewMotion => {
|
||||||
this.motion = newViewMotion;
|
this.motion = newViewMotion;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -97,7 +97,7 @@ export class MotionListComponent extends BaseComponent implements OnInit {
|
|||||||
this.dataSource.paginator = this.paginator;
|
this.dataSource.paginator = this.paginator;
|
||||||
this.dataSource.sort = this.sort;
|
this.dataSource.sort = this.sort;
|
||||||
|
|
||||||
this.repo.getViewMotionListObservable().subscribe(newMotions => {
|
this.repo.getViewModelListObservable().subscribe(newMotions => {
|
||||||
this.dataSource.data = newMotions;
|
this.dataSource.data = newMotions;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ import { User } from '../../../shared/models/users/user';
|
|||||||
import { Workflow } from '../../../shared/models/motions/workflow';
|
import { Workflow } from '../../../shared/models/motions/workflow';
|
||||||
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
|
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
|
||||||
import { BaseModel } from '../../../shared/models/base.model';
|
import { BaseModel } from '../../../shared/models/base.model';
|
||||||
|
import { BaseViewModel } from '../../base-view-model';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Motion class for the View
|
* Motion class for the View
|
||||||
@ -12,7 +14,7 @@ import { BaseModel } from '../../../shared/models/base.model';
|
|||||||
* Provides "safe" access to variables and functions in {@link Motion}
|
* Provides "safe" access to variables and functions in {@link Motion}
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class ViewMotion {
|
export class ViewMotion extends BaseViewModel {
|
||||||
private _motion: Motion;
|
private _motion: Motion;
|
||||||
private _category: Category;
|
private _category: Category;
|
||||||
private _submitters: User[];
|
private _submitters: User[];
|
||||||
@ -70,7 +72,7 @@ export class ViewMotion {
|
|||||||
|
|
||||||
public get categoryId(): number {
|
public get categoryId(): number {
|
||||||
if (this._motion && this._motion.category_id) {
|
if (this._motion && this._motion.category_id) {
|
||||||
return this.category.id;
|
return this._motion.category_id;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -147,6 +149,8 @@ export class ViewMotion {
|
|||||||
workflow?: Workflow,
|
workflow?: Workflow,
|
||||||
state?: WorkflowState
|
state?: WorkflowState
|
||||||
) {
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
this._motion = motion;
|
this._motion = motion;
|
||||||
this._category = category;
|
this._category = category;
|
||||||
this._submitters = submitters;
|
this._submitters = submitters;
|
||||||
@ -155,6 +159,10 @@ export class ViewMotion {
|
|||||||
this._state = state;
|
this._state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTitle(translate?: TranslateService): string {
|
||||||
|
return this.title;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the local objects if required
|
* Updates the local objects if required
|
||||||
* @param update
|
* @param update
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { DataSendService } from '../../../core/services/data-send.service';
|
import { DataSendService } from '../../../core/services/data-send.service';
|
||||||
import { OpenSlidesComponent } from '../../../openslides.component';
|
|
||||||
import { Motion } from '../../../shared/models/motions/motion';
|
import { Motion } from '../../../shared/models/motions/motion';
|
||||||
import { User } from '../../../shared/models/users/user';
|
import { User } from '../../../shared/models/users/user';
|
||||||
import { Category } from '../../../shared/models/motions/category';
|
import { Category } from '../../../shared/models/motions/category';
|
||||||
import { Workflow } from '../../../shared/models/motions/workflow';
|
import { Workflow } from '../../../shared/models/motions/workflow';
|
||||||
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
|
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
|
||||||
import { ViewMotion } from '../models/view-motion';
|
import { ViewMotion } from '../models/view-motion';
|
||||||
import { Observable, BehaviorSubject } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { BaseRepository } from '../../base-repository';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Services for motions (and potentially categories)
|
* Repository Services for motions (and potentially categories)
|
||||||
@ -23,22 +23,7 @@ import { Observable, BehaviorSubject } from 'rxjs';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class MotionRepositoryService extends OpenSlidesComponent {
|
export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion> {
|
||||||
/**
|
|
||||||
* Stores all the viewMotion in an object
|
|
||||||
*/
|
|
||||||
private viewMotionStore: { [motionId: number]: ViewMotion } = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores subjects to viewMotions in a list
|
|
||||||
*/
|
|
||||||
private viewMotionSubjects: { [motionId: number]: BehaviorSubject<ViewMotion> } = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Observable subject for the whole list
|
|
||||||
*/
|
|
||||||
private viewMotionListSubject: BehaviorSubject<ViewMotion[]> = new BehaviorSubject<ViewMotion[]>(null);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a MotionRepository
|
* Creates a MotionRepository
|
||||||
*
|
*
|
||||||
@ -47,46 +32,7 @@ export class MotionRepositoryService extends OpenSlidesComponent {
|
|||||||
* @param DataSend
|
* @param DataSend
|
||||||
*/
|
*/
|
||||||
public constructor(private dataSend: DataSendService) {
|
public constructor(private dataSend: DataSendService) {
|
||||||
super();
|
super(Motion, [Category, User, Workflow]);
|
||||||
|
|
||||||
this.populateViewMotions();
|
|
||||||
|
|
||||||
// Could be raise in error if the root injector is not known
|
|
||||||
this.DS.changeObservable.subscribe(model => {
|
|
||||||
if (model instanceof Motion) {
|
|
||||||
// Add new and updated motions to the viewMotionStore
|
|
||||||
this.AddViewMotion(model);
|
|
||||||
this.updateObservables(model.id);
|
|
||||||
} else if (model instanceof Category || model instanceof User || model instanceof Workflow) {
|
|
||||||
// if an domain object we need was added or changed, update ViewMotionStore
|
|
||||||
this.getViewMotionList().forEach(viewMotion => {
|
|
||||||
viewMotion.updateValues(model);
|
|
||||||
});
|
|
||||||
this.updateObservables(model.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Watch the Observables for deleting
|
|
||||||
this.DS.deletedObservable.subscribe(model => {
|
|
||||||
if (model.collection === 'motions/motion') {
|
|
||||||
delete this.viewMotionStore[model.id];
|
|
||||||
this.updateObservables(model.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called from the constructor.
|
|
||||||
*
|
|
||||||
* Populate the local viewMotionStore with ViewMotion Objects.
|
|
||||||
* Does nothing if the database was not created yet.
|
|
||||||
*/
|
|
||||||
private populateViewMotions(): void {
|
|
||||||
this.DS.getAll<Motion>(Motion).forEach(motion => {
|
|
||||||
this.AddViewMotion(motion);
|
|
||||||
this.updateViewMotionObservable(motion.id);
|
|
||||||
});
|
|
||||||
this.updateViewMotionListObservable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,7 +43,7 @@ export class MotionRepositoryService extends OpenSlidesComponent {
|
|||||||
*
|
*
|
||||||
* @param motion blank motion domain object
|
* @param motion blank motion domain object
|
||||||
*/
|
*/
|
||||||
private AddViewMotion(motion: Motion): void {
|
protected createViewModel(motion: Motion): ViewMotion {
|
||||||
const category = this.DS.get(Category, motion.category_id);
|
const category = this.DS.get(Category, motion.category_id);
|
||||||
const submitters = this.DS.getMany(User, motion.submitterIds);
|
const submitters = this.DS.getMany(User, motion.submitterIds);
|
||||||
const supporters = this.DS.getMany(User, motion.supporters_id);
|
const supporters = this.DS.getMany(User, motion.supporters_id);
|
||||||
@ -106,7 +52,7 @@ export class MotionRepositoryService extends OpenSlidesComponent {
|
|||||||
if (workflow) {
|
if (workflow) {
|
||||||
state = workflow.getStateById(motion.state_id);
|
state = workflow.getStateById(motion.state_id);
|
||||||
}
|
}
|
||||||
this.viewMotionStore[motion.id] = new ViewMotion(motion, category, submitters, supporters, workflow, state);
|
return new ViewMotion(motion, category, submitters, supporters, workflow, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,23 +78,6 @@ export class MotionRepositoryService extends OpenSlidesComponent {
|
|||||||
return this.dataSend.saveModel(updateMotion);
|
return this.dataSend.saveModel(updateMotion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the current observable MotionView
|
|
||||||
*/
|
|
||||||
public getViewMotionObservable(id: number): Observable<ViewMotion> {
|
|
||||||
if (!this.viewMotionSubjects[id]) {
|
|
||||||
this.updateViewMotionObservable(id);
|
|
||||||
}
|
|
||||||
return this.viewMotionSubjects[id].asObservable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return the Observable of the whole store
|
|
||||||
*/
|
|
||||||
public getViewMotionListObservable(): Observable<ViewMotion[]> {
|
|
||||||
return this.viewMotionListSubject.asObservable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deleting a motion.
|
* Deleting a motion.
|
||||||
*
|
*
|
||||||
@ -159,36 +88,4 @@ export class MotionRepositoryService extends OpenSlidesComponent {
|
|||||||
public deleteMotion(viewMotion: ViewMotion): Observable<any> {
|
public deleteMotion(viewMotion: ViewMotion): Observable<any> {
|
||||||
return this.dataSend.delete(viewMotion.motion);
|
return this.dataSend.delete(viewMotion.motion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the ViewMotion observable using a ViewMotion corresponding to the id
|
|
||||||
*/
|
|
||||||
private updateViewMotionObservable(id: number): void {
|
|
||||||
if (!this.viewMotionSubjects[id]) {
|
|
||||||
this.viewMotionSubjects[id] = new BehaviorSubject<ViewMotion>(null);
|
|
||||||
}
|
|
||||||
this.viewMotionSubjects[id].next(this.viewMotionStore[id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper function to return the viewMotions as array
|
|
||||||
*/
|
|
||||||
private getViewMotionList(): ViewMotion[] {
|
|
||||||
return Object.values(this.viewMotionStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* update the observable of the list
|
|
||||||
*/
|
|
||||||
private updateViewMotionListObservable(): void {
|
|
||||||
this.viewMotionListSubject.next(this.getViewMotionList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggers both the observable update routines
|
|
||||||
*/
|
|
||||||
private updateObservables(id: number): void {
|
|
||||||
this.updateViewMotionListObservable();
|
|
||||||
this.updateViewMotionObservable(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user