mirror of
https://github.com/Raghu-Ch/angular-challenges.git
synced 2026-02-10 12:53:03 -05:00
feat(challenge33): add new challenge about decoupling
This commit is contained in:
36
apps/decoupling/.eslintrc.json
Normal file
36
apps/decoupling/.eslintrc.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"rules": {
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "app",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "app",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
]
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@nx/angular",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
]
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"extends": ["plugin:@nx/angular-template"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
43
apps/decoupling/README.md
Normal file
43
apps/decoupling/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
<h1>decoupling component with InjectionToken</h1>
|
||||
|
||||
> Author: Thomas Laforge
|
||||
|
||||
> Big thanks to **Robin Goetz** and his [Spartan Project](https://github.com/goetzrobin/spartan).
|
||||
> This challenge was proposed by Robin and is strongly inspired by his project.
|
||||
|
||||
### Information
|
||||
|
||||
The goal of this challenge is to separate the behavior of a component from its style. For the purpose of this challenge, we will be working on a button element. When we click on it, we will toggle a _disabled_ property which will change the style of the element. This is quite useless in real life but the challenge aims to demonstate a useful concept.
|
||||
|
||||
The behavior of the component (referred to as the _brain_ in the Spartan stack) is located in the brain library. The styling part (referred to as the _helmet_) is located inside the helmet library. Both libraries cannot depend on each other because we want to be able to publish them separatly. To help us address the issue, we are using the Nx enforce eslint rule. You can find more details [here](https://nx.dev/core-features/enforce-module-boundaries);
|
||||
|
||||
However the button's helmet needs to access the state of the component to style the button differently based on its state. As mention above, we cannot import the `BtnDisabledDirective` directly into the helmet library as done currently. If you go to [`BtnHelmetDirective`](../../libs/decoupling/helmet/src/lib/btn-style.directive.ts), you will encounter a linting error. **A project tagged with "type:hlm" can only depend on libs tagged with "type:core"**.
|
||||
|
||||
### Statement
|
||||
|
||||
The goal of this challenge is to find a way to decouple both Directives.
|
||||
|
||||
### Hint
|
||||
|
||||
<details>
|
||||
<summary>Hint 1</summary>
|
||||
Carefully read the title of the challenge 😇
|
||||
</details>
|
||||
|
||||
### Submitting your work
|
||||
|
||||
1. Fork the project
|
||||
2. clone it
|
||||
3. npm ci
|
||||
4. `npx nx lint decoupling-helmet` to visualize the error.
|
||||
5. `npx nx serve decoupling`
|
||||
6. _...work on it_
|
||||
7. Commit your work
|
||||
8. Submit a PR with a title beginning with **Answer:33** that I will review and other dev can review.
|
||||
|
||||
<a href="https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A33+label%3Aanswer"><img src="https://img.shields.io/badge/-Solutions-green" alt="decoupling"/></a>
|
||||
<a href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A33+label%3A"answer+author"'><img src="https://img.shields.io/badge/-Author solution-important" alt="decoupling solution author"/></a>
|
||||
|
||||
<!-- <a href="{Blog post url}" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/-Blog post explanation-blue" alt="decoupling blog article"/></a> -->
|
||||
|
||||
_You can ask any question on_ <a href="https://twitter.com/laforge_toma" target="_blank" rel="noopener noreferrer"><img src="./../../logo/twitter.svg" height=20px alt="twitter"/></a>
|
||||
81
apps/decoupling/project.json
Normal file
81
apps/decoupling/project.json
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"name": "decoupling",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"sourceRoot": "apps/decoupling/src",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@angular-devkit/build-angular:browser",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/apps/decoupling",
|
||||
"index": "apps/decoupling/src/index.html",
|
||||
"main": "apps/decoupling/src/main.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "apps/decoupling/tsconfig.app.json",
|
||||
"assets": [
|
||||
"apps/decoupling/src/favicon.ico",
|
||||
"apps/decoupling/src/assets"
|
||||
],
|
||||
"styles": ["apps/decoupling/src/styles.scss"],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "decoupling:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "decoupling:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"executor": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "decoupling:build"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"apps/decoupling/**/*.ts",
|
||||
"apps/decoupling/**/*.html"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
apps/decoupling/src/app/app.component.ts
Normal file
11
apps/decoupling/src/app/app.component.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { BtnDisabledDirective } from '@angular-challenges/decoupling/brain';
|
||||
import { BtnHelmetDirective } from '@angular-challenges/decoupling/helmet';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [BtnDisabledDirective, BtnHelmetDirective],
|
||||
selector: 'app-root',
|
||||
template: ` <button btnDisabled hlm>Coucou</button> `,
|
||||
})
|
||||
export class AppComponent {}
|
||||
0
apps/decoupling/src/assets/.gitkeep
Normal file
0
apps/decoupling/src/assets/.gitkeep
Normal file
BIN
apps/decoupling/src/favicon.ico
Normal file
BIN
apps/decoupling/src/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
13
apps/decoupling/src/index.html
Normal file
13
apps/decoupling/src/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>decoupling</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>
|
||||
4
apps/decoupling/src/main.ts
Normal file
4
apps/decoupling/src/main.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent).catch((err) => console.error(err));
|
||||
5
apps/decoupling/src/styles.scss
Normal file
5
apps/decoupling/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/decoupling/tailwind.config.js
Normal file
14
apps/decoupling/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/decoupling/tsconfig.app.json
Normal file
10
apps/decoupling/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"]
|
||||
}
|
||||
11
apps/decoupling/tsconfig.editor.json
Normal file
11
apps/decoupling/tsconfig.editor.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"../../libs/decoupling/brain/src/lib/button-disabled.directive.ts",
|
||||
"../../libs/decoupling/helmet/src/lib/btn-style.directive.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
}
|
||||
}
|
||||
29
apps/decoupling/tsconfig.json
Normal file
29
apps/decoupling/tsconfig.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"useDefineForClassFields": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.editor.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user