mirror of
https://github.com/ChronosX88/InstructorsListAppTA.git
synced 2024-12-04 22:52:19 +00:00
Merge branch 'feat/ui-data-binding'
This commit is contained in:
commit
882df64bca
10595
ClientApp/package-lock.json
generated
10595
ClientApp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
@ -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' }),
|
||||
|
0
ClientApp/src/app/home/home.component.css
Normal file
0
ClientApp/src/app/home/home.component.css
Normal 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>
|
||||
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
7
ClientApp/src/app/models/instructor.ts
Normal file
7
ClientApp/src/app/models/instructor.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export class Instructor {
|
||||
constructor(
|
||||
public id?: string,
|
||||
public firstName?: string,
|
||||
public middleName?: string,
|
||||
public lastName?: string) { }
|
||||
}
|
25
ClientApp/src/app/services/instructors-http-service.ts
Normal file
25
ClientApp/src/app/services/instructors-http-service.ts
Normal 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)
|
||||
}
|
||||
}
|
21
ClientApp/src/app/utils/ngbd-sortable-header.css
Normal file
21
ClientApp/src/app/utils/ngbd-sortable-header.css
Normal 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)
|
||||
}
|
29
ClientApp/src/app/utils/ngbd-sortable-header.ts
Normal file
29
ClientApp/src/app/utils/ngbd-sortable-header.ts
Normal 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});
|
||||
}
|
||||
}
|
@ -15,6 +15,11 @@
|
||||
"lib": [
|
||||
"es2017",
|
||||
"dom"
|
||||
]
|
||||
],
|
||||
"paths": {
|
||||
"core-js/es7/reflect": [
|
||||
"node_modules/core-js/proposals/reflect-metadata"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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!"));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user