Article in Russian README_ru.md
The previous article describes how to create an application framework in Angular, which implements the ability to include two additional applications into one main application. In this article, we will continue to develop this application. Let's connect the Angular Material library and organize the work with assets (with image files) for each additional application.
The prerequisites are detailed in the previous article. Let us indicate briefly:
- Node.js version 10.9.0 or later;
- Package manager npm version 6.14.8 or later;
- Angular version 10 or later;
Create a directory for the project go to it:
$ mkdir /home/alexey/ws_ts3/crm-simple2/
$ cd /home/alexey/ws_ts3/crm-simple2/
Copy the project files from the previous article into it github-crm-simple1. However, you can delete the files img-*.png
.
Start installation of all required packages:
$ npm install
The Angular Material library contains many useful components that will help us create a robust and beautiful application. A description of this library can be found on the website https://material.angular.io/.
Let's add the Angular Material version 10 library to the project, since Angular version 10 was installed.
$ npx ng add @angular/material@10
During installation, you need to specify a theme (for example 'Indigo/Pink'). Since all components of this library support the skin (one of the pre-installed or custom). And for this you need to specify the default theme.
And agree to install the animation library. It will provide smooth animation of work of standard components of the Angular Material library (such as: buttons, radio buttons, and so on). The spinner component will not work correctly without animation.
We indicate the default answer for the rest of the parameters.
Since this library has its own set of styles, this set is automatically added to the project description file.
./angular.json
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
The Angular Material library will be required, since it is planned to use the MatTable component to display the table.
Let's make adjustments to the global menu template to make it look more beautiful.
To continue, go to the component directory app
:
$ cd /home/alexey/ws_ts3/crm-simple2/projects/app-client/src/app/
Working with customer data requires a service that can access the API.
Let's create a service for receiving customer data in a separate subdirectory services
:
$ mkdir services
$ cd services
$ npx ng generate service client-api
$ cd ..
We implement receiving data on clients in the service (for example, from a json file).
This service uses an object of class HttpClient
and therefore add HttpClientModule
to the module /projects/app-client/src/app/app.module.ts.
Let's start filling the functionality in the additional application for working with clients. There is a client-list
component that corresponds to the /app-client/list
route element. Let's add two child components to it:
c-l-header
to display the header;c-l-middle
to display the middle part (customer list);
To continue, go to the component directory client-list
:
$ cd /home/alexey/ws_ts3/crm-simple2/projects/app-client/src/app/client-list/
Next, we create a component to display the title in the customer list.
Create module and component c-l-header
:
$ npx ng generate module c-l-header
$ npx ng generate component c-l-header --export=true
Let's add some picture to the header component c-l-header
that can be associated with this functionality. This will help the user to intuitively distinguish which business module he is in. In addition to the picture, you can add any other resource.
Add the image file logo-client.png
to the resource directory of the current additional application './projects/app-client/src/assets/img'.
Let's start and check the performance of the entire application with the command:
$ npx ng serve --port 4250
And check the link in the browser: http://localhost:4250/app-client/list
But there is no new picture in the required place. And in the console we see an error:
GET http://localhost:4250/assets/img/logo-client.png 404 (Not Found)
Since the image file was added to the resources of the additional application app-client
, it is not in the resources of the main application crm-simple
. And for verification, the main application is launched in the browser. It is now clear that the resources of the additional application app-client
should be copied to the resource directory of the main application crm-simple
. This can be done through the angular.json
settings.
Add copying of resource files of the additional application app-client
to the resulting resource directory of the main application crm-simple
.
Modifying the file angular.json
:
/home/alexey/ws_ts3/crm-simple2/angular.json
{
"projects": {
"crm-simple": {
"architect": {
"build": {
"options": {
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*",
"input": "projects/app-client/src/assets",
"output": "/assets"
}
],
This can be found at the link https://angular.io/guide/workspace-config#asset-config.
To check, you can run the build of the entire application with the command:
$ npx ng build
When the build is complete, a dist
directory is created, in which all the files for the main and all additional applications will be collected. And now you can check the image file logo-client.png
from the resource directory of the additional application app-client
to the resource directory of the main application.
Thus, it is possible to add two different files with the same name to two additional applications. Then the file that will be copied last will be included in the main project.
A simple solution is proposed to avoid such conflicts. Add another directory (for example, the name of this additional application) in the assets
resource directory of all additional applications. And transfer all the required resources to this new directory (directory of images img
and so on).
After that, in the project file angular.json
you need to add a block to copy the resources of each additional application.
Modifying the file angular.json
:
/home/alexey/ws_ts3/crm-simple2/angular.json
{
"projects": {
"crm-simple": {
"architect": {
"build": {
"options": {
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*",
"input": "projects/app-client/src/assets/app-client",
"output": "/assets/app-client"
}
],
This structure of the resource directory /assets/app-client
must be considered when using these files.
For example, to use the image logo-client.png
, you need to specify:
background: url("/assets/app-client/img/logo-client.png")
This approach eliminates the conflict when adding two different files with the same name to two additional applications. And developers won't waste their time solving these kinds of problems. This method allows you to create independent additional applications that contain all the resources required for their work.
If there is a duplication of the resource file in different additional applications, this will not affect anything. Each business module is lazy loaded and only one business module will be loaded at any given time.
When you initially plan to use a resource file in two additional applications, it makes sense to move that resource file to a secondary library. And then plug this library into each business module. It is also required to add to angular.json
copying the resources of this library to the main application (as described above).
To continue, go to the directory of the additional application app-client
:
$ cd /home/alexey/ws_ts3/crm-simple2/projects/app-client/src/app/client-list/
Previously, a c-l-header
component was created to display the header. Next, we create a component to display the middle part of the c-l-middle
.
Create module and component c-l-middle
:
$ npx ng generate module c-l-middle
$ npx ng generate component c-l-middle --export=true
Let's add the display of the list of clients to this component.
Let's continue filling the functionality in the additional client service application. There is a client-view
component that corresponds to the /app-client/view
route element. This component will display the properties of one selected client.
To get client properties, call the service method ClientApiService.getData(). For example, suppose the header component requires a customer name, and the middle component requires all other customer properties. Thus, the ClientApiService.getData() method will be called in these two components. Before calling this method, you need to determine the client ID from the route parameters. For this, a subscription to receive route data is organized. And since this data is required in two components, the subscription must be organized the same in two components. If you are subscribing to an event, you should remember to unsubscribe from this event.
A more rational solution is to move the organization of subscription / unsubscription, receiving general data to the level of the parent component. Conclusion: the main task of the component associated with the route is to get all the data for its children. And all this data will be passed through the input parameters of the child components. This way, these child components can be easily reused elsewhere as needed.
To continue, go to the component directory client-view
:
$ cd /home/alexey/ws_ts3/crm-simple2/projects/app-client/src/app/client-view/
Next, we create a component to display the title.
Create module and component c-v-header
:
$ npx ng generate module c-v-header
$ npx ng generate component c-v-header --export=true
Let's implement the c-v-header header component similarly to the c-l-header component described above. And we will add a link to be able to return to the list of clients.
Create a component to display the middle section c-v-middle
.
Create module and component c-v-middle
:
$ npx ng generate module c-v-middle
$ npx ng generate component c-v-middle --export=true
Let's add client properties mapping to this component.
You can now uninstall the add-on navigation component as it is no longer used /projects/app-client/src/app/nav/.
Let's run and check how the additional application works app-client
:
$ npx ng serve --port 4250
And check in the browser the display of the list of clients by the link: http://localhost:4250/app-client/list
And check the display of client properties in the browser by the link: http://localhost:4250/app-client/view/1
Let's do a similar job in the additional task management application.
To continue, go to the directory of the additional application app-task
:
$ cd /home/alexey/ws_ts3/crm-simple2/projects/app-task/
An additional application for working with tasks provides the user with the following capabilities:
- view the list of tasks;
- view the properties of an individual task;
To continue, go to the component directory app
:
$ cd /home/alexey/ws_ts3/crm-simple2/projects/app-task/src/app/
Working with task data requires a service that can access the API.
Let's create a service for receiving task data in a separate subdirectory services
:
$ mkdir services
$ cd services
$ npx ng generate service task-api
$ cd ..
We implement in the service receiving data on tasks (for example, from a json file).
The additional task application has a task-list
component that corresponds to the /app-task/list
route element. Let's add two child components to it:
t-l-header
to display the header;t-l-middle
to display the middle part (task list);
To continue, go to the component directory task-list
:
$ cd /home/alexey/ws_ts3/crm-simple2/projects/app-task/src/app/task-list/
Next, we create a component to display the title in the task list.
Create module and component t-l-header
:
$ npx ng generate module t-l-header
$ npx ng generate component t-l-header --export=true
Add the corresponding image to the t-l-header
header component. The file of this image logo-task.png
will be placed in the resource directory of the current additional application './projects/app-task/src/assets/app-task/img'.
Create module and component t-l-middle
:
$ npx ng generate module t-l-middle
$ npx ng generate component t-l-middle --export=true
Let's add the display of the list of tasks to this component.
Also in the project file angular.json
add a block for copying the resources of this additional application.
Modifying the file angular.json
:
/home/alexey/ws_ts3/crm-simple2/angular.json
{
"projects": {
"crm-simple": {
"architect": {
"build": {
"options": {
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*",
"input": "projects/app-client/src/assets/app-client",
"output": "/assets/app-client"
},
{
"glob": "**/*",
"input": "projects/app-task/src/assets/app-task",
"output": "/assets/app-task"
}
],
Let's continue filling the functionality in the additional application for working with tasks. There is a task-view
component that corresponds to the /app-task/view
route element. This component will display the properties of one selected task.
The main task of this component is to prepare all data for its child components.
To continue, go to the component directory task-view
:
$ cd /home/alexey/ws_ts3/crm-simple2/projects/app-task/src/app/task-view/
Next, we create a component to display the title.
Create module and component t-v-header
:
$ npx ng generate module t-v-header
$ npx ng generate component t-v-header --export=true
Let's implement the t-v-header
header component in the same way as the t-l-header
component described above. And add a link to be able to return to the list of tasks.
Create a component to display the middle section t-v-middle
.
Create module and component t-v-middle
:
$ npx ng generate module t-v-middle
$ npx ng generate component t-v-middle --export=true
Let's add a display of the properties of the current task to this component.
You can now uninstall the add-on navigation component as it is no longer used /projects/app-task/src/app/nav/.
Let's run and check how the additional application works app-client
:
$ npx ng serve --port 4250
And check the display of the list of tasks in the browser by the link: http://localhost:4250/app-task/list
And check the display of task properties by reference in the browser: http://localhost:4250/app-task/view/1
The output is an application that consists of two unrelated additional applications:
- on working with clients
app-client
; - for working with tasks
app-task
;
Each additional application has its own assets that do not overlap with each other.
Source code can be downloaded github-crm-simple2. (Run npm install
before running the application.)
You can launch the project on the StackBlitz website by following the link https://stackblitz.com/github/alx-melnichuk/crm-simple2
When starting a project on the StackBlitz website in the Google Chrome browser version 84, an error occurred:
ERROR: Cross-origin localStorage blocked by browser but required for devserver.
Please enable 3rd party cookies or add an exception for stackblitz.io to resolve.
The solution is described in the article: stackblitz/core#162 In order not to include the 3rd batch of cookies around the world, you can add to exceptions stackblitz.io:
-> chrome://settings/content/cookies
-> Allow -> Add:
[*.]stackblitz.io
As a result of the work done, we have the following conclusions:
- the resources of all additional applications must be copied to the resources directory of the main application (implemented through the
angular.json
settings); - all resource files of the additional application must have an additional subdirectory. For example, the name of the additional application (
/assets/<name-application>/
). This will avoid conflicts with other secondary applications when building into the resource directory of the main application. - in the component that is associated with the route, the data (from the route and other sources) is defined and passed to the child components through the input parameters;