Skip to content
Extraits de code Groupes Projets
Valider 60f35383 rédigé par Simon Defontaine's avatar Simon Defontaine
Parcourir les fichiers

First version

parent 68ca4459
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
Affichage de
avec 351 ajouts et 499 suppressions
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { DownComponent } from './views/down/down.component';
import { ResetComponent } from './views/reset/reset.component';
import { UpComponent } from './views/up/up.component';
const routes: Routes = []; const routes: Routes = [
{ path: 'up', component: UpComponent },
{ path: 'down', component: DownComponent },
{ path: 'reset', component: ResetComponent },
{ path: '', redirectTo: 'up', pathMatch: 'full' }
];
@NgModule({ @NgModule({
imports: [RouterModule.forRoot(routes)], imports: [RouterModule.forRoot(routes)],
......
Ce diff est replié.
.app-container {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
nav {
display: inline-flex;
background-color: blue;
align-items: center;
gap: 8px;
width: 100%;
height: 48px;
padding-left: 8px;
padding-right: 8px;
}
nav a {
color: white;
text-decoration: none;
}
nav a:visited {
color: white;
}
.fill-flex-container {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex-grow: 1;
}
...@@ -19,17 +19,4 @@ describe('AppComponent', () => { ...@@ -19,17 +19,4 @@ describe('AppComponent', () => {
const app = fixture.componentInstance; const app = fixture.componentInstance;
expect(app).toBeTruthy(); expect(app).toBeTruthy();
}); });
it(`should have as title 'papernest-angular-test'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('papernest-angular-test');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain('papernest-angular-test app is running!');
});
}); });
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { BackgroundColorService } from './services/background-color.service';
import { CounterService } from './services/counter.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
...@@ -6,5 +8,7 @@ import { Component } from '@angular/core'; ...@@ -6,5 +8,7 @@ import { Component } from '@angular/core';
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss']
}) })
export class AppComponent { export class AppComponent {
title = 'papernest-angular-test';
/** The main component uses both services to display things in the template. Therefore, make them public. */
constructor(public counter: CounterService, public backgroundColor: BackgroundColorService) { }
} }
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { UpComponent } from './views/up/up.component';
import { DownComponent } from './views/down/down.component';
import { ResetComponent } from './views/reset/reset.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent AppComponent,
UpComponent,
DownComponent,
ResetComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
AppRoutingModule AppRoutingModule,
FormsModule,
ReactiveFormsModule
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]
......
import { TestBed } from '@angular/core/testing';
import { BackgroundColorService } from './background-color.service';
describe('BackgroundColorService', () => {
let service: BackgroundColorService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(BackgroundColorService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { CounterService } from './counter.service';
/**
* @enum Enum containing the different possible background colors.
*/
export enum BackgroundColors {
Green = '#27ae60',
Red = '#e74c3c',
None = ''
}
@Injectable({
providedIn: 'root'
})
/**
* Service providing the functionality for setting a background color according to the counter.
*/
export class BackgroundColorService {
/** @internal the actual Subject to interact on whenever count is set to a new value. */
private backgroundColorSubject: BehaviorSubject<BackgroundColors> = new BehaviorSubject<BackgroundColors>(BackgroundColors.None);
/** The public Observable that will be available for subscription to consumers. */
public backgroundColor$: Observable<string> = this.backgroundColorSubject.asObservable();
constructor(private counter: CounterService) {
// Those are business rules. React to the changes in public count Observable.
this.counter.count$.subscribe({
next: count => {
if (count >= 10) {
this.backgroundColorSubject.next(BackgroundColors.Red);
}
else if (count <= -10) {
this.backgroundColorSubject.next(BackgroundColors.Green);
}
else {
this.backgroundColorSubject.next(BackgroundColors.None);
}
}
});
}
}
import { TestBed } from '@angular/core/testing';
import { CounterService } from './counter.service';
describe('CounterService', () => {
let service: CounterService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CounterService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
/**
* Service providing the functionality for a counter.
*/
export class CounterService {
/** @internal the actual Subject to interact on whenever count must be set to a new value. */
private countSubject: BehaviorSubject<number>;
/** The public Observable that will be available for subscription to consumers. */
public count$: Observable<number>;
/** @internal the number of user actions. */
private actions: number = 0;
/** @internal the value with which the count will be incremented or decremented. Set to 1 as a default. */
private incrementValue: number = 1;
constructor() {
this.countSubject = new BehaviorSubject<number>(this.getSavedCount());
this.count$ = this.countSubject.asObservable();
}
/** @return {number} the saved count. If there is no saved count, or if it is invalid, returns 0 instead. */
private getSavedCount(): number {
const strSaved = localStorage.getItem('count');
if (strSaved === null) {
return 0;
}
else {
return parseInt(strSaved, 10);
}
}
/**
* @param {number} value value at which to set the count.
* Sets the count to value.
*/
private setCount(value: number): void {
this.countSubject.next(value);
localStorage.setItem('count', `${value}`);
}
/** @internal increments the actions counter, and takes necessary actions when it reaches 30. */
private onAction(): void {
this.actions++;
if (this.actions === 30) {
this.actions = 0;
this.incrementValue *= 2;
}
}
/** Increments the count according to the current incrementValue and registers an action. */
public increment(): void {
this.onAction();
this.setCount(this.countSubject.value + this.incrementValue);
}
/** Decrements the count according to the current incrementValue and registers an action. */
public decrement(): void {
this.onAction();
this.setCount(this.countSubject.value - this.incrementValue);
}
/** Resets the count to 0. */
public reset(): void {
this.setCount(0);
}
}
<button (click)="counter.decrement()">Down</button>
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DownComponent } from './down.component';
describe('DownComponent', () => {
let component: DownComponent;
let fixture: ComponentFixture<DownComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DownComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(DownComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { CounterService } from 'src/app/services/counter.service';
@Component({
selector: 'app-down',
templateUrl: './down.component.html',
styleUrls: ['./down.component.scss']
})
export class DownComponent implements OnInit {
/** This component has a single responsibility : inject the counter service, then call its decrement method when its button gets clicked. */
constructor(public counter: CounterService) { }
ngOnInit(): void {
}
}
<!-- Form -->
<form (ngSubmit)="onSubmit()" [formGroup]="form">
<!-- Date input -->
<input type="date" formControlName="date">
<!-- Submit button -->
<button type="submit" [disabled]="form.invalid">Reset if over 18</button>
</form>
\ No newline at end of file
input {
margin-right: 4px;
}
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ResetComponent } from './reset.component';
describe('ResetComponent', () => {
let component: ResetComponent;
let fixture: ComponentFixture<ResetComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ResetComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ResetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { CounterService } from 'src/app/services/counter.service';
export interface ResetForm {
date: FormControl<string>;
}
@Component({
selector: 'app-reset',
templateUrl: './reset.component.html',
styleUrls: ['./reset.component.scss']
})
export class ResetComponent implements OnInit {
/** Form containing the date object. */
form: FormGroup<ResetForm> = new FormGroup<ResetForm>({
date: new FormControl<string>('', { nonNullable: true, validators: [Validators.required] })
});
// We import the counter service to be able to call its reset method. Therefore make it private since template doesn't need to access it.
constructor(private counter: CounterService) { }
ngOnInit(): void {
}
/** Method to handle the form submission. */
onSubmit(): void {
// Do not do anything if the form is invalid.
if (this.form.invalid) {
return;
}
// This is the birthdate.
const date = new Date(this.form.controls.date.value);
// This is the current date.
const today = new Date();
// We need to compare some things to determine whether the birthdate means the user is over 18 or not :
const years = today.getFullYear() - date.getFullYear();
const months = today.getMonth() - date.getMonth();
const days = today.getDate() - date.getDate();
const overEighteen = (years > 18)
|| (years === 18 && months > 0)
|| (years === 18 && months === 0 && days >= 0);
// If the user is indeed over 18, reset the count.
if (overEighteen) {
this.counter.reset();
}
}
}
<button (click)="counter.increment()">Up</button>
\ No newline at end of file
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Veuillez vous inscrire ou vous pour commenter