Cool Posts is an Angular application designed for displaying and managing a collection of posts. It's built with a focus on modern Angular practices, developer experience, and maintainability.
- Prerequisites
- Getting Started
- Running Tests
- Linting and Formatting
- Project Structure
- Architecture Choices & Reasoning
- Common Pitfalls & Issues
- Additional Insights & Motivations
Before you begin, ensure you have the following installed:
-
Clone the repository:
git clone https://github.com/awabcodes/cool-posts.git cd cool-posts -
Install dependencies:
npm install
This will also set up Husky pre-commit hooks.
Run npm start for a dev server. Navigate to http://localhost:4200/. The app will automatically reload if you change any of the source files.
For continuous building during development without serving: npm run watch
Run npm run build to build the project. The build artifacts will be stored in the dist/ directory.
This command uses the production configuration by default.
This project uses Jest for unit testing.
-
Run tests once:
npm test # OR jest
-
Run tests in watch mode:
npm run test:watch # OR jest --watch -
Run tests and generate a coverage report:
npm run test:coverage # OR jest --coverageThe coverage report can be found in the
coverage/directory.
This project uses ESLint for linting TypeScript/JavaScript files and Prettier for code formatting.
-
Lint files and automatically fix issues:
npm run lint
-
Format files:
npm run format
These checks are also run automatically on pre-commit thanks to Husky and lint-staged.
The project follows a standard Angular CLI structure with some conventions for organization:
src/
├── app/
│ ├── core/ # Core module: singleton services, interceptors, guards
│ │ ├── interceptors/ # HTTP interceptors (e.g., error handling, loader)
│ │ └── services/ # Singleton services (e.g., error reporting, global loader state)
│ ├── features/ # Feature modules (e.g., posts, users, settings)
│ │ └── posts/ # Example: 'Posts' feature
│ │ ├── components/ # Smart & Presentational components specific to this feature
│ │ │ └── post-card/
│ │ ├── models/ # Data models/interfaces for this feature
│ │ ├── services/ # Services specific to this feature (e.g., API calls)
│ │ ├── store/ # NgRx Signal Store for this feature's state
│ │ ├── posts.component.ts # Container/smart component for the feature
│ │ └── posts.routes.ts # Routes specific to this feature
│ ├── shared/ # Shared module: reusable components, directives, pipes
│ │ └── components/ # Common UI components (e.g., header, spinner)
│ ├── app.component.* # Root application component
│ ├── app.config.ts # Application configuration (providers, etc.)
│ └── app.routes.ts # Main application routes
├── environments/ # Environment-specific configuration
├── index.html # Main HTML page
├── main.ts # Main entry point of the application
└── styles.css # Global styles
- Reasoning: Angular provides a comprehensive and opinionated framework for building robust, scalable single-page applications. Its strong typing with TypeScript, dependency injection system, component-based architecture, and powerful CLI make it suitable for complex projects. I'm using Angular v19+, leveraging its latest features including Signals.
- Reasoning: For state management, I've adopted
@ngrx/signals. This choice is driven by:- Granular Reactivity: Signals offer fine-grained reactivity, leading to more performant updates as only affected parts of the UI re-render.
- Simplicity & Modern Angular: Aligns with Angular's new reactivity model, offering a potentially simpler API and mental model compared to traditional NgRx with reducers and effects for certain use cases, especially within feature stores.
- Local Feature State: The
posts.store.tswithin thefeatures/postsdirectory demonstrates managing feature-specific state efficiently.
- Reasoning: Tailwind CSS is a utility-first CSS framework.
- Rapid UI Development: Allows for quick prototyping and building of custom designs directly in the markup.
- Consistency: Enforces a consistent design language by using predefined utility classes.
- Reduced CSS Bloat: By composing utilities, we avoid writing lots of custom CSS.
postcssis used in the build process as required by Tailwind.
- Reasoning: Jest is chosen as the testing framework over Karma/Jasmine.
- Developer Experience: Jest is known for its speed, powerful mocking capabilities, and "batteries-included" approach.
- Snapshot Testing: Useful for UI components.
- Ecosystem: Widely adopted in the JavaScript community.
jest-preset-angularis used to configure Jest for Angular projects seamlessly.
- Reasoning:
- ESLint (
@typescript-eslint/eslint-plugin,angular-eslint): Enforces code style, identifies potential bugs, and ensures adherence to best practices for both TypeScript and Angular. - Prettier: An opinionated code formatter that ensures consistent code style across the entire codebase, reducing debates and cognitive load.
eslint-config-prettierandeslint-plugin-prettierensure ESLint and Prettier work well together.
- ESLint (
- Reasoning:
- Automated Quality Checks:
huskyandlint-stagedare configured to automatically run ESLint and Prettier on staged files before each commit. - Maintain Code Quality: This ensures that code committed to the repository adheres to defined quality standards, preventing common issues from entering the codebase.
- Automated Quality Checks:
- Reasoning: The project is structured into
core,features, andshareddirectories.- Scalability & Maintainability: This separation of concerns makes the codebase easier to understand, navigate, and scale. New features can be added as independent modules.
- Lazy Loading: Feature modules (like
posts) are often designed to be lazy-loaded, improving initial application load time.
- Reasoning: The
core/interceptorsdirectory houses HTTP interceptors (e.g.,ErrorInterceptor,LoaderInterceptor).- Centralized Logic: Interceptors provide a way to centrally manage outgoing requests and incoming responses. This is ideal for global error handling, adding authentication tokens, or managing loading indicators (
LoaderService).
- Centralized Logic: Interceptors provide a way to centrally manage outgoing requests and incoming responses. This is ideal for global error handling, adding authentication tokens, or managing loading indicators (
- NgRx Signals Learning Curve:
- If new to Signals, understanding their reactivity model and how they differ from traditional RxJS Observables or NgRx Store might take some time. Refer to the official Angular and NgRx documentation.
- State Management Complexity:
- While
@ngrx/signalscan be simpler for local/feature state, ensure complex interactions or shared global state are thoughtfully designed to avoid prop-drilling or overly coupled stores.
- While
- Jest Configuration for Angular:
- Ensure
jest-preset-angularis correctly set up, especially for handling Angular-specific features like templates, CSS, and dependency injection. Sometimes, specific mocks ormoduleNameMapperentries might be needed.
- Ensure
- Environment Variables:
- Ensure environment-specific configurations (API URLs, keys) are managed correctly using Angular's
environmentsfiles and are not hardcoded.
- Ensure environment-specific configurations (API URLs, keys) are managed correctly using Angular's
- Developer Experience (DX): A key motivation for choosing tools like Jest, Prettier, ESLint, and Husky is to enhance the developer experience by automating common tasks, ensuring code quality, and providing fast feedback loops.
- Modern Angular Focus: The project aims to leverage the latest features and best practices from the Angular ecosystem, including standalone components and the new Signals-based reactivity.
- Scalability: The feature-sliced architecture and use of NgRx Signals for state management are chosen with scalability in mind, allowing the application to grow without becoming unmanageable.
- Clear Separation of Concerns: The project structure and architectural choices (e.g., services for business logic, components for presentation, interceptors for cross-cutting concerns) aim to maintain a clear separation of concerns, making the code easier to test and reason about.