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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 9a79368..2598b38 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 a4cbf40..c1e4e24 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 fcbc7fd..07edd17 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 a4cbf40..c1e4e24 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 fcbc7fd..07edd17 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 fcbc7fd..07edd17 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 fcbc7fd..07edd17 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 fcbc7fd..07edd17 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 fcbc7fd..07edd17 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 fcbc7fd..07edd17 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 fcbc7fd..07edd17 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 fcbc7fd..07edd17 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 fcbc7fd..07edd17 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 4c03f0d..4aa522f 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 feb5ce6..7611251 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 e157426..0f22d6f 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 16156e7..652598f 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 fcbc7fd..07edd17 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),