mirror of
https://github.com/Raghu-Ch/angular-challenges.git
synced 2026-02-10 04:43:03 -05:00
challenge 51: signal effect trigger too often (#855)
* feat: challenge 51 * fix: permission denied
This commit is contained in:
@@ -24,7 +24,7 @@ If you would like to propose a challenge, this project is open source, so feel f
|
||||
|
||||
## Challenges
|
||||
|
||||
Check [all 50 challenges](https://angular-challenges.vercel.app/)
|
||||
Check [all 51 challenges](https://angular-challenges.vercel.app/)
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
|
||||
36
apps/signal/51-function-call-effect/.eslintrc.json
Normal file
36
apps/signal/51-function-call-effect/.eslintrc.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"extends": ["../../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"extends": [
|
||||
"plugin:@nx/angular",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "app",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "app",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"extends": ["plugin:@nx/angular-template"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
13
apps/signal/51-function-call-effect/README.md
Normal file
13
apps/signal/51-function-call-effect/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Function call in effect
|
||||
|
||||
> author: thomas-laforge
|
||||
|
||||
### Run Application
|
||||
|
||||
```bash
|
||||
npx nx serve signal-function-call-effect
|
||||
```
|
||||
|
||||
### Documentation and Instruction
|
||||
|
||||
Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/51-function-call-effect/).
|
||||
72
apps/signal/51-function-call-effect/project.json
Normal file
72
apps/signal/51-function-call-effect/project.json
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"name": "signal-function-call-effect",
|
||||
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"sourceRoot": "apps/signal/51-function-call-effect/src",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@angular-devkit/build-angular:application",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/apps/signal/51-function-call-effect",
|
||||
"index": "apps/signal/51-function-call-effect/src/index.html",
|
||||
"browser": "apps/signal/51-function-call-effect/src/main.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "apps/signal/51-function-call-effect/tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"apps/signal/51-function-call-effect/src/favicon.ico",
|
||||
"apps/signal/51-function-call-effect/src/assets"
|
||||
],
|
||||
"styles": ["apps/signal/51-function-call-effect/src/styles.scss"],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "signal-function-call-effect:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "signal-function-call-effect:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"executor": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "signal-function-call-effect:build"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
effect,
|
||||
inject,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [FormsModule],
|
||||
selector: 'app-actions',
|
||||
template: `
|
||||
<form class="mx-auto max-w-sm">
|
||||
<label for="actions" class="mb-2 block text-sm font-medium text-gray-900">
|
||||
Choose an action
|
||||
</label>
|
||||
<select
|
||||
name="actions"
|
||||
[(ngModel)]="action"
|
||||
id="actions"
|
||||
class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500">
|
||||
<option selected>Please select an action</option>
|
||||
@for (action of actions; track $index) {
|
||||
<option value="{{ action }}">{{ action }}</option>
|
||||
}
|
||||
</select>
|
||||
</form>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ActionsComponent {
|
||||
private userService = inject(UserService);
|
||||
protected action = signal(undefined);
|
||||
|
||||
protected actions = ['Create', 'Read', 'Update', 'Delete'];
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
this.userService.log(this.action() ?? 'No action selected');
|
||||
});
|
||||
}
|
||||
}
|
||||
34
apps/signal/51-function-call-effect/src/app/app.component.ts
Normal file
34
apps/signal/51-function-call-effect/src/app/app.component.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ActionsComponent } from './action.component';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [FormsModule, ActionsComponent],
|
||||
selector: 'app-root',
|
||||
template: `
|
||||
<nav class="flex w-full items-center border border-b">
|
||||
Profile selected:
|
||||
<form class="m-4 w-48">
|
||||
<select
|
||||
[(ngModel)]="userService.name"
|
||||
name="name"
|
||||
class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 ">
|
||||
<option selected>Please choose an user</option>
|
||||
@for (user of users; track $index) {
|
||||
<option value="{{ user }}">{{ user }}</option>
|
||||
}
|
||||
</select>
|
||||
</form>
|
||||
</nav>
|
||||
|
||||
<app-actions />
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AppComponent {
|
||||
protected userService = inject(UserService);
|
||||
|
||||
protected users = ['Thomas', 'John', 'Alice', 'Bob', 'Charlie', 'David'];
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [],
|
||||
};
|
||||
10
apps/signal/51-function-call-effect/src/app/user.service.ts
Normal file
10
apps/signal/51-function-call-effect/src/app/user.service.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class UserService {
|
||||
name = signal('Thomas');
|
||||
|
||||
log(message: string) {
|
||||
console.log(`${this.name()}: ${message}`);
|
||||
}
|
||||
}
|
||||
BIN
apps/signal/51-function-call-effect/src/favicon.ico
Normal file
BIN
apps/signal/51-function-call-effect/src/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
13
apps/signal/51-function-call-effect/src/index.html
Normal file
13
apps/signal/51-function-call-effect/src/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>angular-function-call-effect</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
7
apps/signal/51-function-call-effect/src/main.ts
Normal file
7
apps/signal/51-function-call-effect/src/main.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { AppComponent } from './app/app.component';
|
||||
import { appConfig } from './app/app.config';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err),
|
||||
);
|
||||
5
apps/signal/51-function-call-effect/src/styles.scss
Normal file
5
apps/signal/51-function-call-effect/src/styles.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
14
apps/signal/51-function-call-effect/tailwind.config.js
Normal file
14
apps/signal/51-function-call-effect/tailwind.config.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind');
|
||||
const { join } = require('path');
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'),
|
||||
...createGlobPatternsForDependencies(__dirname),
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
10
apps/signal/51-function-call-effect/tsconfig.app.json
Normal file
10
apps/signal/51-function-call-effect/tsconfig.app.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/main.ts"],
|
||||
"include": ["src/**/*.d.ts"],
|
||||
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
|
||||
}
|
||||
6
apps/signal/51-function-call-effect/tsconfig.editor.json
Normal file
6
apps/signal/51-function-call-effect/tsconfig.editor.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src/**/*.ts"],
|
||||
"compilerOptions": {},
|
||||
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
|
||||
}
|
||||
30
apps/signal/51-function-call-effect/tsconfig.json
Normal file
30
apps/signal/51-function-call-effect/tsconfig.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"useDefineForClassFields": false,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.editor.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"total": 50,
|
||||
"🟢": 19,
|
||||
"total": 51,
|
||||
"🟢": 20,
|
||||
"🟠": 121,
|
||||
"🔴": 209
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: 🟢 Bug in Effect ?
|
||||
description: Challenge 50 is about understanding why an effect is not triggered.
|
||||
author: thomas-laforge
|
||||
contributors:
|
||||
- tomalaforge
|
||||
challengeNumber: 50
|
||||
command: angular-bug-effect-signal
|
||||
sidebar:
|
||||
order: 19
|
||||
---
|
||||
|
||||
## Information
|
||||
|
||||
In this basic exercise, we aim to display an alert whenever at least one checkbox is checked.
|
||||
|
||||
## Statement
|
||||
|
||||
The alert correctly triggers when clicking on each checkbox separately. However, if you first click on one checkbox and then click on a second one, the alert fails to appear. Why does this happen?
|
||||
|
||||
The objective of this challenge is to understand and correct the issue preventing the alert from appearing when the second checkbox is clicked.
|
||||
@@ -9,7 +9,6 @@ challengeNumber: 50
|
||||
command: signal-bug-in-effect
|
||||
sidebar:
|
||||
order: 19
|
||||
badge: New
|
||||
---
|
||||
|
||||
## Information
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: 🟢 Function call in effect
|
||||
description: Challenge 51 is about understanding why an effect is triggered too often.
|
||||
author: thomas-laforge
|
||||
contributors:
|
||||
- tomalaforge
|
||||
challengeNumber: 51
|
||||
command: signal-function-call-effect
|
||||
sidebar:
|
||||
order: 20
|
||||
badge: New
|
||||
---
|
||||
|
||||
## Information
|
||||
|
||||
In this second challenge focusing on Signal effects, we've introduced an input select that allows users to choose an action. Whenever an action is selected, it is logged in the console. The application also permits changes to the selected user.
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Ideally, the system should log an action only when one is specifically selected. However, we currently face an issue where changing the user also triggers a log entry, even though we do not explicitly monitor user changes.
|
||||
|
||||
The objective of this challenge is to identify and resolve the cause of these extra triggers. We aim to ensure that logging only occurs when an action is selected.
|
||||
@@ -13,7 +13,7 @@ hero:
|
||||
icon: right-arrow
|
||||
variant: primary
|
||||
- text: Ir al Desafío más reciente
|
||||
link: /es/signal/angular/50-bug-effect-signal/
|
||||
link: /es/challenges/signal/51-function-call-effect
|
||||
icon: rocket
|
||||
- text: Dar una estrella
|
||||
link: https://github.com/tomalaforge/angular-challenges
|
||||
@@ -26,8 +26,8 @@ import MyIcon from '../../../components/MyIcon.astro';
|
||||
import SubscriptionForm from '../../../components/SubscriptionForm.astro';
|
||||
|
||||
<CardGrid>
|
||||
<Card title="50 Desafíos">
|
||||
Este repositorio contiene 50 Desafíos relacionados con <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> y <b>Typescript</b>.
|
||||
<Card title="51 Desafíos">
|
||||
Este repositorio contiene 51 Desafíos relacionados con <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> y <b>Typescript</b>.
|
||||
Estos desafíos se resuelven en torno a problemas de la vida real o características específicas para mejorar tus habilidades.
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ hero:
|
||||
icon: right-arrow
|
||||
variant: primary
|
||||
- text: Aller au dernier Challenge
|
||||
link: /fr/challenges/signal/50-bug-effect-signal/
|
||||
link: /fr/challenges/signal/51-function-call-effect
|
||||
icon: rocket
|
||||
- text: Donne une étoile
|
||||
link: https://github.com/tomalaforge/angular-challenges
|
||||
@@ -26,8 +26,8 @@ import MyIcon from '../../../components/MyIcon.astro';
|
||||
import SubscriptionForm from '../../../components/SubscriptionForm.astro';
|
||||
|
||||
<CardGrid>
|
||||
<Card title="50 Défis">
|
||||
Ce répertoire rassemble 50 Défis liés à <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> et <b>Typescript</b>. Ces défis portent sur des problèmes réels ou des fonctionnalités spécifiques pour améliorer vos compétences.
|
||||
<Card title="51 Défis">
|
||||
Ce répertoire rassemble 51 Défis liés à <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> et <b>Typescript</b>. Ces défis portent sur des problèmes réels ou des fonctionnalités spécifiques pour améliorer vos compétences.
|
||||
</Card>
|
||||
|
||||
<Card title="Subscribe to get notify of latest challenges">
|
||||
|
||||
@@ -13,7 +13,7 @@ hero:
|
||||
icon: right-arrow
|
||||
variant: primary
|
||||
- text: Go to the latest Challenge
|
||||
link: /challenges/signal/50-bug-effect-signal/
|
||||
link: /challenges/signal/51-function-call-effect
|
||||
icon: rocket
|
||||
- text: Give a star
|
||||
link: https://github.com/tomalaforge/angular-challenges
|
||||
@@ -27,9 +27,9 @@ import MyIcon from '../../components/MyIcon.astro';
|
||||
import SubscriptionForm from '../../components/SubscriptionForm.astro';
|
||||
|
||||
<CardGrid>
|
||||
<Card title="50 Challenges">
|
||||
This repository gathers 50 Challenges related to <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>NgRx</b> and <b>TypeScript</b>.
|
||||
These challenges revolve around real-life issues or specific features to elevate your skills.
|
||||
<Card title="51 Challenges">
|
||||
This repository gathers 51 Challenges related to <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> and <b>Typescript</b>.
|
||||
These challenges resolve around real-life issues or specific features to elevate your skills.
|
||||
</Card>
|
||||
|
||||
<Card title="Subscribe to get notified of the latest challenges">
|
||||
|
||||
@@ -13,7 +13,7 @@ hero:
|
||||
icon: right-arrow
|
||||
variant: primary
|
||||
- text: Ir para o desafio mais recente
|
||||
link: /pt/challenges/signal/50-bug-effect-signal/
|
||||
link: /pt/challenges/signal/51-function-call-effect
|
||||
icon: rocket
|
||||
- text: Dar uma estrela
|
||||
link: https://github.com/tomalaforge/angular-challenges
|
||||
@@ -26,8 +26,8 @@ import MyIcon from '../../../components/MyIcon.astro';
|
||||
import SubscriptionForm from '../../../components/SubscriptionForm.astro';
|
||||
|
||||
<CardGrid>
|
||||
<Card title="50 Desafios">
|
||||
Este repositório possui 50 Desafios relacionados a <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>,
|
||||
<Card title="51 Desafios">
|
||||
Este repositório possui 51 Desafios relacionados a <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>,
|
||||
<b>Ngrx</b> e <b>Typescript</b>.
|
||||
Esses desafios são voltados para problemas reais ou funcionalidades específicas afim de
|
||||
melhorar suas habilidades.
|
||||
|
||||
@@ -13,7 +13,7 @@ hero:
|
||||
icon: right-arrow
|
||||
variant: primary
|
||||
- text: Перейти к последней задаче
|
||||
link: /ru/challenges/signal/50-bug-effect-signal/
|
||||
link: /ru/challenges/signal/51-function-call-effect
|
||||
icon: rocket
|
||||
- text: Добавить звезду
|
||||
link: https://github.com/tomalaforge/angular-challenges
|
||||
@@ -26,8 +26,8 @@ import MyIcon from '../../../components/MyIcon.astro';
|
||||
import SubscriptionForm from '../../../components/SubscriptionForm.astro';
|
||||
|
||||
<CardGrid>
|
||||
<Card title="49 испытаний">
|
||||
Этот репозиторий содержит 49 испытаний, связанных с <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> и <b>Typescript</b>.
|
||||
<Card title="51 испытаний">
|
||||
Этот репозиторий содержит 51 испытаний, связанных с <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> и <b>Typescript</b>.
|
||||
Испытания основаны на реальных задачах или инструментах для того, чтобы прокачать вас.
|
||||
</Card>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user