Merge pull request #4725 from GabrielInTheWorld/form-control

Separates the attachment field to a custom component
This commit is contained in:
Sean 2019-07-08 13:50:02 +02:00 committed by GitHub
commit 596b9516e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 230 additions and 253 deletions

View File

@ -21,7 +21,6 @@
<div *ngIf="itemObserver.value.length > 0"> <div *ngIf="itemObserver.value.length > 0">
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="form"
[formControl]="form.get('agenda_parent_id')" [formControl]="form.get('agenda_parent_id')"
[multiple]="false" [multiple]="false"
[includeNone]="true" [includeNone]="true"

View File

@ -0,0 +1,29 @@
<div class="attachment-container" *ngIf="controlName">
<os-search-value-selector
class="selector"
ngDefaultControl
[multiple]="true"
listname="{{ 'Attachments' | translate }}"
[formControl]="controlName"
[InputListValues]="mediaFileList"
></os-search-value-selector>
<button
type="button"
mat-icon-button
(click)="openUploadDialog(uploadDialog)"
*osPerms="'mediafiles.can_upload'"
>
<mat-icon>cloud_upload</mat-icon>
</button>
</div>
<!-- upload file dialog -->
<ng-template #uploadDialog>
<h1 mat-dialog-title>
<span translate>Upload files</span>
</h1>
<os-media-upload-content
(uploadSuccessEvent)="uploadSuccess($event)"
(errorEvent)="uploadError($event)"
></os-media-upload-content>
</ng-template>

View File

@ -0,0 +1,14 @@
:host {
width: 100%;
.attachment-container {
justify-content: space-between;
display: flex;
.selector {
width: 95%;
}
.mat-icon-button {
top: 20px;
}
}
}

View File

@ -0,0 +1,25 @@
import { async, TestBed, ComponentFixture } from '@angular/core/testing';
import { AttachmentControlComponent } from './attachment-control.component';
import { E2EImportsModule } from 'e2e-imports.module';
describe('AttachmentControlComponent', () => {
let component: AttachmentControlComponent;
let fixture: ComponentFixture<AttachmentControlComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AttachmentControlComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,107 @@
import { Component, OnInit, TemplateRef, Output, EventEmitter, Input } from '@angular/core';
import { MatDialog } from '@angular/material';
import { FormControl, ControlValueAccessor } from '@angular/forms';
import { BehaviorSubject } from 'rxjs';
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
@Component({
selector: 'os-attachment-control',
templateUrl: './attachment-control.component.html',
styleUrls: ['./attachment-control.component.scss']
})
export class AttachmentControlComponent implements OnInit, ControlValueAccessor {
/**
* Output for an error handler
*/
@Output()
public errorHandler: EventEmitter<string> = new EventEmitter();
/**
* The form-control name to access the value for the form-control
*/
@Input()
public controlName: FormControl;
/**
* The file list that is necessary for the `SearchValueSelector`
*/
public mediaFileList: BehaviorSubject<ViewMediafile[]> = new BehaviorSubject([]);
/**
* Default constructor
*
* @param dialogService Reference to the `MatDialog`
* @param mediaService Reference for the `MediaFileRepositoryService`
*/
public constructor(private dialogService: MatDialog, private mediaService: MediafileRepositoryService) {}
/**
* On init method
*/
public ngOnInit(): void {
this.mediaFileList = this.mediaService.getViewModelListBehaviorSubject();
}
/**
* Function to open a given dialog
*
* @param dialog the dialog to open
*/
public openUploadDialog(dialog: TemplateRef<string>): void {
this.dialogService.open(dialog, {
width: '750px',
maxWidth: '90vw',
maxHeight: '90vh'
});
}
/**
* Function to set the value for the `SearchValueSelector` after successful upload
*
* @param fileIDs a list with the ids of the uploaded files
*/
public uploadSuccess(fileIDs: number[]): void {
if (this.controlName) {
const newValues = [...this.controlName.value, ...fileIDs];
this.controlName.setValue(newValues);
this.dialogService.closeAll();
}
}
/**
* Function to emit an occurring error.
*
* @param error The occurring error
*/
public uploadError(error: string): void {
this.errorHandler.emit(error);
}
/**
* Function to write a new value to the form.
* Satisfy the interface.
*
* @param value The new value for this form.
*/
public writeValue(value: any): void {
if (value && this.controlName) {
this.controlName.setValue(value);
}
}
/**
* Function executed when the control's value changed.
*
* @param fn the function that is executed.
*/
public registerOnChange(fn: any): void {}
/**
* To satisfy the interface
*
* @param fn the registered callback function for onBlur-events.
*/
public registerOnTouched(fn: any): void {}
}

View File

@ -36,10 +36,8 @@
<os-search-value-selector <os-search-value-selector
*ngIf="searchList" *ngIf="searchList"
ngDefaultControl ngDefaultControl
[form]="extensionFieldForm"
[formControl]="extensionFieldForm.get('list')" [formControl]="extensionFieldForm.get('list')"
[fullWidth]="true" [fullWidth]="true"
[multiple]="false"
[InputListValues]="searchList" [InputListValues]="searchList"
[listname]="searchListLabel" [listname]="searchListLabel"
></os-search-value-selector> ></os-search-value-selector>

View File

@ -1,25 +1,14 @@
<mat-form-field [formGroup]="form" [style.display]="fullWidth ? 'block' : 'inline-block'"> <mat-form-field [style.display]="fullWidth ? 'block' : 'inline-block'">
<mat-select [formControl]="formControl" placeholder="{{ listname | translate }}" [multiple]="multiple" #thisSelector> <mat-select [formControl]="formControl" placeholder="{{ listname | translate }}" [multiple]="multiple" #thisSelector>
<ngx-mat-select-search [formControl]="filterControl"></ngx-mat-select-search> <ngx-mat-select-search ngModel (ngModelChange)="onSearch($event)"></ngx-mat-select-search>
<div *ngIf="!multiple && includeNone"> <div *ngIf="!multiple && includeNone">
<mat-option [value]="null"> <mat-option [value]="null">
<span></span> <span></span>
</mat-option> </mat-option>
<mat-divider></mat-divider> <mat-divider></mat-divider>
</div> </div>
<mat-option *ngFor="let selectedItem of filteredItems | async" [value]="selectedItem.id"> <mat-option *ngFor="let selectedItem of getFilteredItems()" [value]="selectedItem.id">
{{ selectedItem.getTitle() | translate }} {{ selectedItem.getTitle() | translate }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<div *ngIf="dispSelected">
<p>
<span translate>Selected values</span>:
</p>
<mat-chip-list #chipList>
<mat-chip *ngFor="let selectedItem of thisSelector?.value" (removed)="remove(selectedItem)">
{{ selectedItem.name }}
<mat-icon (click)="remove(selectedItem)">cancel</mat-icon>
</mat-chip>
</mat-chip-list>
</div>

View File

@ -45,7 +45,6 @@ describe('SearchValueSelectorComponent', () => {
const formGroup = formBuilder.group({ const formGroup = formBuilder.group({
testArray: [] testArray: []
}); });
hostComponent.searchValueSelectorComponent.form = formGroup;
hostComponent.searchValueSelectorComponent.formControl = <FormControl>formGroup.get('testArray'); hostComponent.searchValueSelectorComponent.formControl = <FormControl>formGroup.get('testArray');
hostFixture.detectChanges(); hostFixture.detectChanges();

View File

@ -1,9 +1,9 @@
import { Component, OnInit, Input, ViewChild, OnDestroy } from '@angular/core'; import { Component, Input, ViewChild, OnDestroy } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { MatSelect } from '@angular/material/select'; import { MatSelect } from '@angular/material';
import { Subject, ReplaySubject, BehaviorSubject, Subscription } from 'rxjs'; import { BehaviorSubject, Subscription } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { auditTime } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Selectable } from '../selectable'; import { Selectable } from '../selectable';
@ -25,7 +25,6 @@ import { Selectable } from '../selectable';
* [multiple]="true" * [multiple]="true"
* placeholder="Placeholder" * placeholder="Placeholder"
* [InputListValues]="myListValues" * [InputListValues]="myListValues"
* [form]="myform_name"
* [formControl]="myformcontrol"> * [formControl]="myformcontrol">
* </os-search-value-selector> * </os-search-value-selector>
* ``` * ```
@ -37,38 +36,27 @@ import { Selectable } from '../selectable';
templateUrl: './search-value-selector.component.html', templateUrl: './search-value-selector.component.html',
styleUrls: ['./search-value-selector.component.scss'] styleUrls: ['./search-value-selector.component.scss']
}) })
export class SearchValueSelectorComponent implements OnInit, OnDestroy { export class SearchValueSelectorComponent implements OnDestroy {
/**
* ngModel variable - Deprecated with Angular 7
* DO NOT USE: READ AT remove() FUNCTION!
*/
public myModel = [];
/**
* Control for the filtering of the list
*/
public filterControl = new FormControl();
/**
* List of the filtered content, when entering something in the search bar
*/
public filteredItems: ReplaySubject<Selectable[]> = new ReplaySubject<Selectable[]>(1);
/**
* The inputlist subject.
*/
private _inputListSubject: BehaviorSubject<Selectable[]>;
/** /**
* Saves the current subscription to _inputListSubject. * Saves the current subscription to _inputListSubject.
*/ */
private _inputListSubscription: Subscription = null; private _inputListSubscription: Subscription = null;
/**
* Value of the search input
*/
private searchValue = '';
/**
* All items
*/
private selectableItems: Selectable[];
/** /**
* Decide if this should be a single or multi-select-field * Decide if this should be a single or multi-select-field
*/ */
@Input() @Input()
public multiple: boolean; public multiple = false;
/** /**
* Decide, if none should be included, if multiple is false. * Decide, if none should be included, if multiple is false.
@ -95,9 +83,14 @@ export class SearchValueSelectorComponent implements OnInit, OnDestroy {
if (this._inputListSubscription) { if (this._inputListSubscription) {
this._inputListSubscription.unsubscribe(); this._inputListSubscription.unsubscribe();
} }
this._inputListSubject = value; // this.inputSubject = value;
this._inputListSubscription = this._inputListSubject.subscribe(() => { this._inputListSubscription = value.pipe(auditTime(10)).subscribe(items => {
this.filterItems(); this.selectableItems = items;
if (this.formControl) {
items.length === 0
? this.formControl.disable({ emitEvent: false })
: this.formControl.enable({ emitEvent: false });
}
}); });
} }
@ -107,56 +100,23 @@ export class SearchValueSelectorComponent implements OnInit, OnDestroy {
@Input() @Input()
public listname: String; public listname: String;
/**
* Form Group
*/
@Input()
public form: FormGroup;
/** /**
* Name of the Form * Name of the Form
*/ */
@Input() @Input()
public formControl: FormControl; public formControl: FormControl;
/**
* DO NOT USE UNTIL BUG IN UPSTREAM ARE RESOLVED!
* READ AT FUNCTION remove()
*
* Displayes the selected Items as Chip-List
*/
// @Input()
public dispSelected = false;
/** /**
* The MultiSelect Component * The MultiSelect Component
*/ */
@ViewChild('thisSelector', { static: true }) @ViewChild('thisSelector', { static: true })
public thisSelector: MatSelect; public thisSelector: MatSelect;
/**
* Subject that emits when the component has been destroyed
*/
private _onDestroy = new Subject<void>();
/** /**
* Empty constructor * Empty constructor
*/ */
public constructor(protected translate: TranslateService) {} public constructor(protected translate: TranslateService) {}
/**
* onInit with filter ans subscription on filter
*/
public ngOnInit(): void {
if (this._inputListSubject) {
this.filteredItems.next(this._inputListSubject.getValue());
}
// listen to value changes
this.filterControl.valueChanges.pipe(takeUntil(this._onDestroy)).subscribe(() => {
this.filterItems();
});
}
/** /**
* Unsubscribe on destroing. * Unsubscribe on destroing.
*/ */
@ -164,53 +124,31 @@ export class SearchValueSelectorComponent implements OnInit, OnDestroy {
if (this._inputListSubscription) { if (this._inputListSubscription) {
this._inputListSubscription.unsubscribe(); this._inputListSubscription.unsubscribe();
} }
this._onDestroy.next();
} }
/** /**
* the filter function itself * Function to get a list filtered by the entered search value.
*
* @returns The filtered list of items.
*/ */
private filterItems(): void { public getFilteredItems(): Selectable[] {
if (!this._inputListSubject) { if (this.selectableItems) {
return; return this.selectableItems.filter(
} item =>
// get the search keyword item
let search = this.filterControl.value;
if (!search) {
this.filteredItems.next(this._inputListSubject.getValue());
return;
} else {
search = search.toLowerCase();
}
// filter the values
this.filteredItems.next(
this._inputListSubject.getValue().filter(
selectedItem =>
selectedItem
.toString() .toString()
.toLowerCase() .toLowerCase()
.indexOf(search) > -1 .indexOf(this.searchValue) > -1
)
); );
} }
}
/** /**
* If the dispSelected value is marked as true, a chipList should be shown below the * Function to set the search value.
* selection list. Unfortunately it is not possible (yet) to change the datamodel in the backend *
* https://github.com/angular/material2/issues/10085 - therefore you can display the values in two * @param searchValue the new value the user is searching for.
* places, but can't reflect the changes in both places. Until this can be done this will be unused code
* @param item the selected item to be removed
*/ */
public remove(item: Selectable): void { public onSearch(searchValue: string): void {
const myArr = this.thisSelector.value; this.searchValue = searchValue.toLowerCase();
const index = myArr.indexOf(item, 0);
// my model was the form according to fix
// https://github.com/angular/material2/issues/10044
// but this causes bad behaviour and will be depricated in Angular 7
this.myModel = this.myModel.slice(index, 1);
if (index > -1) {
myArr.splice(index, 1);
}
this.thisSelector.value = myArr;
} }
} }

View File

@ -92,6 +92,7 @@ import { IconContainerComponent } from './components/icon-container/icon-contain
import { ListViewTableComponent } from './components/list-view-table/list-view-table.component'; import { ListViewTableComponent } from './components/list-view-table/list-view-table.component';
import { AgendaContentObjectFormComponent } from './components/agenda-content-object-form/agenda-content-object-form.component'; import { AgendaContentObjectFormComponent } from './components/agenda-content-object-form/agenda-content-object-form.component';
import { ExtensionFieldComponent } from './components/extension-field/extension-field.component'; import { ExtensionFieldComponent } from './components/extension-field/extension-field.component';
import { AttachmentControlComponent } from './components/attachment-control/attachment-control.component';
/** /**
* Share Module for all "dumb" components and pipes. * Share Module for all "dumb" components and pipes.
@ -214,6 +215,7 @@ import { ExtensionFieldComponent } from './components/extension-field/extension-
SlideContainerComponent, SlideContainerComponent,
CountdownTimeComponent, CountdownTimeComponent,
MediaUploadContentComponent, MediaUploadContentComponent,
AttachmentControlComponent,
PrecisionPipe, PrecisionPipe,
SpeakerButtonComponent, SpeakerButtonComponent,
GridLayoutComponent, GridLayoutComponent,
@ -262,7 +264,8 @@ import { ExtensionFieldComponent } from './components/extension-field/extension-
IconContainerComponent, IconContainerComponent,
ListViewTableComponent, ListViewTableComponent,
AgendaContentObjectFormComponent, AgendaContentObjectFormComponent,
ExtensionFieldComponent ExtensionFieldComponent,
AttachmentControlComponent
], ],
providers: [ providers: [
{ provide: DateAdapter, useClass: OpenSlidesDateAdapter }, { provide: DateAdapter, useClass: OpenSlidesDateAdapter },

View File

@ -121,9 +121,7 @@
<os-search-value-selector <os-search-value-selector
class="search-users" class="search-users"
ngDefaultControl ngDefaultControl
[form]="addSpeakerForm"
[formControl]="addSpeakerForm.get('user_id')" [formControl]="addSpeakerForm.get('user_id')"
[multiple]="false"
listname="{{ 'Select or search new speaker ...' | translate }}" listname="{{ 'Select or search new speaker ...' | translate }}"
[InputListValues]="filteredUsers" [InputListValues]="filteredUsers"
></os-search-value-selector> ></os-search-value-selector>

View File

@ -195,7 +195,6 @@
<os-search-value-selector <os-search-value-selector
class="search-bar" class="search-bar"
ngDefaultControl ngDefaultControl
[form]="candidatesForm"
[formControl]="candidatesForm.get('userId')" [formControl]="candidatesForm.get('userId')"
[multiple]="false" [multiple]="false"
listname="{{ 'Select a new candidate' | translate }}" listname="{{ 'Select a new candidate' | translate }}"
@ -259,7 +258,6 @@
<div class="content-field" *ngIf="tagsAvailable"> <div class="content-field" *ngIf="tagsAvailable">
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="assignmentForm"
[formControl]="assignmentForm.get('tags_id')" [formControl]="assignmentForm.get('tags_id')"
[multiple]="true" [multiple]="true"
[includeNone]="true" [includeNone]="true"
@ -269,15 +267,8 @@
</div> </div>
<!-- Attachments --> <!-- Attachments -->
<div class="content-field" *ngIf="mediafilesAvailable"> <div class="content-field">
<os-search-value-selector <os-attachment-control (errorHandler)="raiseError($event)" [controlName]="assignmentForm.get('attachments_id')"></os-attachment-control>
ngDefaultControl
[form]="assignmentForm"
[formControl]="assignmentForm.get('attachments_id')"
[multiple]="true"
listname="{{ 'Election documents' | translate }}"
[InputListValues]="mediafilesObserver"
></os-search-value-selector>
</div> </div>
<os-agenda-content-object-form *ngIf="newAssignment" [form]="assignmentForm"></os-agenda-content-object-form> <os-agenda-content-object-form *ngIf="newAssignment" [form]="assignmentForm"></os-agenda-content-object-form>

View File

@ -16,7 +16,6 @@
<span> <span>
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="modelSelectForm"
[formControl]="modelSelectForm.get('model')" [formControl]="modelSelectForm.get('model')"
[multiple]="false" [multiple]="false"
[includeNone]="false" [includeNone]="false"

View File

@ -21,7 +21,6 @@
<p> <p>
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="createForm"
[formControl]="this.createForm.get('read_groups_id')" [formControl]="this.createForm.get('read_groups_id')"
[multiple]="true" [multiple]="true"
listname="Groups with read permissions" listname="Groups with read permissions"
@ -31,7 +30,6 @@
<p> <p>
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="createForm"
[formControl]="this.createForm.get('write_groups_id')" [formControl]="this.createForm.get('write_groups_id')"
[multiple]="true" [multiple]="true"
listname="Groups with write permissions" listname="Groups with write permissions"
@ -93,7 +91,6 @@
<p> <p>
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="updateForm"
[formControl]="this.updateForm.get('read_groups_id')" [formControl]="this.updateForm.get('read_groups_id')"
[multiple]="true" [multiple]="true"
listname="Groups with read permissions" listname="Groups with read permissions"
@ -103,7 +100,6 @@
<p> <p>
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="updateForm"
[formControl]="this.updateForm.get('write_groups_id')" [formControl]="this.updateForm.get('write_groups_id')"
[multiple]="true" [multiple]="true"
listname="Groups with write permissions" listname="Groups with write permissions"

View File

@ -37,9 +37,7 @@
<os-search-value-selector <os-search-value-selector
class="search-users" class="search-users"
ngDefaultControl ngDefaultControl
[form]="addSubmitterForm"
[formControl]="addSubmitterForm.get('userId')" [formControl]="addSubmitterForm.get('userId')"
[multiple]="false"
listname="{{ 'Select or search new submitter ...' | translate }}" listname="{{ 'Select or search new submitter ...' | translate }}"
[InputListValues]="users" [InputListValues]="users"
></os-search-value-selector> ></os-search-value-selector>

View File

@ -581,7 +581,6 @@
<div *ngIf="perms.isAllowed('change_metadata', motion)"> <div *ngIf="perms.isAllowed('change_metadata', motion)">
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="contentForm"
[formControl]="contentForm.get('submitters_id')" [formControl]="contentForm.get('submitters_id')"
[multiple]="true" [multiple]="true"
listname="{{ 'Submitters' | translate }}" listname="{{ 'Submitters' | translate }}"
@ -763,9 +762,7 @@
<div class="content-field" *ngIf="newMotion && categoryObserver.value.length > 0"> <div class="content-field" *ngIf="newMotion && categoryObserver.value.length > 0">
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="contentForm"
[formControl]="contentForm.get('category_id')" [formControl]="contentForm.get('category_id')"
[multiple]="false"
[includeNone]="true" [includeNone]="true"
listname="{{ 'Category' | translate }}" listname="{{ 'Category' | translate }}"
[InputListValues]="categoryObserver" [InputListValues]="categoryObserver"
@ -783,24 +780,8 @@
</mat-list-item> </mat-list-item>
</mat-list> </mat-list>
</div> </div>
<div *osPerms="'motions.can_manage'; and: editMotion" class="shortened-selector"> <div *osPerms="'motions.can_manage'; and: editMotion">
<os-search-value-selector <os-attachment-control (errorHandler)="showUploadError($event)" [controlName]="contentForm.get('attachments_id')"></os-attachment-control>
class="selector"
ngDefaultControl
[form]="contentForm"
[formControl]="contentForm.get('attachments_id')"
[multiple]="true"
listname="{{ 'Attachments' | translate }}"
[InputListValues]="mediafilesObserver"
></os-search-value-selector>
<button
type="button"
mat-icon-button
(click)="onUploadAttachmentsButton(uploadDialog)"
*osPerms="'mediafiles.can_upload'"
>
<mat-icon>cloud_upload</mat-icon>
</button>
</div> </div>
</div> </div>
@ -813,7 +794,6 @@
<div *ngIf="perms.isAllowed('change_metadata', motion)"> <div *ngIf="perms.isAllowed('change_metadata', motion)">
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="contentForm"
[formControl]="contentForm.get('supporters_id')" [formControl]="contentForm.get('supporters_id')"
[multiple]="true" [multiple]="true"
listname="{{ 'Supporters' | translate }}" listname="{{ 'Supporters' | translate }}"
@ -827,9 +807,7 @@
<div *ngIf="perms.isAllowed('change_metadata', motion)"> <div *ngIf="perms.isAllowed('change_metadata', motion)">
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="contentForm"
[formControl]="contentForm.get('workflow_id')" [formControl]="contentForm.get('workflow_id')"
[multiple]="false"
listname="{{ 'Workflow' | translate }}" listname="{{ 'Workflow' | translate }}"
[InputListValues]="workflowObserver" [InputListValues]="workflowObserver"
></os-search-value-selector> ></os-search-value-selector>
@ -966,14 +944,3 @@
Final print template Final print template
</button> </button>
</mat-menu> </mat-menu>
<!-- upload file dialog -->
<ng-template #uploadDialog>
<h1 mat-dialog-title>
<span translate>Upload files</span>
</h1>
<os-media-upload-content
(uploadSuccessEvent)="uploadSuccess($event)"
(errorEvent)="showUploadError($event)"
></os-media-upload-content>
</ng-template>

View File

@ -231,14 +231,3 @@ span {
font-size: 12px; font-size: 12px;
margin-top: 4px; margin-top: 4px;
} }
.shortened-selector {
justify-content: space-between;
display: flex;
.selector {
width: 95%;
}
.mat-icon-button {
top: 20px;
}
}

View File

@ -1,5 +1,5 @@
import { ActivatedRoute, Router, NavigationEnd } from '@angular/router'; import { ActivatedRoute, Router, NavigationEnd } from '@angular/router';
import { Component, OnInit, OnDestroy, ElementRef, HostListener, TemplateRef } from '@angular/core'; import { Component, OnInit, OnDestroy, ElementRef, HostListener } from '@angular/core';
import { DomSanitizer, SafeHtml, Title } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml, Title } from '@angular/platform-browser';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms'; import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { MatCheckboxChange } from '@angular/material/checkbox'; import { MatCheckboxChange } from '@angular/material/checkbox';
@ -20,7 +20,6 @@ import { ItemRepositoryService } from 'app/core/repositories/agenda/item-reposit
import { LinenumberingService } from 'app/core/ui-services/linenumbering.service'; import { LinenumberingService } from 'app/core/ui-services/linenumbering.service';
import { LocalPermissionsService } from 'app/site/motions/services/local-permissions.service'; import { LocalPermissionsService } from 'app/site/motions/services/local-permissions.service';
import { Mediafile } from 'app/shared/models/mediafiles/mediafile'; import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
import { Motion } from 'app/shared/models/motions/motion'; import { Motion } from 'app/shared/models/motions/motion';
import { import {
MotionChangeRecommendationDialogComponentData, MotionChangeRecommendationDialogComponentData,
@ -56,7 +55,6 @@ import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
import { ViewCategory } from 'app/site/motions/models/view-category'; import { ViewCategory } from 'app/site/motions/models/view-category';
import { ViewCreateMotion } from 'app/site/motions/models/view-create-motion'; import { ViewCreateMotion } from 'app/site/motions/models/view-create-motion';
import { ViewportService } from 'app/core/ui-services/viewport.service'; import { ViewportService } from 'app/core/ui-services/viewport.service';
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-motion-change-recommendation'; import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-motion-change-recommendation';
import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph'; import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph';
import { ViewTag } from 'app/site/tags/models/view-tag'; import { ViewTag } from 'app/site/tags/models/view-tag';
@ -250,11 +248,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
*/ */
public blockObserver: BehaviorSubject<ViewMotionBlock[]>; public blockObserver: BehaviorSubject<ViewMotionBlock[]>;
/**
* Subject for mediafiles
*/
public mediafilesObserver: BehaviorSubject<ViewMediafile[]>;
/** /**
* Subject for tags * Subject for tags
*/ */
@ -445,7 +438,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
private userRepo: UserRepositoryService, private userRepo: UserRepositoryService,
private notifyService: NotifyService, private notifyService: NotifyService,
private tagRepo: TagRepositoryService, private tagRepo: TagRepositoryService,
private mediaFilerepo: MediafileRepositoryService,
private workflowRepo: WorkflowRepositoryService, private workflowRepo: WorkflowRepositoryService,
private blockRepo: MotionBlockRepositoryService, private blockRepo: MotionBlockRepositoryService,
private itemRepo: ItemRepositoryService, private itemRepo: ItemRepositoryService,
@ -462,7 +454,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
public ngOnInit(): void { public ngOnInit(): void {
// get required information from the repositories // get required information from the repositories
this.tagObserver = this.tagRepo.getViewModelListBehaviorSubject(); this.tagObserver = this.tagRepo.getViewModelListBehaviorSubject();
this.mediafilesObserver = this.mediaFilerepo.getViewModelListBehaviorSubject();
this.workflowObserver = this.workflowRepo.getViewModelListBehaviorSubject(); this.workflowObserver = this.workflowRepo.getViewModelListBehaviorSubject();
this.blockObserver = this.blockRepo.getViewModelListBehaviorSubject(); this.blockObserver = this.blockRepo.getViewModelListBehaviorSubject();
this.motionObserver = this.repo.getViewModelListBehaviorSubject(); this.motionObserver = this.repo.getViewModelListBehaviorSubject();
@ -501,17 +492,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
this.configService this.configService
.get<boolean>('motions_show_sequential_numbers') .get<boolean>('motions_show_sequential_numbers')
.subscribe(shown => (this.showSequential = shown)); .subscribe(shown => (this.showSequential = shown));
// disable the selector for attachments if there are none
this.mediafilesObserver.subscribe(() => {
if (this.contentForm) {
const attachmentsCtrl = this.contentForm.get('attachments_id');
if (this.mediafilesObserver.value.length === 0) {
attachmentsCtrl.disable();
} else {
attachmentsCtrl.enable();
}
}
});
// Update statute paragraphs // Update statute paragraphs
this.statuteRepo.getViewModelListObservable().subscribe(newViewStatuteParagraphs => { this.statuteRepo.getViewModelListObservable().subscribe(newViewStatuteParagraphs => {
@ -1546,30 +1526,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
this.personalNoteService.savePersonalNote(this.motion, this.motion.personalNote).then(null, this.raiseError); this.personalNoteService.savePersonalNote(this.motion, this.motion.personalNote).then(null, this.raiseError);
} }
/**
* Handler for the upload attachments button
*/
public onUploadAttachmentsButton(templateRef: TemplateRef<string>): void {
this.dialogService.open(templateRef, {
maxHeight: '90vh',
width: '750px',
maxWidth: '90vw'
});
}
/**
* Handler for successful uploads.
* Adds the IDs of the upload process to the mediafile selector
*
* @param fileIds the ids of the uploads if they were successful
*/
public uploadSuccess(fileIds: number[]): void {
const currentAttachments = this.contentForm.get('attachments_id').value as number[];
const newAttachments = [...currentAttachments, ...fileIds];
this.contentForm.get('attachments_id').setValue(newAttachments);
this.dialogService.closeAll();
}
/** /**
* Handler for upload errors * Handler for upload errors
* *

View File

@ -68,14 +68,7 @@
</div> </div>
<!-- Attachments --> <!-- Attachments -->
<os-search-value-selector <os-attachment-control [controlName]="topicForm.get('attachments_id')" (errorHandler)="raiseError($event)"></os-attachment-control>
ngDefaultControl
[form]="topicForm"
[formControl]="topicForm.get('attachments_id')"
[multiple]="true"
listname="{{ 'Attachments' | translate }}"
[InputListValues]="mediafilesObserver"
></os-search-value-selector>
<div *ngIf="newTopic"> <div *ngIf="newTopic">
<!-- Visibility --> <!-- Visibility -->
@ -93,9 +86,7 @@
<div> <div>
<os-search-value-selector <os-search-value-selector
ngDefaultControl ngDefaultControl
[form]="topicForm"
[formControl]="topicForm.get('agenda_parent_id')" [formControl]="topicForm.get('agenda_parent_id')"
[multiple]="false"
[includeNone]="true" [includeNone]="true"
listname="{{ 'Parent agenda item' | translate }}" listname="{{ 'Parent agenda item' | translate }}"
[InputListValues]="itemObserver" [InputListValues]="itemObserver"

View File

@ -15,10 +15,8 @@ import { BehaviorSubject } from 'rxjs';
import { ItemVisibilityChoices } from 'app/shared/models/agenda/item'; import { ItemVisibilityChoices } from 'app/shared/models/agenda/item';
import { CreateTopic } from '../../models/create-topic'; import { CreateTopic } from '../../models/create-topic';
import { Topic } from 'app/shared/models/topics/topic'; import { Topic } from 'app/shared/models/topics/topic';
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
import { ViewItem } from 'app/site/agenda/models/view-item';
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service'; import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
import { ViewItem } from 'app/site/agenda/models/view-item';
/** /**
* Detail page for topics. * Detail page for topics.
@ -49,11 +47,6 @@ export class TopicDetailComponent extends BaseViewComponent {
*/ */
public topicForm: FormGroup; public topicForm: FormGroup;
/**
* Subject for mediafiles
*/
public mediafilesObserver: BehaviorSubject<ViewMediafile[]>;
/** /**
* Subject for agenda items * Subject for agenda items
*/ */
@ -88,7 +81,6 @@ export class TopicDetailComponent extends BaseViewComponent {
private repo: TopicRepositoryService, private repo: TopicRepositoryService,
private promptService: PromptService, private promptService: PromptService,
private operator: OperatorService, private operator: OperatorService,
private mediafileRepo: MediafileRepositoryService,
private itemRepo: ItemRepositoryService, private itemRepo: ItemRepositoryService,
private sanitizer: DomSanitizer private sanitizer: DomSanitizer
) { ) {
@ -96,7 +88,6 @@ export class TopicDetailComponent extends BaseViewComponent {
this.getTopicByUrl(); this.getTopicByUrl();
this.createForm(); this.createForm();
this.mediafilesObserver = this.mediafileRepo.getViewModelListBehaviorSubject();
this.itemObserver = this.itemRepo.getViewModelListBehaviorSubject(); this.itemObserver = this.itemRepo.getViewModelListBehaviorSubject();
} }