Posted onEdited onInjavascript Symbols count in article: 282Reading time ≈1 mins.
A question from stackoverflow
The following code does not work, do you know why? When you run the code, you will got the error TypeError: Cannot read properties of undefined (reading 'navigate').
Reason: Code executed by setTimeout() is called from an execution context separate from the function from which setTimeout was called. The usual rules for setting the this keyword for the called function apply, and if you have not set this in the call or with bind, it will default to the window (or global) object, even in strict mode. It will not be the same as the this value for the function that called setTimeout. - From MDN- The this problem for setTimeout
That means router is undefined in gotoProduct function. The reason is that this in gotoProduct function is not the instance of AppComponent. It is the instance of Window object.
How to fix it?
Use Arrow function
Arrow function doesn’t have its own this binding, it will inherit the this from the parent scope. So, you can use arrow function in setTimeout function to keep the context of the parent scope.
1 2 3
setTimeout(() => { this.gotoProduct(); }, 1000);
Use bind
You can also use bind function to bind the this to the gotoProduct function.
1
setTimeout(this.gotoProduct.bind(this), 1000);
Conclusion
Becareful when using setTimeout function in Angular. Always use arrow function in setTimeout function to keep the context of the parent scope.
HostListener is a more powerful way to listen for events because it allows you to listen for events on any element, not just the element that the HostListener is attached to. For example, you can listen for the events from document or window object.
Posted onEdited onInangular Symbols count in article: 747Reading time ≈3 mins.
Introduction
In this article, we will explore the Angular Module Federation feature in depth. Please read the first article in this series to get a basic understanding of Module Federation.
remoteEntry.js
Each time you call loadRemoteModule, Angular will fetch the remoteEntry.js file first from the remote server. What is this file? Let’s take a look at the remoteEntry.js file in the remote project.
You can find the remoteEntry.js file in the dist folder of the remote project. The file is generated by the ModuleFederationPlugin in the webpack configuration. But the file under the dist folder is minimized and hard to read. You can read it on Chrome network tab when you call loadRemoteModule.
This file is very large and contains lots of code, you can scroll down to the bottom or search container entry to find key part.
remoteEntry.js mainly contains the following information:
1. The list of modules that are exposed by the remote project.
In the following example, the remote project exposes a module named ./Component from its ./projects/mfe1/src/app/product/product.component.ts file.
remoteEntry.js is a file that contains the list of modules that are exposed by the remote project.
Angular fetches the remoteEntry.js file first before loading the remote module.
The remoteEntry.js file is generated by the ModuleFederationPlugin in the webpack configuration.
shell/host project does not have a remoteEntry.js file.
loadRemoteModule
How loadRemoteModule works in Angular Module Federation? Let’s take a look at the source code of the loadRemoteModule function in the @angular-architects/module-federation package.
The entry point is from the router file, we call loadRemoteModule function to load the remote module. The loadRemoteModule function is defined in the load-remote-module.ts file in the @angular-architects/module-federation package.
Here is the source code of the loadRemoteModule function from webpack:///node_modules/@angular-architects/module-federation-runtime/fesm2022/angular-architects-module-federation-runtime.mjs
We can see that it uses the import function to load the remoteEntry.js file. The import function is a dynamic import function that fetches the remoteEntry.js file from the remote server. After loading the remoteEntry.js file, it calls the initRemote function to initialize the remote container. This container is used to get the remote module later.
After loading the remoteEntry.js file, then it calls lookupExposedModule function to get the module from the remote project.
Posted onEdited onInangular Symbols count in article: 267Reading time ≈1 mins.
Constructor vs ngOnInit
What’s the difference between constructor and ngOnInit in Angular? constructor is a TypeScript class constructor while ngOnInit is an Angular lifecycle hook called by Angular to indicate that Angular is done creating the component. a common question is when to use constructor and when to use ngOnInit?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
import {Component, OnInit} from'@angular/core';
@Component({ selector: 'app-product', standalone: true, imports: [], templateUrl: './product.component.html', styleUrl: './product.component.scss' }) exportclassProductComponentimplementsOnInit { constructor() { // What should be done here? } ngOnInit() { // What should be done here? } }
constructor is used to initialize the class properties, but it’s not the right place to do any initialization that depends on Angular services or inputs. ngOnInit is the right place to do such initialization.
ngOnInit is an Angular lifecycle hook that is called after Angular has initialized all data-bound properties of a directive. It’s the right place to put initialization logic that depends on Angular services or inputs.
Posted onEdited onInangular Symbols count in article: 221Reading time ≈1 mins.
Introduction
How Angular handles assets file such as images, fonts and other static files in the project.
Angular 18+
The following config from angular.json indicates that the Angular builder will copy everything under public folder to the outputPath: dist folder.
1 2 3 4 5 6
"assets":[ { "glob":"**/*", "input":"public" } ],
So
Put your logo file(angular.svg) under public folder
In your component file, you can just use it as below:
1
<img src="angular.svg" alt="Angular Logo" />
To verify whether the image was in the final build, you can run the following command and check the dist folder under project root.
1
ng build
If you want to use the old way, you can config angular.json file as below, in this config, we copy everything under src/assets folder to dist folder.(Note, if you omit the output option, the assets folder will not copied, it only copy images folder)
Posted onEdited onInangular Symbols count in article: 372Reading time ≈1 mins.
Introduction
When you are developing an Angular application, you may need to call some APIs from the backend server. But in the development environment, the backend server may not be ready yet, or you want to use a local backend server for testing. In this case, you can use the proxy configuration to redirect the API requests to another server.
1. Create proxy file
Create a new file named proxy.conf.json in the root src folder of your Angular project. The content of the file should look like this:
In this example, we are redirecting all requests that start with /api to http://localhost:3000. You can change the target URL to your backend server address.
2. Update angular.json
Open the angular.json file in the root of your Angular project. Find the serve section under projects > your-project-name > architect > serve > configuration > development. Add the proxyConfig option with the path to the proxy.conf.json file.
1 2 3 4 5 6 7 8 9 10 11
"serve":{ "builder":"@angular-devkit/build-angular:dev-server", "configurations":{ "production":{ "browserTarget":"angular-16:build:production" }, "development":{ "proxyConfig":"src/proxy.conf.json",// <--- Add this line "browserTarget":"angular-16:build:development" } },
Note: you can also serve the proxy file in ng serve command as below.
1
ng serve --proxy-config src/proxy.conf.json
3. Start the development server
Now you can start the development server with the following command:
// Start the server app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); });
5. Troubleshooting
Make sure your backend server is running on the specified target URL. you can test your api from the browser or using tools like Postman to make sure it works.
Don’t call backend api directly, only calls made to dev server will be proxied.
1 2 3 4 5 6 7 8 9 10 11
fetchUser() { returnthis.http.get<any>('/api/user'); // OK }
fetchUser() { returnthis.http.get<any>('http://localhost:4200/api/user'); // OK }
Posted onEdited onInnode.js Symbols count in article: 189Reading time ≈1 mins.
The npm install --production command is used to install only the dependencies listed under the dependencies section in the package.json file, excluding the devDependencies. This is typically used in production environments where you want to avoid installing unnecessary development tools and libraries.
Here is a brief explanation of when and why you might use npm install --production:
Production Deployment: When deploying an application to a production environment, you often want to minimize the size of the deployment package and reduce the number of installed packages to only those necessary for running the application. This helps in improving performance and security.
Server Environments: In server environments where the application is running, you generally do not need development tools like testing frameworks, linters, or build tools. Using npm install --production ensures that only the essential packages are installed.
Docker Images: When building Docker images for your application, using npm install --production can help create smaller and more efficient images by excluding development dependencies.
Example usage:
1
npm install --production
This command will read the package.json file and install only the packages listed under dependencies, ignoring those under devDependencies.
Posted onEdited onInrxjs Symbols count in article: 432Reading time ≈2 mins.
Introduction
In RxJS, there is a timeout operator, it’s used to throw an error if the source observable does not emit a value within a specified timeout duration.
Use case
In Angular, the HttpClient service is used to make HTTP requests. Sometimes, we want to set a timeout for the request, if the request does not complete within the specified time, we want to cancel the request and show an error message to the user.
getData() { returnthis.http.get('https://example.com/api/data').pipe( timeout(10000), // 10 seconds catchError((error) => { // Handle timeout or other errors console.error('Request timed out or failed', error); returnthrowError(error); }) ); } }
But, wait, what if I want to add this timeout for all my requests? Do I need to add the timeout operator to every request? The answer is no, you can create an interceptor to add the timeout operator to all requests.
Now, all your HTTP requests will have a timeout of 10 seconds.
But, what if I want to set different timeout values for some specific requests? You can add a custom header to the request and check it in the interceptor, if custom header timeout exists, use the custom timeout value, otherwise use the default timeout value.
Because the first test case only reach the first if branch in getDuration function, it only call getIdleTime once. So there is no problem.
But the second test case need to reach the second if branch in getDuration function, but the mock only take effect in the first if branch, the second branch will still use the original implementation of getIdleTime function, so the test failed.
To fix this issue, you can use mockImplementation instead of mockImplementationOnce in the second test case.
Posted onEdited onInnx Symbols count in article: 409Reading time ≈1 mins.
Nx Library Types by functionality
Feature library
此类library主要负责和业务相关的组件和页面等等。
UI library
此类Library主要是负责和UI相关的功能。
Data access library
此类library主要负责和数据相关的功能,比如和后端API交互,数据处理等。
Utility library
此类library主要负责工具和辅助功能,比如一些通用的函数,服务等。
Nx library types by buildable and publishable
Workspace library(Normal library)
Create without any options, it’s a normal library.
1
nx g @nx/angular:lib libName
No ng-packagr file generated.
No package.json file generated .
No targets/build section in project.json file.
This type of libraries is intended to be used within the monorepo. It was imported by apps or other libraries in the same monorepo. It can’t be builded or published independently.
Buildable library
Create by adding buildable option.
1
nx g @nx/angular:lib libName --buildable
Add ng-packagr file to root of the library.
Add package.json file to root of the library.
name property in package.json is the libName.
Add targets/build section in project.json file.
Executor of build is: "executor": "@nx/angular:ng-packagr-lite"
Buildable libraries are similar to “publishable libraries” described above. Their scope however is not to distribute or publish them to some external registry. Thus they might not be optimized for bundling and distribution.
Buildable libraries are mostly used for producing some pre-compiled output that can be directly referenced from an Nx workspace application without the need to again compile it. A typical scenario is to leverage Nx’s incremental building capabilities.
Publishable library
Create by adding publishable and importPath option. importPath is the path that the library will be imported from, will be used as name of the package.
1
nx g @nx/angular:lib libName --publishable --importPath=@myorg/libName
Add ng-packagr file to root of the library.
Add package.json file to root of the library.
name property in package.json is the importPath.
Add targets/build section in project.json file.
Executor of build is: "executor": "@nx/angular:package"
Publishable libraries is intended to be published outside of the monorepo, and can be imported by other projects.