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
- 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/
.
Run ng build
to build the project. The build artifacts will be stored in the dist/
directory.
Run ng test
to execute the unit tests via jest.
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
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.
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.
The AuthService
allows you to authenticate a user and keep it logged in thanks to localstorage with a JWT token.
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( ... )
First you need to import the authService and inject it.
constructor(private readonly authService:AuthService){}
You can retrieve the user's token and check if the user is connected.
const userToken: string = authService.token;
const isUserConnected: boolean = authService.connected;
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
}
});
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
}
});
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
}
});
To disconnect the user and clear their token.
authService.logout();
That's it! You can now use the AuthService
!
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.
Let's create the UsernameModalComponent.
First be sure that the module where UsernameModalComponent is declared has imported the SharedModule
.
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);
}
}
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>
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.
see angular internationalization documentation for more information: angular i18n
<p>{{ 'global.title' | translate }}</p>
constructor(private readonly languageService: LanguageService) {}
ngOnInit() {
this.languageService.getTranslation('global.title').pipe(take(1)).subscribe((translation: string) => {
console.log(translation); // Hello world
});
}
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.
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.
The syntax is .grid-<row/col>-<number>
.
The number can be from 2 to 5 and symbolize the number of columns/rows.
The syntax is .h-<size>
for height and .w-<size>
for width.
full
size is 100vw or 100vh
100
and 50
are % values.
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';
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
}
}