From e0beab676b0654265591885287ff2598dd7d6017 Mon Sep 17 00:00:00 2001 From: Younes Jaaidi Date: Tue, 10 Mar 2026 18:50:30 +0100 Subject: [PATCH] =?UTF-8?q?refactor:=20=F0=9F=9B=A0=EF=B8=8F=20update=20Re?= =?UTF-8?q?cipeFilterCriteria=20to=20simplify=20signal=20forms=20exercises?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 4 +- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- .../src/app/recipe/recipe-filter-criteria.ts | 14 +++++-- .../src/app/recipe/recipe-filter.ng.ts | 40 ++++--------------- .../src/app/recipe/recipe-repository.fake.ts | 17 +++++--- .../src/app/recipe/recipe-repository.ts | 15 ++++--- .../src/app/recipe/recipe-search.ng.ts | 7 +++- 131 files changed, 1260 insertions(+), 999 deletions(-) diff --git a/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-repository.fake.ts b/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-repository.ts b/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-repository.ts +++ b/apps/301-recipe-search-isolated-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-repository.fake.ts b/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-repository.ts b/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-repository.ts +++ b/apps/301-recipe-search-isolated-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-repository.fake.ts b/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-repository.ts b/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-repository.ts +++ b/apps/302-recipe-search-integration-browser-mode-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-repository.fake.ts b/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-repository.ts b/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-repository.ts +++ b/apps/302-recipe-search-integration-browser-mode-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-repository.fake.ts b/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-repository.ts b/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-repository.ts +++ b/apps/302-recipe-search-integration-test-bed-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-repository.fake.ts b/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-repository.ts b/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-repository.ts +++ b/apps/302-recipe-search-integration-test-bed-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-repository.fake.ts b/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-repository.ts b/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-repository.ts +++ b/apps/302-recipe-search-integration-testing-library-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-repository.fake.ts b/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-repository.ts b/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-repository.ts +++ b/apps/302-recipe-search-integration-testing-library-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-repository.fake.ts b/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-repository.ts b/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-repository.ts +++ b/apps/303-recipe-search-shallow-browser-mode-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-repository.fake.ts b/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-repository.ts b/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-repository.ts +++ b/apps/303-recipe-search-shallow-browser-mode-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-repository.fake.ts b/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-repository.ts b/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-repository.ts +++ b/apps/303-recipe-search-shallow-test-bed-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-repository.fake.ts b/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-repository.ts b/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-repository.ts +++ b/apps/303-recipe-search-shallow-test-bed-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-repository.fake.ts b/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-repository.ts b/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-repository.ts +++ b/apps/303-recipe-search-shallow-testing-library-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-repository.fake.ts b/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-repository.ts b/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-repository.ts +++ b/apps/303-recipe-search-shallow-testing-library-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-repository.fake.ts b/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-repository.ts b/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-repository.ts +++ b/apps/304-recipe-search-async-pipe-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-repository.fake.ts b/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-repository.ts b/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-repository.ts +++ b/apps/304-recipe-search-async-pipe-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-repository.fake.ts b/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-repository.ts b/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-repository.ts +++ b/apps/305-recipe-search-signals-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-repository.fake.ts b/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-repository.ts b/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-repository.ts +++ b/apps/305-recipe-search-signals-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/401-recipe-filter-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/401-recipe-filter-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/401-recipe-filter-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/401-recipe-filter-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/401-recipe-filter-solution/src/app/recipe/recipe-filter.ng.ts b/apps/401-recipe-filter-solution/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/401-recipe-filter-solution/src/app/recipe/recipe-filter.ng.ts +++ b/apps/401-recipe-filter-solution/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/401-recipe-filter-solution/src/app/recipe/recipe-repository.fake.ts b/apps/401-recipe-filter-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/401-recipe-filter-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/401-recipe-filter-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/401-recipe-filter-solution/src/app/recipe/recipe-repository.ts b/apps/401-recipe-filter-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/401-recipe-filter-solution/src/app/recipe/recipe-repository.ts +++ b/apps/401-recipe-filter-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/401-recipe-filter-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/401-recipe-filter-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/401-recipe-filter-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/401-recipe-filter-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/401-recipe-filter-starter/src/app/recipe/recipe-filter.ng.ts b/apps/401-recipe-filter-starter/src/app/recipe/recipe-filter.ng.ts index 9a793683..2598b383 100644 --- a/apps/401-recipe-filter-starter/src/app/recipe/recipe-filter.ng.ts +++ b/apps/401-recipe-filter-starter/src/app/recipe/recipe-filter.ng.ts @@ -1,11 +1,11 @@ import { ChangeDetectionStrategy, Component, output } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; +import { FormField, FormRoot } from '@angular/forms/signals'; import { RecipeFilterCriteria } from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'wm-recipe-filter', - imports: [ReactiveFormsModule], + imports: [FormField, FormRoot], template: ` <🚧 wm-recipe-filter> `, }) export class RecipeFilter { diff --git a/apps/401-recipe-filter-starter/src/app/recipe/recipe-repository.fake.ts b/apps/401-recipe-filter-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/401-recipe-filter-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/401-recipe-filter-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/401-recipe-filter-starter/src/app/recipe/recipe-repository.ts b/apps/401-recipe-filter-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/401-recipe-filter-starter/src/app/recipe/recipe-repository.ts +++ b/apps/401-recipe-filter-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-filter.ng.ts b/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-filter.ng.ts +++ b/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-repository.fake.ts b/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-repository.ts b/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-repository.ts +++ b/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-search.ng.ts b/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-search.ng.ts index a4cbf408..c1e4e247 100644 --- a/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-search.ng.ts +++ b/apps/402-recipe-search-filter-interaction-solution/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -27,7 +30,7 @@ import { RecipeRepository } from './recipe-repository'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-filter.ng.ts b/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-filter.ng.ts +++ b/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-repository.fake.ts b/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-repository.ts b/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-repository.ts +++ b/apps/402-recipe-search-filter-interaction-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-filter.ng.ts b/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-filter.ng.ts +++ b/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-repository.fake.ts b/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-repository.ts b/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-repository.ts +++ b/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-search.ng.ts b/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-search.ng.ts index fcbc7fd7..07edd17a 100644 --- a/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-search.ng.ts +++ b/apps/403-recipe-search-add-button-solution/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -30,7 +33,7 @@ import { RecipeAddButton } from '../meal-planner/recipe-add-button.ng'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-filter.ng.ts b/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-filter.ng.ts +++ b/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-repository.fake.ts b/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-repository.ts b/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-repository.ts +++ b/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-search.ng.ts b/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-search.ng.ts index a4cbf408..c1e4e247 100644 --- a/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-search.ng.ts +++ b/apps/403-recipe-search-add-button-starter/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -27,7 +30,7 @@ import { RecipeRepository } from './recipe-repository'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-filter.ng.ts b/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-filter.ng.ts +++ b/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-repository.fake.ts b/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-repository.ts b/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-repository.ts +++ b/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-search.ng.ts b/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-search.ng.ts index fcbc7fd7..07edd17a 100644 --- a/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-search.ng.ts +++ b/apps/404-recipe-filter-material-solution/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -30,7 +33,7 @@ import { RecipeAddButton } from '../meal-planner/recipe-add-button.ng'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-filter.ng.ts b/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-filter.ng.ts +++ b/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-repository.fake.ts b/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-repository.ts b/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-repository.ts +++ b/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-search.ng.ts b/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-search.ng.ts index fcbc7fd7..07edd17a 100644 --- a/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-search.ng.ts +++ b/apps/404-recipe-filter-material-starter/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -30,7 +33,7 @@ import { RecipeAddButton } from '../meal-planner/recipe-add-button.ng'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-filter.ng.ts b/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-filter.ng.ts +++ b/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-repository.fake.ts b/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-repository.ts b/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-repository.ts +++ b/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-search.ng.ts b/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-search.ng.ts index fcbc7fd7..07edd17a 100644 --- a/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-search.ng.ts +++ b/apps/501-recipe-preview-testronaut-solution/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -30,7 +33,7 @@ import { RecipeAddButton } from '../meal-planner/recipe-add-button.ng'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-filter.ng.ts b/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-filter.ng.ts +++ b/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-repository.fake.ts b/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-repository.ts b/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-repository.ts +++ b/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-search.ng.ts b/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-search.ng.ts index fcbc7fd7..07edd17a 100644 --- a/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-search.ng.ts +++ b/apps/501-recipe-preview-testronaut-starter/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -30,7 +33,7 @@ import { RecipeAddButton } from '../meal-planner/recipe-add-button.ng'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-filter.ng.ts b/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-filter.ng.ts +++ b/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-repository.fake.ts b/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-repository.ts b/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-repository.ts +++ b/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-search.ng.ts b/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-search.ng.ts index fcbc7fd7..07edd17a 100644 --- a/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-search.ng.ts +++ b/apps/502-recipe-filter-testronaut-solution/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -30,7 +33,7 @@ import { RecipeAddButton } from '../meal-planner/recipe-add-button.ng'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-filter.ng.ts b/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-filter.ng.ts +++ b/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-repository.fake.ts b/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-repository.ts b/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-repository.ts +++ b/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-search.ng.ts b/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-search.ng.ts index fcbc7fd7..07edd17a 100644 --- a/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-search.ng.ts +++ b/apps/502-recipe-filter-testronaut-starter/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -30,7 +33,7 @@ import { RecipeAddButton } from '../meal-planner/recipe-add-button.ng'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-filter.ng.ts b/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-filter.ng.ts +++ b/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-repository.fake.ts b/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-repository.ts b/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-repository.ts +++ b/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-search.ng.ts b/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-search.ng.ts index fcbc7fd7..07edd17a 100644 --- a/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-search.ng.ts +++ b/apps/503-recipe-search-testronaut-solution/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -30,7 +33,7 @@ import { RecipeAddButton } from '../meal-planner/recipe-add-button.ng'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-filter.ng.ts b/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-filter.ng.ts +++ b/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-repository.fake.ts b/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-repository.ts b/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-repository.ts +++ b/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-search.ng.ts b/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-search.ng.ts index fcbc7fd7..07edd17a 100644 --- a/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-search.ng.ts +++ b/apps/503-recipe-search-testronaut-starter/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -30,7 +33,7 @@ import { RecipeAddButton } from '../meal-planner/recipe-add-button.ng'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-filter-criteria.ts b/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-filter.ng.ts b/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-filter.ng.ts +++ b/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-repository.fake.ts b/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-repository.fake.ts +++ b/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-repository.ts b/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-repository.ts +++ b/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-search.ng.ts b/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-search.ng.ts index fcbc7fd7..07edd17a 100644 --- a/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-search.ng.ts +++ b/apps/504-playwright-visual-regression-testing-solution/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -30,7 +33,7 @@ import { RecipeAddButton } from '../meal-planner/recipe-add-button.ng'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params), diff --git a/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-filter-criteria.ts b/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-filter-criteria.ts index 4c03f0d3..4aa522f5 100644 --- a/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-filter-criteria.ts +++ b/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-filter-criteria.ts @@ -1,7 +1,15 @@ export interface RecipeFilterCriteria { - keywords?: string; - maxIngredientCount?: number; - maxStepCount?: number; + keywords: string; + maxIngredientCount: number | null; + maxStepCount: number | null; +} + +export function createDefaultRecipeFilterCriteria(): RecipeFilterCriteria { + return { + keywords: '', + maxIngredientCount: null, + maxStepCount: null, + }; } export function createRecipeFilterCriteria( diff --git a/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-filter.ng.ts b/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-filter.ng.ts index feb5ce68..76112518 100644 --- a/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-filter.ng.ts +++ b/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-filter.ng.ts @@ -1,12 +1,9 @@ -import { - ChangeDetectionStrategy, - Component, - effect, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, model } from '@angular/core'; import { form, FormField, FormRoot } from '@angular/forms/signals'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -42,30 +39,7 @@ import { RecipeFilterCriteria } from './recipe-filter-criteria'; `, }) export class RecipeFilter { - filterChange = output(); - - filterForm = form( - signal<{ - keywords: string; - maxIngredientCount: number | null; - maxStepCount: number | null; - }>({ - keywords: '', - maxIngredientCount: null, - maxStepCount: null, - }), - ); + filter = model(createDefaultRecipeFilterCriteria()); - constructor() { - effect(() => { - const { keywords, maxIngredientCount, maxStepCount } = - this.filterForm().value(); - this.filterChange.emit({ - keywords: keywords.length > 0 ? keywords : undefined, - maxIngredientCount: - maxIngredientCount != null ? maxIngredientCount : undefined, - maxStepCount: maxStepCount != null ? maxStepCount : undefined, - }); - }); - } + filterForm = form(this.filter); } diff --git a/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-repository.fake.ts b/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-repository.fake.ts index e1574267..0f22d6f0 100644 --- a/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-repository.fake.ts +++ b/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-repository.fake.ts @@ -5,18 +5,23 @@ import { } from '@angular/core'; import { defer, Observable, of } from 'rxjs'; import { Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeRepository, RecipeRepositoryDef } from './recipe-repository'; @Injectable() export class RecipeRepositoryFake implements RecipeRepositoryDef { private _recipes: Recipe[] = []; - search({ - keywords, - maxIngredientCount, - maxStepCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + maxStepCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { return defer(() => { const recipes = this._recipes.filter((recipe) => { const conditions = [ diff --git a/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-repository.ts b/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-repository.ts index 16156e72..652598fc 100644 --- a/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-repository.ts +++ b/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-repository.ts @@ -1,7 +1,10 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { createRecipe, Recipe } from './recipe'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + createDefaultRecipeFilterCriteria, + RecipeFilterCriteria, +} from './recipe-filter-criteria'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; @@ -15,10 +18,12 @@ export interface RecipeRepositoryDef { export class RecipeRepository implements RecipeRepositoryDef { private _httpClient = inject(HttpClient); - search({ - keywords, - maxIngredientCount, - }: RecipeFilterCriteria = {}): Observable { + search( + { + keywords, + maxIngredientCount, + }: RecipeFilterCriteria = createDefaultRecipeFilterCriteria(), + ): Observable { const params: ResponseListQueryParams = { embed: 'ingredients', ...(keywords ? { q: keywords } : {}), diff --git a/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-search.ng.ts b/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-search.ng.ts index fcbc7fd7..07edd17a 100644 --- a/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-search.ng.ts +++ b/apps/504-playwright-visual-regression-testing-starter/src/app/recipe/recipe-search.ng.ts @@ -6,7 +6,10 @@ import { } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { Catalog } from '../shared/catalog.ng'; -import { RecipeFilterCriteria } from './recipe-filter-criteria'; +import { + RecipeFilterCriteria, + createDefaultRecipeFilterCriteria, +} from './recipe-filter-criteria'; import { RecipeFilter } from './recipe-filter.ng'; import { RecipePreview } from './recipe-preview.ng'; import { RecipeRepository } from './recipe-repository'; @@ -30,7 +33,7 @@ import { RecipeAddButton } from '../meal-planner/recipe-add-button.ng'; `, }) export class RecipeSearch { - filter = signal({}); + filter = signal(createDefaultRecipeFilterCriteria()); recipes = rxResource({ params: () => this.filter(), stream: ({ params }) => this._recipeRepository.search(params),