Skip to content

Commit 3261f56

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
1 parent efd1180 commit 3261f56

File tree

1 file changed

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

1 file changed

+16
-14
lines changed

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

Lines changed: 16 additions & 14 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" [disabled]="loginForm().invalid()">Log In</button>
382383
</form>
383384
`,
384385
})
@@ -804,20 +805,21 @@ touch src/app/mock-recipes.ts
804805

805806
**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.
806807

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.
808+
- **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:
809+
1. Call `event.preventDefault()` to prevent the default form submission.
810+
2. Use the `submit(this.myForm, ...)` utility.
811+
3. Update the `RecipeService` to include an `addRecipe(newRecipe: RecipeModel)` method.
812+
4. Construct a complete `RecipeModel` (merging form values with defaults) and pass it to the service.
813+
5. **Reset the form**: Call `this.myForm().reset()` to clear interaction flags.
814+
6. **Clear the values**: Call `this.myModel.set(...)` to reset the inputs.
813815

814816
- **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:
815817
- `name`: Required (Message: 'Recipe name is required.').
816818
- `description`: Required (Message: 'Description is required.').
817819
- `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.**
820+
**Finally, ensure your submit button has `type="submit"` and bind its `[disabled]` property to `myForm().invalid()` so users cannot submit invalid data.**
819821

820822
- **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:
821823
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()`.
824+
2. Inside the block, use `@for` to iterate over the field's `.errors()` (use `track error.kind` to identify each error by its type).
823825
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)