Merge pull request #3867 from FinnStutzenstein/baseRepo

first work for BaseRepo
This commit is contained in:
Sean 2018-09-11 11:02:25 +02:00 committed by GitHub
commit 34412c7d9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 184 additions and 113 deletions

View 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);
}
}

View 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);
}
}

View File

@ -90,7 +90,7 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
} else {
// load existing motion
this.route.params.subscribe(params => {
this.repo.getViewMotionObservable(params.id).subscribe(newViewMotion => {
this.repo.getViewModelObservable(params.id).subscribe(newViewMotion => {
this.motion = newViewMotion;
});
});

View File

@ -97,7 +97,7 @@ export class MotionListComponent extends BaseComponent implements OnInit {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
this.repo.getViewMotionListObservable().subscribe(newMotions => {
this.repo.getViewModelListObservable().subscribe(newMotions => {
this.dataSource.data = newMotions;
});
}

View File

@ -4,6 +4,8 @@ import { User } from '../../../shared/models/users/user';
import { Workflow } from '../../../shared/models/motions/workflow';
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
import { BaseModel } from '../../../shared/models/base.model';
import { BaseViewModel } from '../../base-view-model';
import { TranslateService } from '@ngx-translate/core';
/**
* 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}
* @ignore
*/
export class ViewMotion {
export class ViewMotion extends BaseViewModel {
private _motion: Motion;
private _category: Category;
private _submitters: User[];
@ -70,7 +72,7 @@ export class ViewMotion {
public get categoryId(): number {
if (this._motion && this._motion.category_id) {
return this.category.id;
return this._motion.category_id;
} else {
return null;
}
@ -147,6 +149,8 @@ export class ViewMotion {
workflow?: Workflow,
state?: WorkflowState
) {
super();
this._motion = motion;
this._category = category;
this._submitters = submitters;
@ -155,6 +159,10 @@ export class ViewMotion {
this._state = state;
}
public getTitle(translate?: TranslateService): string {
return this.title;
}
/**
* Updates the local objects if required
* @param update

View File

@ -1,14 +1,14 @@
import { Injectable } from '@angular/core';
import { DataSendService } from '../../../core/services/data-send.service';
import { OpenSlidesComponent } from '../../../openslides.component';
import { Motion } from '../../../shared/models/motions/motion';
import { User } from '../../../shared/models/users/user';
import { Category } from '../../../shared/models/motions/category';
import { Workflow } from '../../../shared/models/motions/workflow';
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
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)
@ -23,22 +23,7 @@ import { Observable, BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class MotionRepositoryService extends OpenSlidesComponent {
/**
* 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);
export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion> {
/**
* Creates a MotionRepository
*
@ -47,46 +32,7 @@ export class MotionRepositoryService extends OpenSlidesComponent {
* @param DataSend
*/
public constructor(private dataSend: DataSendService) {
super();
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();
super(Motion, [Category, User, Workflow]);
}
/**
@ -97,7 +43,7 @@ export class MotionRepositoryService extends OpenSlidesComponent {
*
* @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 submitters = this.DS.getMany(User, motion.submitterIds);
const supporters = this.DS.getMany(User, motion.supporters_id);
@ -106,7 +52,7 @@ export class MotionRepositoryService extends OpenSlidesComponent {
if (workflow) {
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);
}
/**
* 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.
*
@ -159,36 +88,4 @@ export class MotionRepositoryService extends OpenSlidesComponent {
public deleteMotion(viewMotion: ViewMotion): Observable<any> {
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);
}
}