Skip to content

Commit bf59bed

Browse files
author
Alain BOUDARD
committed
bootstrap + fontawesome + logger + components
1 parent 2ed9268 commit bf59bed

32 files changed

+438
-16
lines changed

angular.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
"apps/todosws/src/favicon.ico",
2727
"apps/todosws/src/assets"
2828
],
29-
"styles": ["apps/todosws/src/styles.scss"],
29+
"styles": [
30+
"apps/todosws/src/assets/vendor.scss",
31+
"apps/todosws/src/assets/main.scss"
32+
],
3033
"scripts": []
3134
},
3235
"configurations": {

apps/api/src/app/app.module.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
import { Module } from '@nestjs/common';
1+
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
22
import { AppController } from './app.controller';
33
import { AppService } from './app.service';
44
import { TodoModule } from './todo/todo.module';
5-
5+
import { LoggerMiddleware } from './common/logger.middleware';
66

77
@Module({
88
imports: [TodoModule],
99
controllers: [AppController],
1010
providers: [AppService]
1111
})
12-
export class AppModule {}
12+
export class AppModule implements NestModule {
13+
configure(consumer: MiddlewareConsumer) {
14+
consumer.apply(LoggerMiddleware).forRoutes('todo');
15+
}
16+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { LoggerMiddleware } from './logger.middleware';
2+
3+
describe('LoggerMiddleware', () => {
4+
it('should be defined', () => {
5+
expect(new LoggerMiddleware()).toBeDefined();
6+
});
7+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Injectable, NestMiddleware } from '@nestjs/common';
2+
3+
@Injectable()
4+
export class LoggerMiddleware implements NestMiddleware {
5+
use(req: Request, res: Response, next: Function) {
6+
console.log('request : ' + req.method + ' - ' + req.url);
7+
next();
8+
}
9+
}

apps/api/src/main.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,25 @@
44
**/
55

66
import { NestFactory } from '@nestjs/core';
7-
7+
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
88
import { AppModule } from './app/app.module';
99

1010
async function bootstrap() {
1111
const app = await NestFactory.create(AppModule);
1212
const globalPrefix = 'api';
1313
app.setGlobalPrefix(globalPrefix);
14+
15+
// define setBasePath() so swagger UI can run tests against /api
16+
const options = new DocumentBuilder()
17+
.setTitle('Todo API')
18+
.setDescription('The todo API description')
19+
.setVersion('1.0')
20+
.addTag('todo')
21+
.setBasePath('api')
22+
.build();
23+
const document = SwaggerModule.createDocument(app, options);
24+
SwaggerModule.setup('swagger', app, document);
25+
1426
const port = process.env.port || 3333;
1527
await app.listen(port, () => {
1628
console.log('Listening at http://localhost:' + port + '/' + globalPrefix);

apps/todosws/src/app/app.component.html

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
1-
<div style="text-align:center">
1+
<todosws-navbar></todosws-navbar>
2+
<div class="container">
23
<h1>Welcome to todosws!</h1>
3-
<div>
4-
<span [innerText]="counter$ | async"></span>
4+
<div class="row">
5+
<div class="col-sm-3">
6+
<todosws-counter></todosws-counter>
7+
</div>
8+
<div class="col-sm-9">
9+
<img
10+
src="../assets/img/ngrx-data-nestjs.png"
11+
height="130px"
12+
alt="NgRx Data & NestJS"
13+
/>
14+
<p>
15+
This is an Angular app built with <a href="https://nx.dev">Nx</a>,
16+
<a href="https://docs.nestjs.com/">Nest Server</a> and
17+
<a href="https://ngrx.io/guide/data">NgRx Data</a> in monorepo.
18+
</p>
19+
<todosws-todo-list></todosws-todo-list>
20+
21+
<div>Message: {{ hello$ | async | json }}</div>
22+
</div>
523
</div>
624
</div>
7-
<div>Message: {{ hello$ | async | json }}</div>

apps/todosws/src/app/app.component.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@ import { Observable } from 'rxjs';
1111
})
1212
export class AppComponent implements OnInit {
1313
hello$ = this.http.get<Message>('/api/hello');
14-
counter$: Observable<number>;
15-
constructor(private http: HttpClient, private todoService: TodoService) {
16-
this.counter$ = todoService.count$;
17-
}
14+
15+
constructor(private http: HttpClient, private todoService: TodoService) {}
1816
ngOnInit(): void {
1917
this.todoService.getAll();
2018
}

apps/todosws/src/app/app.module.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,24 @@ import { EntityDataModule } from '@ngrx/data';
88
import { entityConfig } from './store/entity-metadata';
99
import { EffectsModule } from '@ngrx/effects';
1010
import { StoreModule } from '@ngrx/store';
11+
import { NavbarComponent } from './comp/navbar/navbar.component';
12+
import { CounterComponent } from './comp/counter/counter.component';
13+
import { TodoListComponent } from './comp/todo-list/todo-list.component';
14+
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
15+
import { UtilsService } from './services/utils.service';
16+
17+
// font awesome icons
18+
UtilsService.initFaIcons();
1119

1220
@NgModule({
13-
declarations: [AppComponent],
21+
declarations: [
22+
AppComponent,
23+
NavbarComponent,
24+
CounterComponent,
25+
TodoListComponent
26+
],
1427
imports: [
28+
FontAwesomeModule,
1529
BrowserModule,
1630
HttpClientModule,
1731
StoreModule.forRoot([]),

apps/todosws/src/app/comp/counter/counter.component.css

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<p>Counter value from NgRx Data Store :</p>
2+
<div class="alert alert-primary" role="alert">
3+
Todo counter
4+
<span class="badge badge-secondary" [innerText]="counter$ | async"></span>
5+
</div>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { CounterComponent } from './counter.component';
4+
5+
describe('CounterComponent', () => {
6+
let component: CounterComponent;
7+
let fixture: ComponentFixture<CounterComponent>;
8+
9+
beforeEach(async(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ CounterComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(CounterComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import { TodoService } from '../../services/todo.service';
3+
import { Observable } from 'rxjs';
4+
5+
@Component({
6+
selector: 'todosws-counter',
7+
templateUrl: './counter.component.html',
8+
styleUrls: ['./counter.component.css']
9+
})
10+
export class CounterComponent implements OnInit {
11+
counter$: Observable<number>;
12+
constructor(private todosService: TodoService) {
13+
this.counter$ = this.todosService.count$;
14+
}
15+
16+
ngOnInit() {}
17+
}

apps/todosws/src/app/comp/navbar/navbar.component.css

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
2+
<a class="navbar-brand py-0 px-3" href="#" style="">
3+
<img height="50px" src="assets/img/nx-logo-50.png" />
4+
</a>
5+
6+
<button class="navbar-toggler" type="button" (click)="toggleNavbar()">
7+
<span class="navbar-toggler-icon"></span>
8+
</button>
9+
10+
<div class="collapse navbar-collapse" [ngClass]="{ show: navbarOpen }">
11+
<ul class="navbar-nav mr-auto">
12+
<li class="nav-item">
13+
<a class="nav-link" href="#">Item 1</a>
14+
</li>
15+
16+
<li class="nav-item">
17+
<a class="nav-link" href="#">Item 2</a>
18+
</li>
19+
</ul>
20+
</div>
21+
</nav>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { NavbarComponent } from './navbar.component';
4+
5+
describe('NavbarComponent', () => {
6+
let component: NavbarComponent;
7+
let fixture: ComponentFixture<NavbarComponent>;
8+
9+
beforeEach(async(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ NavbarComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(NavbarComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Component, OnInit } from '@angular/core';
2+
3+
@Component({
4+
selector: 'todosws-navbar',
5+
templateUrl: './navbar.component.html',
6+
styleUrls: ['./navbar.component.css']
7+
})
8+
export class NavbarComponent implements OnInit {
9+
10+
navbarOpen = false;
11+
12+
constructor() { }
13+
14+
toggleNavbar() {
15+
this.navbarOpen = !this.navbarOpen;
16+
}
17+
18+
ngOnInit() {
19+
}
20+
21+
}

apps/todosws/src/app/comp/todo-list/todo-list.component.css

Whitespace-only changes.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<table class="table table-hover table-bordered">
2+
<thead class="thead-light">
3+
<tr>
4+
<th scope="col">#</th>
5+
<th scope="col">Title</th>
6+
<th scope="col">Description</th>
7+
<th>&nbsp;</th>
8+
</tr>
9+
</thead>
10+
<tbody>
11+
<tr *ngFor="let todo of (todos$ | async); let i = index">
12+
<th scope="row">
13+
<span [innerText]="todo.id"></span>
14+
</th>
15+
<td>
16+
<span [innerText]="todo.title"></span>
17+
</td>
18+
<td><span [innerText]="todo.description"></span></td>
19+
<td>
20+
<div class="btn-group" role="group" aria-label="Actions">
21+
<button
22+
title="edit"
23+
type="button"
24+
class="btn btn-outline-secondary"
25+
(click)="editTodo(todo.id)"
26+
>
27+
<fa-icon icon="edit"></fa-icon>
28+
</button>
29+
<button
30+
title="delete"
31+
type="button"
32+
class="btn btn-outline-secondary"
33+
(click)="deleteTodo(todo.id)"
34+
>
35+
<fa-icon icon="trash-alt"></fa-icon>
36+
</button>
37+
<button
38+
title="active"
39+
type="button"
40+
class="btn btn-outline-secondary"
41+
>
42+
<fa-icon
43+
*ngIf="todo.active"
44+
(click)="updateTodo(todo)"
45+
icon="check-square"
46+
></fa-icon>
47+
<fa-icon
48+
*ngIf="!todo.active"
49+
(click)="updateTodo(todo)"
50+
icon="square"
51+
></fa-icon>
52+
</button>
53+
</div>
54+
</td>
55+
</tr>
56+
</tbody>
57+
</table>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { TodoListComponent } from './todo-list.component';
4+
5+
describe('TodoListComponent', () => {
6+
let component: TodoListComponent;
7+
let fixture: ComponentFixture<TodoListComponent>;
8+
9+
beforeEach(async(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ TodoListComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(TodoListComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import { TodoService } from '../../services/todo.service';
3+
import { Todo } from '@todosws/api-interface';
4+
import { Observable } from 'rxjs';
5+
6+
@Component({
7+
selector: 'todosws-todo-list',
8+
templateUrl: './todo-list.component.html',
9+
styleUrls: ['./todo-list.component.css']
10+
})
11+
export class TodoListComponent implements OnInit {
12+
todos$: Observable<Todo[]>;
13+
constructor(private todoService: TodoService) {
14+
this.todos$ = todoService.entities$;
15+
}
16+
17+
ngOnInit() {
18+
this.getTodos();
19+
}
20+
getTodos() {
21+
this.todoService.getAll();
22+
}
23+
deleteTodo(todoId: number) {
24+
this.todoService.delete(todoId);
25+
}
26+
27+
updateTodo(todo: Todo) {
28+
todo.active = !todo.active;
29+
this.todoService.update(todo);
30+
}
31+
32+
editTodo(id: number) {
33+
alert('not implemented yet');
34+
}
35+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { UtilsService } from './utils.service';
4+
5+
describe('UtilsService', () => {
6+
beforeEach(() => TestBed.configureTestingModule({}));
7+
8+
it('should be created', () => {
9+
const service: UtilsService = TestBed.get(UtilsService);
10+
expect(service).toBeTruthy();
11+
});
12+
});

0 commit comments

Comments
 (0)