Skip to content

Commit c94a0a8

Browse files
committed
fix(@angular/cli): improve signal forms lesson examples in AI tutor
- Use error.kind for tracking in @for loops instead of $index - Use form submit event instead of button click for better semantics - Add type="submit" to submit buttons - Update Module 19 and 20 exercises to reflect best practices - Remove disabled button pattern
1 parent efd1180 commit c94a0a8

File tree

1 file changed

+16
-15
lines changed
  • packages/angular/cli/src/commands/mcp/resources

1 file changed

+16
-15
lines changed

packages/angular/cli/src/commands/mcp/resources/ai-tutor.md

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,9 @@ When teaching or generating code for Phase 5 (Signal Forms), you **must** strict
327327
- Use the `[field]` directive to bind a form control to an input.
328328
- **Correct Syntax**: `<input [field]="myForm.username">` (Note: `field` is lowercase here).
329329
- **Submission Logic**:
330-
- Use the `submit()` utility function inside a standard click handler.
331-
- **Syntax**: `submit(this.myForm, async () => { /* logic */ })`.
330+
- Use the `submit()` utility function inside the form's `(submit)` event handler.
331+
- **Syntax**: Add `(submit)="save($event)"` to the `<form>` element and use `submit(this.myForm, async () => { /* logic */ })` in the handler.
332+
- The handler must call `event.preventDefault()` to prevent the default form submission behavior.
332333
- **Resetting Logic**:
333334
- To reset the form, you must perform two actions:
334335
1. **Clear Interaction State**: Call `.reset()` on the form signal's value: `this.myForm().reset()`.
@@ -344,7 +345,7 @@ When teaching or generating code for Phase 5 (Signal Forms), you **must** strict
344345
```html
345346
@if (myForm.email().invalid()) {
346347
<ul>
347-
@for (error of myForm.email().errors(); track error) {
348+
@for (error of myForm.email().errors(); track error.kind) {
348349
<li>{{ error.message }}</li>
349350
}
350351
</ul>
@@ -363,22 +364,22 @@ When teaching or generating code for Phase 5 (Signal Forms), you **must** strict
363364
standalone: true,
364365
imports: [Field],
365366
template: `
366-
<form>
367+
<form (submit)="save($event)">
367368
<label>
368369
Email
369370
<input [field]="loginForm.email" />
370371
</label>
371372
@if (loginForm.email().touched() && loginForm.email().invalid()) {
372373
<p class="error">
373-
@for (error of loginForm.email().errors(); track $index) {
374+
@for (error of loginForm.email().errors(); track error.kind) {
374375
<span>{{ error.message }}</span>
375376
}
376377
</p>
377378
}
378379

379380
<label>Password <input type="password" [field]="loginForm.password" /></label>
380381

381-
<button (click)="save($event)" [disabled]="loginForm().invalid()">Log In</button>
382+
<button type="submit">Log In</button>
382383
</form>
383384
`,
384385
})
@@ -553,7 +554,6 @@ _(The LLM will need to interpret "project-specific" or "app-themed" below based
553554
- **Module 20 (Validation in Signal Forms)**
554555
- **20a**: The component imports validator functions (e.g., `required`, `email`) from `@angular/forms/signals`. `description`: "importing functional validators."
555556
- **20b**: The `form()` definition uses a validation callback. `description`: "defining the validation schema."
556-
- **20c**: The button uses `[disabled]` bound to `myForm.invalid()`. `description`: "disabling the button for invalid forms."
557557
- **Module 21 (Field State & Error Messages)**
558558
- **21a**: The template uses an `@if` block checking `field().invalid()` (e.g., `myForm.name().invalid()`). `description`: "checking field invalidity."
559559
- **21b**: Inside the check, an `@for` loop iterates over `field().errors()`. `description`: "iterating over validation errors."
@@ -804,20 +804,21 @@ touch src/app/mock-recipes.ts
804804

805805
**Exercise**: Your goal is to create a new `AddRecipe` component that uses the modern `Signal Forms` API. Import `form` and `Field` from `@angular/forms/signals`. Create a form using the `form()` function that includes fields for `name`, `description`, and `authorEmail`. In your template, use the `[field]` binding to connect your inputs to these form controls.
806806

807-
- **Module 19**: **Submitting & Resetting**: Concept: Handling form submission and resetting state. **Exercise**: Inject the service into your `AddRecipe` component. Create a protected `save()` method triggered by a "Save Recipe" button's `(click)` event. Inside this method:
808-
1. Use the `submit(this.myForm, ...)` utility.
809-
2. Update the `RecipeService` to include an `addRecipe(newRecipe: RecipeModel)` method.
810-
3. Construct a complete `RecipeModel` (merging form values with defaults) and pass it to the service.
811-
4. **Reset the form**: Call `this.myForm().reset()` to clear interaction flags.
812-
5. **Clear the values**: Call `this.myModel.set(...)` to reset the inputs.
807+
- **Module 19**: **Submitting & Resetting**: Concept: Handling form submission and resetting state. **Exercise**: Inject the service into your `AddRecipe` component. Create a protected `save()` method triggered by the form's `(submit)` event. Inside this method:
808+
1. Call `event.preventDefault()` to prevent the default form submission.
809+
2. Use the `submit(this.myForm, ...)` utility.
810+
3. Update the `RecipeService` to include an `addRecipe(newRecipe: RecipeModel)` method.
811+
4. Construct a complete `RecipeModel` (merging form values with defaults) and pass it to the service.
812+
5. **Reset the form**: Call `this.myForm().reset()` to clear interaction flags.
813+
6. **Clear the values**: Call `this.myModel.set(...)` to reset the inputs.
813814

814815
- **Module 20**: **Validation in Signal Forms**: Concept: Applying functional validators. **Exercise**: Import `required` and `email` from `@angular/forms/signals`. Modify your `form()` definition to add a validation callback enforcing:
815816
- `name`: Required (Message: 'Recipe name is required.').
816817
- `description`: Required (Message: 'Description is required.').
817818
- `authorEmail`: Required (Message: 'Author email is required.') AND Email format (Message: 'Please enter a valid email address.').
818-
**Finally, bind the `[disabled]` property of your button to `myForm.invalid()` so users cannot submit invalid data.**
819+
**Finally, ensure your submit button has `type="submit"`. Note: the `submit()` utility automatically handles validation by marking all fields as touched when submission is attempted.**
819820

820821
- **Module 21**: **Field State & Error Messages**: Concept: Providing user feedback by accessing field state signals. **Exercise**: Improve the UX of your `AddRecipe` component by showing specific error messages when data is missing or incorrect. In your template, for the `name`, `description`, and `authorEmail` inputs:
821822
1. Create an `@if` block that checks if the field is `invalid()` (e.g., `myForm.name().invalid()`).
822-
2. Inside the block, use `@for` to iterate over the field's `.errors()`.
823+
2. Inside the block, use `@for` to iterate over the field's `.errors()` (use `track error.kind` to identify each error by its type).
823824
3. Display the `error.message` in a red text color or helper text style so the user knows exactly what to fix.

0 commit comments

Comments
 (0)