Hacking away at signals and resources.

This commit is contained in:
Jesse Brault 2025-12-12 17:28:55 -06:00
parent ab1c39560f
commit 86ef321065
15 changed files with 109 additions and 39 deletions

View File

@ -1,5 +1,4 @@
<main>
<app-recipe></app-recipe>
<h1>Meals Made Easy</h1>
<router-outlet />
</main>
<router-outlet />

View File

@ -1,3 +1,9 @@
import { Routes } from '@angular/router';
import { RecipeView } from './recipe-view/recipe-view.component';
export const routes: Routes = [];
export const routes: Routes = [
{
path: 'recipes/:username/:slug',
component: RecipeView
}
];

View File

@ -1,10 +1,9 @@
import { Component, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Recipe } from './recipe/recipe';
@Component({
selector: 'app-root',
imports: [RouterOutlet, Recipe],
imports: [RouterOutlet],
templateUrl: './app.html',
styleUrl: './app.css',
})

View File

@ -6,7 +6,20 @@ export interface RecipeInfoViews {
content: Recipe[];
}
export interface RecipeView {
isOwner: boolean | null;
isStarred: boolean | null;
recipe: Recipe;
}
export interface Recipe {
id: number;
title: string;
mainImage: ImageView;
text: string;
}
export interface ImageView {
alt: string;
url: string;
}

View File

@ -0,0 +1,3 @@
<h1>{{ recipe.title }}</h1>
<img [ngSrc]="recipe.mainImage.url" [alt]="recipe.mainImage.alt" [width]="600" [height]="400">
<div [innerHTML]="recipe.text"></div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RecipeViewCard } from './recipe-view-card.component';
describe('Card', () => {
let component: RecipeViewCard;
let fixture: ComponentFixture<RecipeViewCard>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RecipeViewCard]
})
.compileComponents();
fixture = TestBed.createComponent(RecipeViewCard);
component = fixture.componentInstance;
await fixture.whenStable();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,18 @@
import { Component, Input } from '@angular/core';
import { Recipe } from '../../model/Recipe.model';
import { NgOptimizedImage } from '@angular/common';
@Component({
selector: 'app-recipe-view-card',
imports: [
NgOptimizedImage
],
templateUrl: './recipe-view-card.component.html',
styleUrl: './recipe-view-card.component.css',
})
export class RecipeViewCard {
@Input({ required: true })
public recipe!: Recipe;
}

View File

@ -0,0 +1,5 @@
@if (recipe.isLoading()) {
<p>Loading...</p>
} @else if (recipe.hasValue()) {
<app-recipe-view-card [recipe]="recipe.value()"></app-recipe-view-card>
}

View File

@ -1,17 +1,17 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Recipe } from './recipe';
import { RecipeView } from './recipe-view.component';
describe('Recipe', () => {
let component: Recipe;
let fixture: ComponentFixture<Recipe>;
let component: RecipeView;
let fixture: ComponentFixture<RecipeView>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [Recipe],
imports: [RecipeView],
}).compileComponents();
fixture = TestBed.createComponent(Recipe);
fixture = TestBed.createComponent(RecipeView);
component = fixture.componentInstance;
await fixture.whenStable();
});

View File

@ -0,0 +1,25 @@
import { Component, inject, resource } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { RecipeService } from '../recipe.service';
import { RecipeViewCard } from './recipe-view-card/recipe-view-card.component';
@Component({
selector: 'app-recipe-view',
imports: [
RecipeViewCard
],
templateUrl: './recipe-view.component.html',
styleUrl: './recipe-view.component.css',
})
export class RecipeView {
private recipeService = inject(RecipeService);
private route = inject(ActivatedRoute);
private username = this.route.snapshot.paramMap.get('username') as string;
private slug = this.route.snapshot.paramMap.get('slug') as string;
protected recipe = resource({
loader: () => {
return this.recipeService.getRecipe(this.username, this.slug);
},
});
}

View File

@ -1,7 +1,7 @@
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map, Observable } from 'rxjs';
import { Recipe, RecipeInfoViews } from './model/Recipe.model';
import { Recipe, RecipeInfoViews, RecipeView } from './model/Recipe.model';
@Injectable({
providedIn: 'root',
@ -14,4 +14,10 @@ export class RecipeService {
.get<RecipeInfoViews>('http://localhost:8080/recipes')
.pipe(map((res) => res.content));
}
public async getRecipe(username: string, slug: string): Promise<Recipe> {
const res = await fetch(`http://localhost:8080/recipes/${username}/${slug}`)
const recipeView = await res.json() as RecipeView;
return recipeView.recipe;
}
}

View File

@ -1,6 +0,0 @@
<p>recipe works!</p>
<p>{{ title() }}</p>
@for (recipeTitle of recipeTitles(); track recipeTitle) {
<p>{{ recipeTitle }}</p>
}
<button (click)="onClick()">Click me!</button>

View File

@ -1,21 +0,0 @@
import { Component, inject, signal } from '@angular/core';
import { RecipeService } from '../recipe.service';
@Component({
selector: 'app-recipe',
imports: [],
templateUrl: './recipe.html',
styleUrl: './recipe.css',
})
export class Recipe {
protected readonly title = signal('Hello, Jesse!');
protected readonly recipeTitles = signal<string[]>([]);
private readonly recipeService = inject(RecipeService);
protected onClick() {
this.recipeService.getRecipes().subscribe((recipes) => {
this.recipeTitles.set(recipes.map((recipe) => recipe.title));
});
}
}