Awesome
AngularStarterKit
This starter kit use angular version 18.0.0
and includes the following features:
- Scalable folder structure
- Linter and prettier
- Routing and lazy loading
- Authentication service
- Light design system and layout utilities (css class based)
- Toast service (snackbar and loading screen)
- Modal service (support custom modals)
- Test setup (jest)
- docker compose for production
Start from this template
- Clone this repository
- Delete
.git
folder and rungit init
to start a new repository - Run
npm install
- Run
ng serve
to start the development server - Navigate to
http://localhost:4200/
.
Build
Run ng build
to build the project. The build artifacts will be stored in the dist/
directory.
Running unit tests
Run ng test
to execute the unit tests via jest.
Guidelines
Folder structure
public/
├─ assets/ # static assets
│ ├─ icons/
│ ├─ illustrations/
src/
├─ app/ # source code of the application
│ ├─ core/ # core components and classes etc
│ │ ├─ components/ # generic components and header, footer etc..
│ │ ├─ constants/ # constants for the app
│ │ ├─ guards/ # guards for routes
│ │ ├─ interceptors/ # interceptors for http requests
│ │ ├─ models/ # models, interfaces for your app
│ │ ├─ services/ # general services
│ ├─ modules/ # features
│ ├─ styles/ # global styles
│ ├─ app.component.ts # the root component (there is more files hidden for simplicity)
├─ environments/ # environment variables. DO NOT PUT SECRETS HERE. THIS IS VISIBILE IN THE BROWSER
linter and prettier
Setup your ide to lint and format on save. This will help you to keep the code clean and consistent.
for vscode, install the following extensions:
- prettier
- eslint
and configure them to lint on save.
Routing and lazy loading
The app is using standalone components. This means that the components are loaded only when the user navigates to the route. This is a good practice to improve the performance of the app. The routing is configured in app.routes.ts
file.
Authentication service
The AuthService
allows you to authenticate a user and keep it logged in thanks to localstorage with a JWT token.
Before starting
don't forget to unsubscribe from observables. The easiest way is to use takeUntil
as last operator of your pipe
someobservable$.pipe(takeUntil(this.destroyed$)).subscribe( ... )
AuthService
First you need to import the authService and inject it.
constructor(private readonly authService:AuthService){}
services attributes
You can retrieve the user's token and check if the user is connected.
const userToken: string = authService.token;
const isUserConnected: boolean = authService.connected;
Login
connect a user. This method will automatically save the JWT token in the service and the local storage. In case of failure it will show an error toast.
authService.login(login: string, password: string)
.pipe(takeUntil(this.destroy$))
.subscribe((loggedIn: boolean) => {
if (loggedIn) {
// Successfully logged in
} else {
// Handle login failure
}
});
Register
To allow users to create a new account. This will NOT connect the user. In case of failure it will show an error toast.
authService.register(login: string, password: string)
.pipe(takeUntil(this.destroy$))
.subscribe((registered: boolean) => {
if (registered) {
// Successfully registered
} else {
// Handle registration failure
}
});
Checking Token Validity
You can check the validity of the user's token by using the isTokenValid
method.
If the token is invalid, the user will be logged out.
authService
.isTokenValid()
.pipe(takeUntil(this.destroy$))
.subscribe((isValid: boolean) => {
if (isValid) {
// Token is valid
} else {
// Token is invalid or expired
}
});
Logout
To disconnect the user and clear their token.
authService.logout();
That's it! You can now use the AuthService
!
Modal service & custom modals
The modal service allows you to open a modal and close it from anywhere in the app. It also allows you to create custom modals.
Create a custom modal
Let's create the UsernameModalComponent.
First be sure that the module where UsernameModalComponent is declared has imported the SharedModule
.
username-modal.component.ts
To create a custom modal, you need to create a component that extends the BaseModalComponent
, it allows you to open the modal with the service. You can modify the payload in your handleClose
function.
export class UsernameModalComponent extends BaseModalComponent {
username: string = '';
constructor() {
super();
}
handleClose(event: ModalPayload) {
const editedPayload = { ...event, data: this.username };
this.onClose(editedPayload);
}
}
username-modal.component.html
In the template you will need to use the modal component app-modal
. This component will handle the modal logic & options. You can add your custom content inside the modal
You have to
- pass the
options
(inherited from BaseModalComponent) - define the function
handleClose
that will be called when the user close the modal.
The validator
is optional and allows you to disable the confirm button if the validator returns false.
<app-modal
[options]="options"
[validator]="username !== ''"
(closeEvent)="handleClose($event)">
<!-- custom info -->
<div class="w-100 flex-col center middle">
<input
type="text"
class="basic"
placeholder="basic input"
[(ngModel)]="username"
placeholder="Name" />
</div>
</app-modal>
The service
First you need to import the modalService and inject it.
constructor(private readonly modalService:ModalService){}
Then you can either open any modal. Modals options are always optionals. you can custom mutliple options like the title, the size, the data etc...
export interface ModalOptions {
title?: string;
message?: string;
confirmText?: string;
cancelText?: string;
confirmColor?: string;
cancelColor?: string;
}
Here are the default options.
{
title: 'Modal title',
message: '',
confirmText: 'Yes',
cancelText: 'No',
confirmColor: 'primary',
cancelColor: 'basic'
}
openModal() {
this.modalService
.open(YourModalComponent, { title: "What's your name ?" })
.subscribe(payload => {
if (payload.success) {
// Do something, the modal was confirmed
} else {
// Do something else the modal was canceled
}
});
}
You can also open a confirmation modal (under the hood it's just a basic modal with different default options)
{
title: 'Please confirm your choice',
message: 'Are you sure you want to do this?',
cancelText: 'Cancel',
confirmText: 'Confirm',
confirmColor: 'danger',
}
openConfirmModal() {
this.modalService.openConfirmModal().subscribe(payload => {
if (payload.success) {
// Do something, user confirmed
} else {
// Do something else user canceled
}
});
}
And that's it! You can now use your modal ! You can check the CustomModalComponent
in the app (in the starter-kit
module) for a working example.
Language service (i18n)
see angular internationalization documentation for more information: angular i18n
Using the pipe
<p>{{ 'global.title' | translate }}</p>
Using the service
constructor(private readonly languageService: LanguageService) {}
ngOnInit() {
this.languageService.getTranslation('global.title').pipe(take(1)).subscribe((translation: string) => {
console.log(translation); // Hello world
});
}
Design system
This is a very light design system, based on css classes. The idea is to have a set of classes that can be used to build the UI.
Flex layout
The syntax is .flex-<direction>
.
The direction can be row
or col
and it can be combined with the following modifiers:
.left
, .center
, .right
for the x-axis
.top
, .middle
, .bottom
for the y-axis
.btw
for space between elements following the direction
.wrap
, .wrap-reverse
for wrapping elements
for wrapping elements in reverse order
.no-gap
, .small-gap
, .micro-gap
for the gap between elements. By default, the gap is 1rem.
Grid layout
The syntax is .grid-<row/col>-<number>
.
The number can be from 2 to 5 and symbolize the number of columns/rows.
Sizes
The syntax is .h-<size>
for height and .w-<size>
for width.
full
size is 100vw or 100vh
100
and 50
are % values.
Variables
The variables are defined in src/styles/variables.scss
file. You can use them in your components by importing them. Use them as much as possible to keep the design consistent.
@import '/src/app/styles/variables.scss';
Responsive
For responsive design, use flex layout and grid layout as much as possible and avoid px
values. Use vw
/vh
rem
and %
values instead.
You can also use the following mixins:
@import '/src/app/styles/mixins.scss';
.my-class {
//default styles (in this case for width 300px to 600px)
@include width-under(300px) {
// styles for width under 300px
}
@include width-over(600px) {
// styles for width over 600px
}
}