Add editing/creating of ranks

pull/1/head
Florian Hartwich 2017-05-15 15:32:36 +02:00
parent 4c6f3dab78
commit ec756028aa
13 changed files with 252 additions and 174 deletions

View File

@ -0,0 +1,3 @@
.form-control {
height: auto;
}

View File

@ -12,7 +12,7 @@
</div> </div>
<div class="col-xs-3"> <div class="col-xs-3">
<img src="resource/rank/{{rank._id}}.png" class="rank-list-preview"> <img src="{{imageSrc}}" class="rank-list-preview">
<span (click)="delete(); $event.stopPropagation()" title="Löschen" class="glyphicon glyphicon-trash trash"></span> <span (click)="delete(); $event.stopPropagation()" title="Löschen" class="glyphicon glyphicon-trash trash"></span>
</div> </div>

View File

@ -14,6 +14,7 @@ export class RankItemComponent {
selected: boolean; selected: boolean;
rank: Rank; rank: Rank;
imageSrc;
rankSelected = new EventEmitter(); rankSelected = new EventEmitter();
rankDelete = new EventEmitter(); rankDelete = new EventEmitter();
@ -22,6 +23,10 @@ export class RankItemComponent {
} }
ngOnInit() {
this.imageSrc = 'resource/rank/' + this.rank._id + '.png?' + Date.now();
}
select() { select() {
this.rankSelected.emit(this.rank._id) this.rankSelected.emit(this.rank._id)
} }

View File

@ -48,12 +48,13 @@ export class RankListComponent implements OnInit {
} }
openNewRankForm() { openNewRankForm() {
this.selectedRankId = null;
this.router.navigate([{outlets: {'right': ['new']}}], {relativeTo: this.route}); this.router.navigate([{outlets: {'right': ['new']}}], {relativeTo: this.route});
} }
selectRank(rankId: string | number) { selectRank(rankId: string | number) {
this.selectedRankId = rankId; this.selectedRankId = rankId;
this.router.navigate([{outlets: {'right': ['overview', rankId]}}], {relativeTo: this.route}); this.router.navigate([{outlets: {'right': ['new', rankId]}}], {relativeTo: this.route});
} }
filterRanksByFraction(query = '', fractionFilter) { filterRanksByFraction(query = '', fractionFilter) {

View File

@ -0,0 +1,7 @@
.preview-image {
margin: 10px;
}
.form-control {
height: auto;
}

View File

@ -0,0 +1,66 @@
<form #form="ngForm" class="overview">
<h3 *ngIf="rank._id">Rang editieren</h3>
<h3 *ngIf="!rank._id">Neuen Rang hinzufügen</h3>
<div class="form-group">
<label for="title">Name</label>
<input type="text" class="form-control"
[(ngModel)]="rank.name"
name="title"
id="title"
required maxlength="50"/>
<show-error text="Name" path="title"></show-error>
</div>
<div class="form-group">
<label for="fraction">Fraktion</label>
<select id="fraction" name="fraction" class="form-control btn dropdown-toggle"
required
[(ngModel)]="rank.fraction">
<option value="OPFOR">CSAT</option>
<option value="BLUFOR">NATO</option>
</select>
<show-error text="Fraktion" path="fraction"></show-error>
</div>
<div class="form-group">
<label for="level">Stufe</label>
<input id="level" name="level" type="number" class="form-control btn dropdown-toggle"
[(ngModel)]="rank.level">
<show-error text="Stufe" path="level"></show-error>
</div>
<div class="form-group">
<label for="logo">Bild</label>
<input id="logo" name="logo" class="ui-button form-control" type="file"
accept="image/png"
#fileInput
(change)="fileChange($event)">
<span class="label label-bg label-danger center-block" style="font-size:small" *ngIf="showImageError">
Bild muss im PNG Format vorliegen
</span>
<img class="preview-image" src="{{imagePreviewSrc}}">
</div>
<button id="cancel"
(click)="cancel()"
class="btn btn-default">
Abbrechen
</button>
<button id="save"
(click)="saveRank(fileInput)"
class="btn btn-default"
[disabled]="!form.valid">
Squad speichern
</button>
<span *ngIf="showSuccessLabel"
class="label label-success label-small"
style="margin-left: inherit">
Erfolgreich gespeichert
</span>
</form>

View File

@ -0,0 +1,105 @@
import {Component, ViewChild} from "@angular/core";
import {ActivatedRoute, Router} from "@angular/router";
import {NgForm} from "@angular/forms";
import {Rank} from "../../models/model-interfaces";
import {RankService} from "../../services/rank-service/rank.service";
import {Subscription} from "rxjs/Subscription";
@Component({
templateUrl: './new-rank.component.html',
styleUrls: ['./new-rank.component.css', '../../style/new-entry-form.css']
})
export class CreateRankComponent {
subscription: Subscription;
rank: Rank = {name: '', fraction: '', level: 0};
fileList: FileList;
saved = false;
showImageError = false;
imagePreviewSrc;
showSuccessLabel = false;
@ViewChild(NgForm) form: NgForm;
constructor(private route: ActivatedRoute,
private router: Router,
private rankService : RankService) {
}
ngOnInit() {
this.subscription = this.route.params
.map(params => params['id'])
.filter(id => id != undefined)
.flatMap(id => this.rankService.getRank(id))
.subscribe(rank => {
this.rank = rank;
this.imagePreviewSrc = 'resource/rank/' + this.rank._id + '.png?' + Date.now();
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
fileChange(event) {
if (!event.target.files[0].name.endsWith('.png')) {
this.showImageError = true;
this.fileList = undefined;
} else {
this.showImageError = false;
this.fileList = event.target.files;
}
}
saveRank(fileInput) {
let file: File;
if (!this.rank._id) {
if (this.fileList) {
file = this.fileList[0];
this.rankService.submitRank(this.rank, file)
.subscribe(rank => {
this.saved = true;
this.router.navigate(['..'], {relativeTo: this.route});
})
} else {
return window.alert(`Bild ist ein Pflichtfeld`);
}
} else {
if (this.fileList) {
file = this.fileList[0];
}
delete this.rank['__v'];
this.rankService.submitRank(this.rank, file)
.subscribe(rank => {
setTimeout(() => {
this.imagePreviewSrc = 'resource/rank/' + this.rank._id + '.png?' + Date.now();
}, 300);
fileInput.value = '';
this.showSuccessLabel = true;
setTimeout(() => {
this.showSuccessLabel = false;
}, 2000)
})
}
}
cancel() {
this.router.navigate([this.rank._id ? '../..' : '..'], {relativeTo: this.route});
return false;
}
canDeactivate(): boolean {
if (this.saved || !this.form.dirty) {
return true;
}
return window.confirm(`Ihr Formular besitzt ungespeicherte Änderungen, möchten Sie die Seite wirklich verlassen?`);
}
}

View File

@ -1,79 +0,0 @@
<div class="overview">
<h3 style="margin-bottom: 25px">Rang-Details
<span *ngIf="showSuccessLabel"
class="label label-success label-small"
style="margin-left: inherit">
Erfolgreich gespeichert
</span>
</h3>
<div *ngIf="rank">
<div class="col-xs-12">
<div class="div-table">
<div class="div-table-row">
<div class="div-table-col content-s">
<label>Name:</label>
</div>
<div class="div-table-col content content-m">
{{rank.name}}
</div>
<div class="div-table-col content-l">
<input class="form-control" width="250px" placeholder="Neuer Name" #newNameInput>
</div>
<div class="div-table-col content-s">
<a class="pull-right btn btn-sm btn-block btn-default" (click)="update('name', newNameInput)">Bestätigen</a>
</div>
</div>
<div class="div-table-row">
<div class="div-table-col content-s">
<label>Fraktion:</label>
</div>
<div class="div-table-col fraction-opfor content-m" *ngIf="rank.fraction == 'OPFOR'">
CSAT
</div>
<div class="div-table-col fraction-blufor content-m" *ngIf="rank.fraction == 'BLUFOR'">
NATO
</div>
<div class="div-table-col">
</div>
</div>
<div class="div-table-row">
<div class="div-table-col content-s">
<label>Stufe:</label>
</div>
<div class="div-table-col content content-m">
{{rank.level}}
</div>
<div class="div-table-col">
</div>
</div>
</div>
<hr>
<div class="div-table">
<div class="div-table-row">
<div class="div-table-col content-s">
<label>Bild:</label>
</div>
<div class="div-table-col content-m-flex">
<img src="{{imagePreview}}">
</div>
<div class="div-table-col content-l">
<label class="control-label">Neues Logo</label>
<input class="form-control" type="file" accept="image/png" #newGraphicInput (change)="fileChange($event)">
<span class="label label-bg label-danger center-block" style="font-size:small" *ngIf="showImageError">
Bild muss im PNG Format vorliegen
</span>
</div>
<div class="div-table-col content-s">
<label>&nbsp;</label>
<a class="pull-right btn btn-sm btn-block btn-default" (click)="updateGraphic(newGraphicInput)">Bestätigen</a>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,86 +0,0 @@
import {Component} from "@angular/core";
import {ActivatedRoute} from "@angular/router";
import {Rank} from "../../models/model-interfaces";
import {RankService} from "../../services/rank-service/rank.service";
@Component({
templateUrl: './rank-overview.component.html',
styleUrls: ['./rank-overview.component.css', '../../style/overview.css'],
})
export class RankOverviewComponent {
showSuccessLabel = false;
showImageError = false;
rank: Rank;
fileList: FileList;
imagePreview;
constructor(private route: ActivatedRoute,
private rankService: RankService) {
}
ngOnInit() {
this.route.params.subscribe((params) => {
this.rankService.getRank(params['id']).subscribe(rank => {
this.rank = rank;
this.imagePreview = 'resource/rank/' + rank._id + '.png?' + Date.now();
})
})
}
/**
* register change on file input and save to local fileList
* @param event
*/
fileChange(event) {
if (!event.target.files[0].name.endsWith('.png')) {
this.showImageError = true;
this.fileList = undefined;
} else {
this.showImageError = false;
this.fileList = event.target.files;
}
}
update(attrName, inputField) {
const inputValue = inputField.value;
if (inputValue.length > 0 && this.rank[attrName] !== inputValue) {
const updateObject = {_id: this.rank._id};
updateObject[attrName] = inputValue;
this.rankService.updateRank(updateObject)
.subscribe(rank => {
this.rank = rank;
inputField.value = '';
this.showSuccessLabel = true;
setTimeout(() => {
this.showSuccessLabel = false;
}, 2000)
})
}
}
updateGraphic(fileInput) {
if (this.fileList && this.fileList.length > 0) {
let file: File = this.fileList[0];
this.rankService.updateRankGraphic(this.rank._id, file)
.subscribe((res) => {
setTimeout(() => {
this.imagePreview = 'resource/rank/' + this.rank._id + '.png?' + Date.now();
}, 300);
fileInput.value = '';
this.showSuccessLabel = true;
setTimeout(() => {
this.showSuccessLabel = false;
}, 2000)
})
}
}
}

View File

@ -1,7 +1,7 @@
import {Routes} from "@angular/router"; import {Routes} from "@angular/router";
import {RankComponent} from "./ranks.component"; import {RankComponent} from "./ranks.component";
import {RankListComponent} from "./rank-list/rank-list.component"; import {RankListComponent} from "./rank-list/rank-list.component";
import {RankOverviewComponent} from "./rank-overview/rank-overview.component"; import {CreateRankComponent} from "./rank-new/new-rank.component";
export const ranksRoutes: Routes = [{ export const ranksRoutes: Routes = [{
@ -14,10 +14,15 @@ export const ranksRoutes: Routes = [{
] ]
}, },
{ {
path: 'overview/:id', path: 'new',
component: RankOverviewComponent, component: CreateRankComponent,
outlet: 'right'
},
{
path: 'new/:id',
component: CreateRankComponent,
outlet: 'right' outlet: 'right'
}]; }];
export const ranksRoutingComponents = [RankComponent, RankListComponent, RankOverviewComponent]; export const ranksRoutingComponents = [RankComponent, RankListComponent, CreateRankComponent];

View File

@ -3,7 +3,7 @@ import {Decoration, Rank} from "../../models/model-interfaces";
import {RequestMethod, RequestOptions, URLSearchParams} from "@angular/http"; import {RequestMethod, RequestOptions, URLSearchParams} from "@angular/http";
import {Observable} from "rxjs/Observable"; import {Observable} from "rxjs/Observable";
import {LOAD} from "../stores/decoration.store"; import {LOAD} from "../stores/decoration.store";
import {EDIT, RankStore, REMOVE} from "../stores/rank.store"; import {ADD, EDIT, RankStore, REMOVE} from "../stores/rank.store";
import {AppConfig} from "../../app.config"; import {AppConfig} from "../../app.config";
import {HttpClient} from "../http-client"; import {HttpClient} from "../http-client";
@ -29,8 +29,8 @@ export class RankService {
this.http.get(this.config.apiUrl + this.config.apiRankPath, searchParams) this.http.get(this.config.apiUrl + this.config.apiRankPath, searchParams)
.map(res => res.json()) .map(res => res.json())
.do((squads) => { .do((ranks) => {
this.rankStore.dispatch({type: LOAD, data: squads}); this.rankStore.dispatch({type: LOAD, data: ranks});
}).subscribe(_ => { }).subscribe(_ => {
}); });
@ -42,6 +42,54 @@ export class RankService {
.map(res => res.json()); .map(res => res.json());
} }
/**
* For creating new data with POST or
* update existing with patch PATCH
*/
submitRank(rank: Rank, imageFile?) {
let requestUrl = this.config.apiUrl + this.config.apiRankPath;
let requestMethod: RequestMethod;
let accessType;
let body;
if (rank._id) {
requestUrl += rank._id;
requestMethod = RequestMethod.Patch;
accessType = EDIT;
} else {
requestMethod = RequestMethod.Post;
accessType = ADD;
}
if (imageFile) {
body = new FormData();
Object.keys(rank).map((objectKey) => {
if (rank[objectKey] !== undefined) {
body.append(objectKey, rank[objectKey]);
}
});
body.append('image', imageFile, imageFile.name);
} else {
body = rank;
}
const options = new RequestOptions({
body: body,
method: requestMethod
});
return this.http.request(requestUrl, options)
.map(res => res.json())
.do(savedRank => {
const action = {type: accessType, data: savedRank};
// leave some time to save image file before accessing it through listview
setTimeout(() => {
this.rankStore.dispatch(action);
}, 300);
});
}
/** /**
* send PATCH request to update db entry * send PATCH request to update db entry
* *

View File

@ -0,0 +1,3 @@
.form-control {
height: auto;
}