Merge branch 'feat/ui-data-binding'

This commit is contained in:
ChronosX88 2019-09-26 01:00:40 +04:00
commit 882df64bca
Signed by: ChronosXYZ
GPG Key ID: 085A69A82C8C511A
12 changed files with 221 additions and 10646 deletions

10595
ClientApp/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,42 +12,42 @@
},
"private": true,
"dependencies": {
"@angular/animations": "6.1.10",
"@angular/common": "6.1.10",
"@angular/compiler": "6.1.10",
"@angular/core": "6.1.10",
"@angular/forms": "6.1.10",
"@angular/http": "6.1.10",
"@angular/platform-browser": "6.1.10",
"@angular/platform-browser-dynamic": "6.1.10",
"@angular/platform-server": "6.1.10",
"@angular/router": "6.1.10",
"@nguniversal/module-map-ngfactory-loader": "6.0.0",
"core-js": "^2.5.4",
"rxjs": "^6.0.0",
"zone.js": "^0.8.26",
"@angular/animations": "8.2.8",
"@angular/common": "8.2.8",
"@angular/compiler": "8.2.8",
"@angular/core": "8.2.8",
"@angular/forms": "8.2.8",
"@angular/http": "7.2.15",
"@angular/platform-browser": "8.2.8",
"@angular/platform-browser-dynamic": "8.2.8",
"@angular/platform-server": "8.2.8",
"@angular/router": "8.2.8",
"@nguniversal/module-map-ngfactory-loader": "8.1.1",
"core-js": "^3.2.1",
"rxjs": "^6.5.3",
"zone.js": "^0.10.2",
"aspnet-prerendering": "^3.0.1",
"bootstrap": "^4.3.1",
"jquery": "3.3.1",
"popper.js": "^1.14.3"
"jquery": "3.4.1",
"popper.js": "^1.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.6.0",
"@angular/cli": "~6.0.0",
"@angular/compiler-cli": "6.1.10",
"@angular/language-service": "^6.0.0",
"@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "~4.2.1",
"jasmine-core": "~2.99.1",
"@angular-devkit/build-angular": "~0.803.6",
"@angular/cli": "~8.3.6",
"@angular/compiler-cli": "8.2.8",
"@angular/language-service": "^8.2.8",
"@types/jasmine": "~3.4.0",
"@types/jasminewd2": "~2.0.6",
"@types/node": "~12.7.7",
"codelyzer": "~5.1.1",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "^3.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~1.4.2",
"karma-jasmine": "~1.1.1",
"karma-jasmine-html-reporter": "^0.2.2",
"typescript": "~2.7.2"
"karma": "^4.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~2.1.0",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.2",
"typescript": "^3.5.3"
},
"optionalDependencies": {
"node-sass": "^4.12.0",

View File

@ -7,12 +7,14 @@ import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { NavMenuComponent } from './nav-menu/nav-menu.component';
import { HomeComponent } from './home/home.component';
import { NgbdSortableHeader } from './utils/ngbd-sortable-header';
@NgModule({
declarations: [
AppComponent,
NavMenuComponent,
HomeComponent,
NgbdSortableHeader
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),

View File

@ -1,17 +1,36 @@
<div style="text-align: center;"><h1>Add/Edit Instructor</h1></div>
<table class='table table-striped'>
<table class='table table-striped table-bordered table-sm'>
<thead>
<tr>
<th>First name</th>
<th>Last Name</th>
<th>DELETE</th>
<th scope="col" sortable="firstName" (sort)="onSort($event)">First Name</th>
<th scope="col" sortable="lastName" (sort)="onSort($event)">Last Name</th>
<th>Manage</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let instructor of instructors">
<td>{{ instructor.firstName }}</td>
<td>{{ instructor.lastName }}</td>
<td><button type="button" class="btn btn-danger">DELETE</button></td>
<td>
<ng-template [ngIf]="changingInstructor?.id != instructor.id" [ngIfElse]="edit">
<button type="button" class="btn btn-primary" style="margin-right: 10px" (click)="setChangingInstructor(instructor)">Change</button>
</ng-template>
<button type="button" class="btn btn-danger" (click)="deleteInstructor(instructor)">DELETE</button>
</td>
</tr>
</tbody>
</table>
</table>
<ng-template #edit>
<button type="button" class="btn btn-primary" style="margin-right: 10px" (click)="clearChangingInstructor(true)">Cancel edit</button>
</ng-template>
<div style="text-align: center;"><h1>Instructor Details</h1>
<br>
<label><h5 style="text-align: initial">First Name</h5><input type="text" style="margin-right: 20px" [(ngModel)]="changingInstructor.firstName"></label>
<label><h5 style="text-align: initial">Middle Name</h5><input type="text" style="margin-right: 20px" [(ngModel)]="changingInstructor.middleName"></label>
<label><h5 style="text-align: initial">Last Name</h5><input type="text" [(ngModel)]="changingInstructor.lastName"></label>
<br>
<button type="button" class="btn btn-primary" (click)="saveChanges()">ADD/SAVE</button>
</div>

View File

@ -1,22 +1,84 @@
import { Component, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Component, Inject, QueryList, ViewChildren } from '@angular/core';
import { Instructor } from '../models/instructor';
import { InstructorsHttpService } from '../services/instructors-http-service';
import { NgbdSortableHeader, SortEvent, compare } from '../utils/ngbd-sortable-header';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['../utils/ngbd-sortable-header.css'],
providers: [InstructorsHttpService]
})
export class HomeComponent {
public instructors: any[] = [{id: "1", firstName: "Test", lastName: "Testov"}];
public instructors: Instructor[];
private changingInstructor: Instructor = new Instructor();
private savedInstructor: Instructor = new Instructor();
constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
/*http.get<Instructor[]>(baseUrl + 'api/v1/instructors').subscribe(result => {
this.instructors = result;
}, error => console.error(error));*/
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>
constructor(private instructorsService: InstructorsHttpService) {}
ngOnInit() {
this.loadInstructors()
}
}
interface Instructor {
id: string,
firstName: string,
lastName: string
private loadInstructors() {
this.instructorsService.getInstructors().subscribe(result => {
this.instructors = result
})
}
setChangingInstructor(instructor: Instructor) {
Object.assign(this.savedInstructor, instructor)
this.changingInstructor = instructor
}
clearChangingInstructor(isUndo: boolean) {
if(isUndo) {
// undo edit changes
Object.assign(this.changingInstructor, this.savedInstructor) // copy previous object
}
this.changingInstructor = new Instructor() // remove ref
this.savedInstructor = new Instructor()
}
saveChanges() {
if(this.changingInstructor.id == null) {
console.log("Creating instructor...")
this.instructorsService.createInstructor(this.changingInstructor).subscribe(result => {
this.instructors.push(result)
console.log("Instructor successfully created")
})
} else {
console.log("Updating instructor...")
this.instructorsService.updateInstructor(this.changingInstructor).subscribe(_ => {
console.log("Instructor successfully updated")
})
}
this.clearChangingInstructor(false)
}
deleteInstructor(instructor: Instructor) {
this.instructorsService.deleteInstructor(instructor.id).subscribe(_ =>{
this.instructors.splice(this.instructors.indexOf(instructor), 1)
})
}
onSort({column, direction}: SortEvent) {
// resetting other headers
this.headers.forEach(header => {
if (header.sortable !== column) {
header.direction = '';
}
});
if(direction != '') {
this.instructors.sort((a, b) => {
const res = compare(a[column], b[column]);
return direction === 'asc' ? res : -res;
});
}
}
}

View File

@ -0,0 +1,7 @@
export class Instructor {
constructor(
public id?: string,
public firstName?: string,
public middleName?: string,
public lastName?: string) { }
}

View File

@ -0,0 +1,25 @@
import { Injectable, Inject } from '@angular/core';
import { HttpClient} from '@angular/common/http';
import { Instructor } from '../models/instructor';
import { ResponseType } from '@angular/http';
@Injectable()
export class InstructorsHttpService {
constructor(private http: HttpClient, @Inject('BASE_URL') private baseUrl: string) {}
getInstructors() {
return this.http.get<Instructor[]>(this.baseUrl + "api/v1/instructors")
}
createInstructor(instructor: Instructor) {
return this.http.post<Instructor>(this.baseUrl + "api/v1/instructors/add", instructor)
}
updateInstructor(instructor: Instructor) {
return this.http.put(this.baseUrl + "api/v1/instructors/" + instructor.id, instructor)
}
deleteInstructor(id: string) {
return this.http.delete(this.baseUrl + "api/v1/instructors/" + id)
}
}

View File

@ -0,0 +1,21 @@
th[sortable] {
cursor:pointer;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
-webkit-user-select:none
}
th[sortable].asc:after,
th[sortable].desc:after {
content:'';
display:block;
background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAmxJREFUeAHtmksrRVEUx72fH8CIGQNJkpGUUmakDEiZSJRIZsRQmCkTJRmZmJgQE0kpX0D5DJKJgff7v+ru2u3O3vvc67TOvsdatdrnnP1Y///v7HvvubdbUiIhBISAEBACQkAICAEhIAQ4CXSh2DnyDfmCPEG2Iv9F9MPlM/LHyAecdyMzHYNwR3fdNK/OH9HXl1UCozD24TCvILxizEDWIEzA0FcM8woCgRrJCoS5PIwrANQSMAJX1LEI9bqpQo4JYNFFKRSvIgsxHDVnqZgIkPnNBM0rIGtYk9YOOsqgbgepRCfdbmFtqhFkVEDVPjJp0+Z6e6hRHhqBKgg6ZDCvYBygVmUoEGoh5JTRvIJwhJo1aUOoh4CLPMyvxxi7EWOMgnCGsXXI1GIXlZUYX7ucU+kbR8NW8lh3O7cue0Pk32MKndfUxQFAwxdirk3fHappAnc0oqDPzDfGTBrCfHP04dM4oTV8cxr0SVzH9FF07xD3ib6xCDE+M+aUcVygtWzzbtGX2rPBrEUYfecfQkaFzYi6HjVnGBdtL7epqAlc1+jRdAap74RrnPc4BCijttY2tRcdN0g17w7HqZrXhdJTYAuS3hd8z+vKgK3V1zWPae0mZDMykadBn1hTQBLnZNwVrJpSe/NwEeDsEwCctEOsJTsgxLvCqUl2ACftEGvJDgjxrnBqkh3ASTvEWrIDQrwrnJpkB3DSDrGW7IAQ7wqnJtkBnLRztejXXVu4+mxz/nQ9jR1w5VB86ejLTFcnnDwhzV+F6T+CHZlx6THSjn76eyyBIOPHyDakhBAQAkJACAgBISAEhIAQYCLwC8JxpAmsEGt6AAAAAElFTkSuQmCC) 0 0/22px no-repeat;
width:22px;
height:22px;
float:left;
}
th[sortable].desc:after {
-webkit-transform:rotate(180deg);
transform:rotate(180deg);
-ms-transform:rotate(180deg)
}

View File

@ -0,0 +1,29 @@
import { Directive, Input, Output, EventEmitter } from '@angular/core'
export type SortDirection = 'asc' | 'desc' | '';
const rotate: {[key: string]: SortDirection} = { 'asc': 'desc', 'desc': '', '': 'asc' };
export const compare = (v1, v2) => v1 < v2 ? -1 : v1 > v2 ? 1 : 0;
export interface SortEvent {
column: string;
direction: SortDirection;
}
@Directive({
selector: 'th[sortable]',
host: {
'[class.asc]': 'direction === "asc"',
'[class.desc]': 'direction === "desc"',
'(click)': 'rotate()'
},
})
export class NgbdSortableHeader {
@Input() sortable: string;
@Input() direction: SortDirection = '';
@Output() sort = new EventEmitter<SortEvent>();
rotate() {
this.direction = rotate[this.direction];
this.sort.emit({column: this.sortable, direction: this.direction});
}
}

View File

@ -15,6 +15,11 @@
"lib": [
"es2017",
"dom"
]
],
"paths": {
"core-js/es7/reflect": [
"node_modules/core-js/proposals/reflect-metadata"
]
}
}
}

View File

@ -43,7 +43,7 @@ namespace InstructorsListApp.Controllers
instructor.Id = Guid.NewGuid().ToString();
databaseContext.Instructors.Add(instructor);
databaseContext.SaveChanges();
return Ok(instructor.Id);
return Ok(instructor);
}
return BadRequest(new Error(Error.PostBodyIsNotValid, "Given model isn't valid!"));
}