Skip to content
Luis Aviles

AUTHOR

Luis Aviles

Senior Software Engineer

Luis is a Senior Software Engineer and Google Developer Expert in Web Technologies and Angular. He is an author of online courses, technical articles, and a public speaker. He has participated in different international technology conferences, giving technical talks, workshops, and training sessions. He’s passionate about the developer community and he loves to help junior developers and professionals to improve their skills. When he’s not coding, Luis is doing photography or Astrophotography.

Select...
Select...
How to Create Standalone Components in Angular cover image

How to Create Standalone Components in Angular

Delve into the power of Angular's component-based architecture, focusing on standalone components that facilitate modularity and reusability....

How to Manage Breakpoints using BreakpointObserver in Angular cover image

How to Manage Breakpoints using BreakpointObserver in Angular

Defining Breakpoints is important when you start working with Responsive Design and most of the time they're created using CSS code. For example: `css .title { font-size: 12px; } @media (max-width: 600px) { .title { font-size: 14px; } } ` By default, the text size value will be 12px, and this value will be changed to 14px when the viewport gets changed to a smaller screen (a maximum width of 600px). That solution works. However, what about if you need to listen_ for certain breakpoints to perform changes in your application? This may be needed to configure third-party components, processing events, or any other. Luckily, Angular comes with a handy solution for these scenarios: the BreakpointObserver. Which is a utility for checking the matching state of @media queries. In this post, we will build a sample application to add the ability to configure certain breakpoints, and being able to listen_ to them. Project Setup Prerequisites You'll need to have installed the following tools in your local environment: - Node.js**. Preferably the latest LTS version. - A package manager**. You can use either NPM or Yarn. This tutorial will use NPM. Creating the Angular Project Let's start creating a project from scratch using the Angular CLI tool. `bash ng new breakpointobserver-example-angular --routing --prefix corp --style css --skip-tests ` This command will initialize a base project using some configuration options: - --routing`. It will create a routing module. - --prefix corp`. It defines a prefix to be applied to the selectors for created components(`corp` in this case). The default value is `app`. - --style scss`. The file extension for the styling files. - --skip-tests`. it avoids the generations of the `.spec.ts` files, which are used for testing Adding Angular Material and Angular CDK Before creating the breakpoints, let's add the Angular Material components, which will install the Angular CDK` library under the hood. `bash ng add @angular/material ` Creating the Home Component We can create a brand new component to handle a couple of views to be updated while the breakpoints are changing. We can do that using the ng generate` command. `bash ng generate component home ` Pay attention to the output of the previous command since it will show you the auto-generated files. Update the Routing Configuration Remember we used the flag --routing` while creating the project? That parameter has created the main routing configuration file for the application: `app-routing.module.ts`. Let's update it to be able to render the `home` component by default. `ts // app-routing.module.ts import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HomeComponent } from './home/home.component'; const routes: Routes = [ { path: '', component: HomeComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { } ` Update the App Component template Remove all code except the router-outlet` placeholder: `html ` This will allow rendering the home` component by default once the routing configuration is running. Using the BreakpointObserver The application has the Angular CDK installed already, which has a layout` package with some utilities to build responsive UIs that _react_ to screen-size changes. Let's update the HomeComponent`, and inject the `BreakpointObserver` as follows. `ts //home.component.ts import { Component, OnInit } from '@angular/core'; import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; @Component({ selector: 'corp-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit { readonly breakpoint$ = this.breakpointObserver .observe([Breakpoints.Large, Breakpoints.Medium, Breakpoints.Small, '(min-width: 500px)']) .pipe( tap(value => console.log(value)), distinctUntilChanged() ); constructor(private breakpointObserver: BreakpointObserver) { } ngOnInit(): void { } } ` Once the BreakpointObserver` is injected, we'll be able to evaluate media queries to determine the current screen size, and perform changes accordingly. Then, a breakpoint$` variable references an _observable_ object after a call to the `observe` method. The observe** method gets an observable of results for the given queries, and can be used along with predetermined values defined on `Breakpoints` as a constant. Also, it's possible to use custom breakpoints such as (min-width: 500px)`. Please refer to the documentation to find more details about this. Next, you may need to subscribe_ to the `breakpoint$` observable to see the emitted values after matching the given queries. Again, let's update the home.component.ts` file to do that. `ts // home.component.ts // .... other imports import { distinctUntilChanged, tap } from 'rxjs/operators'; @Component({ //.... }) export class HomeComponent implements OnInit { Breakpoints = Breakpoints; currentBreakpoint:string = ''; // ... readonly breakpoint$ = this.breakpointObserver constructor(private breakpointObserver: BreakpointObserver) { } ngOnInit(): void { this.breakpoint$.subscribe(() => this.breakpointChanged() ); } private breakpointChanged() { if(this.breakpointObserver.isMatched(Breakpoints.Large)) { this.currentBreakpoint = Breakpoints.Large; } else if(this.breakpointObserver.isMatched(Breakpoints.Medium)) { this.currentBreakpoint = Breakpoints.Medium; } else if(this.breakpointObserver.isMatched(Breakpoints.Small)) { this.currentBreakpoint = Breakpoints.Small; } else if(this.breakpointObserver.isMatched('(min-width: 500px)')) { this.currentBreakpoint = '(min-width: 500px)'; } } } ` In the above code, the ngOnInit` method is used to perform a _subscription_ to the `breakpoint$` observable and the method `breakpointChanged` will be invoked every time a breakpoint match occurs. As you may note, the breakpointChanged` method verifies what Breakpoint value has a match through `isMatched` method. In that way, the current component can perform changes after a match happened (in this case, it just updates the value for the `currentBreakpoint` attribute). Using Breakpoint values on the Template Now, we can set a custom template in the home.component.html` file and be able to render a square according to the `currentBreakpoint` value. `html {{ currentBreakpoint }} Large Medium Small Custom ` The previous template will render the current media query value at the top along with a rectangle according to the size: Large, Medium, Small or Custom. Live Demo and Source Code Want to play around with this code? Just open the Stackblitz editor or the preview mode in fullscreen. Find the complete angular project in this GitHub repository: breakpointobserver-example-angular. Do not forget to give it a star ⭐️ and play around with the code. Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work....

How to Build a Slideshow App Using Swiper and Angular cover image

How to Build a Slideshow App Using Swiper and Angular

Luis Aviles...

How to Update the Application Title based on Routing Changes in Angular cover image

How to Update the Application Title based on Routing Changes in Angular

Have you tried to update the document's title of your application? Maybe you're thinking that applying interpolation should be enough: `html {{myCustomTitleVariable}} ` That solution is not going to work since the ` element is outside of the scope of the Angular application. In fact, the root component of your app is within `` tag, and the title is part of the `` element. Luckily, Angular provides the Title service with the methods to read the current title of the application, and a setTitle(title)` to update that value. However, what happens if you need to update the title on routing changes? Also, you may consider updating it on certain components for Analytics purposes. In this blog post, I'll explain step-by-step how to create a custom Title service to have full control over the title of the current HTML document for your application. Project Setup Prerequisites You'll need to have installed the following tools in your local environment: - Node.js**. Preferably the latest LTS version. - A package manager**. You can use either NPM or Yarn. This tutorial will use NPM. Creating the Angular Project Let's assume we'll need to build an application with the following routes as requirements: `txt /home |- Renders a home component /products |- Renders a list of products /products/ |- Renders a product detail based on its Identifier The app redirects to /home path by default ` Now, let's create the project from scratch using the Angular CLI tool. `bash ng new angular-update-title --routing --prefix corp --style css --skip-tests ` This command will initialize a base project using some configuration options: - --routing`. It will create a routing module. - --prefix corp`. It defines a prefix to be applied to the selectors for created components(`corp` in this case). The default value is `app`. - --style css`. The file extension for the styling files. - --skip-tests`. it avoids the generations of the `.spec.ts` files, which are used for testing Creating the Modules and Components Once we got the initial structure of the app, we'll continue running the following commands to create a separate module for /home` and `/products`, which are the main paths of the project: `bash ng generate module home --routing ng generate component home/home ng generate module products --routing ng generate component products/products ng generate component products/product-detail ` The `--routing` flag can be using also along with `ng generate module` to create a routing configuration file for that module. Creating the Title Service Similar to the previous section, we will create a shared` module to hold the `Title` service. Both can be generated with the following commands: `bash ng generate module shared --module app ng generate service shared/services/title ` The `--module app` flag is used to "link" the brand new module to the pre-existing `app.module.ts` file. The Routing Configuration Open the app-routing.module.ts` file, and create the initial routes. `ts // app-routing.module.ts const routes: Routes = [ { path: '', pathMatch: 'full', redirectTo: 'home' }, { path: 'home', component: HomeComponent, data: { pageTitle: 'Home' } }, { path: 'products', loadChildren: () => import('./products/products.module').then(m => m.ProductsModule) } ]; ` By default, the application will redirect to the `home` path. When the router loads the `home` path, a `HomeComponent` will be rendered. The `products` path will be loaded using the _lazy loading_ feature. Pay attention to the data provided to the home` path. It contains the configured title through `pageTitle` string. Next, open the products-routing.module.ts` file to enable an additional configuration to load the _Products_ and the _Product Detail_ page. `ts // products-routing.module.ts const routes: Routes = [ { path: '', component: ProductsComponent, children: [ { path: ':id', component: ProductDetailComponent, } ], data: { pageTitle: 'Products' } }, ]; ` The router will render the `ProductsComponent` by default when the path matches to `/products`. This route also defines custom data to be rendered as titles later. When the path also adds an Id on `/products/:id`, the router will render the `ProductDetailComponent`. The Title Service Implementation It's time to implement the custom Title Service for our application. `ts // title.service.ts import { Injectable } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { BehaviorSubject, merge, Observable } from 'rxjs'; import { filter, map, tap } from 'rxjs/operators'; const DEFAULTTITLE = 'Corp'; @Injectable({ providedIn: 'root' }) export class TitleService { title$ = new BehaviorSubject(DEFAULTTITLE); private titleRoute$: Observable = this.router.events.pipe( filter((event) => event instanceof NavigationEnd), map(() => this.getPageTitle(this.activatedRoute.firstChild)) ); private titleState$ = merge(this.title$, this.titleRoute$).pipe( filter((title) => title !== undefined), tap((title) => { this.titleService.setTitle(${DEFAULT_TITLE} - ${title}`); }) ); constructor( private router: Router, private activatedRoute: ActivatedRoute, private titleService: Title ) { this.titleState$.subscribe(); } private getPageTitle( activatedRoute: ActivatedRoute | null ): string | undefined { while (activatedRoute) { if (activatedRoute.firstChild) { activatedRoute = activatedRoute.firstChild; } else if ( activatedRoute.snapshot.data && activatedRoute.snapshot.data['pageTitle'] ) { return activatedRoute.snapshot.data['pageTitle'] as string; } else { return undefined; } } return undefined; } } ` The above service implementation could be understood in just a few steps. First, we'll need to make sure to inject the `Router`, `ActivatedRoute` and `Title` services in the constructor. The `title$` attribute contains the initial value for the title("Corp"), which will be emitted through a _BehaviorSubject_. The `titleRoute$` is an Observable ready to emit any `pageTitle` value defined in the current route. It may use the parent's _pageTitle_ otherwise. The `titleState$` is an Observable ready to _listen_ to either `title$` or `titleRoute$` values. In case incoming value is defined, it will call the Angular Title service to perform the update. The `getPageTitle` method will be in charge of obtaining the `pageTitle` of the current route if it is defined or the title of the parent otherwise. Injecting the Title Service One easy way to apply the custom Title Service in the whole application is by updating the app.module.ts` file and injecting it into the constructor. `ts // app.module.ts export class AppModule { constructor(public titleService: TitleService) {} } ` In that way, once the default component gets rendered, the title will be displayed as Corp - Home`. If you click on Go to Products_ link, then a redirection will be performed and the Title service will be invoked again to display `Corp - Products` at this time. However, we may need to render a different title according to the product detail. In this case, we'll show Corp - Product Detail - :id` where the `Id` matches with the current route parameter. `ts // product-detail.component.ts import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { map, Subscription, tap } from 'rxjs'; import { TitleService } from 'src/app/shared/services/title.service'; @Component({ selector: 'corp-product-detail', templateUrl: './product-detail.component.html', styleUrls: ['./product-detail.component.css'], }) export class ProductDetailComponent implements OnInit, OnDestroy { protected subscription = new Subscription(); productId$ = this.route.params.pipe(map((params) => params['id'])); constructor( private route: ActivatedRoute, private titleService: TitleService ) {} ngOnInit(): void { const productIdSubscription = this.productId$ .pipe( tap((id) => this.titleService.title$.next(Product Detail - ${id}`)) ) .subscribe(); this.subscription.add(productIdSubscription); } ngOnDestroy(): void { this.subscription.unsubscribe(); } } ` Let's explain the implementation of this component: The constructor injects the `ActivatedRoute` and the custom `TitleService`. The `productId$` is the _Observable_ which is going to emit the `Id` parameter every time it changes in the URL. Once the component gets initialized, we'll need to _subscribe_ to the `productId$` _Observable_ and then emit a new value for the title after creating a new string using the `id`. That's possible through the `titleService.title$.next()` method. When the component gets _destroyed_, we'll need to _unsubscribe_ from the `productIdSubscription`. We're ready to go! Every time you select a product, the ProductDetail` component will be rendered, and the title will be updated accordingly. Live Demo and Source Code Want to play around with the final application? Just open the following link in your browser: https://luixaviles.github.io/angular-update-title. Find the complete angular project in this GitHub repository: angular-update-title-service. Do not forget to give it a star ⭐️, and play around with the code. Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work....

Why Migrate from Protractor to Cypress? cover image

Why Migrate from Protractor to Cypress?

Great memories come to my mind when I read about Protractor. I remember those days when I was working actively using the AngularJS framework to build Single-Page Applications, and Protractor was the most viable option for writing E2E(End-to-End) tests. In fact, Protractor was created in 2013 when AngularJS was the most popular web framework, and it allowed developers to run tests through a web application in a real browser, along with interaction commands to mimic the user behavior. As expected, the ecosystem around JavaScript technologies has been changing continuously, with new tools emerging that afford developers more modern options for writing tests. Is this the right time to think of other alternatives? In this post, I'll give you some reasons to migrate to Cypress since it's one of the best alternatives for E2E testing. Protractor Will Be Deprecated The Angular team has announced the deprecation of Protractor in May 2021. Some of the reasons behind these deprecation plans are: - State of Protractor**. Protractor is dependent on `selenium-webdriver`, and is not able to upgrade to the new version without introducing a huge breaking change, and forcing users to do a migration for all their tests. - Web Testing Landscape**. There are different testing solutions today, and they offer better stability and improved support for features like cross-browser testing. The current deprecation timeline looks like this: - Angular version 12 (May 2021) - Announcement of deprecation - Angular CLI will not include Protractor for new projects - Show warning when @angular-devkit/build-angular:protractor is used - Only PRs that address security issues and fixes for browser releases that break Protractor tests will be merged - Angular version 15 (end of 2022) - End of development on Protractor There is a closed RFC with the future of Angular E2E & Plans for Protractor on GitHub with more details about this. Cypress supports Interactive Testing First, we'll need to understand that as part of the Cypress tooling, there's a Test Runner and a Dashboard Service available for your test suite. - The Test Runner** is an open source project available to run your test suite in an interactive environment in real-time. While it's in progress, a command log will display the results of your tests. - The Dashboard Service** can record your test run, and enables access to your results. The above screenshot shows an example of the test runner in real-time, where you will find an intuitive way to navigate through the different steps in your tests. Cypress Has a Huge Ecosystem That’s true if you consider the number of resources you can find about Cypress nowadays. It's evolving every day, and it's adopted by developers and QA engineers using modern JavaScript frameworks. As part of the Angular ecosystem, we can mention one of the biggest players: Nx, which is widely used with Angular projects. For this tool, Cypress is used by default while generating new applications. > Cypress will be used by default when generating new applications. If you want to continue using Protractor, set the e2eTestRunner to protractor in the schematics section of the angular.json file. In case you're using Nx, please refer to the Updating your E2E testing configuration guide for more information. How to Perform the Migration? The Cypress team is actively working on the Cypress Angular Schematic, which allows adding Cypress for E2E testing in your Angular projects. You'll need to run the following command and pay attention to the output. `bash ng add @cypress/schematic ` Here's what happens in the background: Cypress tooling gets installed in your project. Some npm scripts are added in your `package.json` file to run your tests. `"cypress:open": "cypress open"`. This script will open your browser and render the content of your app. `"cypress:run": "cypress run"`. This script will run your tests headlessly (the browser will not be opened). Cypress configuration files and directories are created. Initial tests and scripts are generated so that you can run Cypress for the first time. It has the ability to update the default `ng e2e` command to use Cypress instead of Protractor. If those steps don't work for you, you may need to do the upgrade manually and the good news is that Cypress documentation has you covered already: Manual Installation. Are you ready to learn more about Cypress? Do not miss this topic on our blog site. Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work....

Angular CDK: Sorting Items using Drag & Drop cover image

Angular CDK: Sorting Items using Drag & Drop

There is no doubt that we are living in an era where the interactivity of your web application is a very important part of retaining your users. For example, it's very common to find requirements to perform a Drag and Drop operation inside of your web application: Upload image files, prioritize activities (think in a Task Board) or even sort elements in your web app. Luckily, the Angular Team has implemented a library to enable the creating of drag-and-drop interfaces in your Angular application. In this blog post, we'll see a practical example to allow sorting Items using Drag and Drop considering a single and a mixed-orientation layout. Project Setup Prerequisites You'll need to have installed the following tools in your local environment: - Node.js**. Preferably the latest LTS version. - A package manager**. You can use either NPM or Yarn. This tutorial will use NPM. Creating the Angular Project Let's create a project from scratch using the Angular CLI tool. `bash ng new angular-cdk-sorting-drag-drop --routing --prefix corp --style css --skip-tests ` This command will initialize a base project using some configuration options: - --routing`. It will create a routing module. - --prefix corp`. It defines a prefix to be applied to the selectors for created components(`corp` in this case). The default value is `app`. - --style css`. The file extension for the styling files. - --skip-tests`. it avoids the generations of the `.spec.ts` files, which are used for testing. Creating a Component Before using the Angular CDK library, let's create a component using the command ng generate` as follows. `bash ng generate component dashboard ` Now, pay attention to the output of the previous command, since it will auto-generate a couple of files: `bash CREATE src/app/dashboard/dashboard.component.css (0 bytes) CREATE src/app/dashboard/dashboard.component.html (24 bytes) CREATE src/app/dashboard/dashboard.component.ts (288 bytes) UPDATE src/app/app.module.ts (487 bytes) ` The last output line shows the affected module, where now the component belongs to. Using Angular CDK Install the Angular CDK library The Angular Component Dev Kit (CDK) is a set of behavior primitives for building UI components. In fact, they're used to build the widely used Angular Components. Let's start the package using NPM. `bash npm install --save @angular/cdk ` Import the Drag&Drop Module Before using any Drag&Drop feature from the Angular CDK library, it's required to import the DragDropModule` module from `@angular/cdk/drag-drop`, and then use it in the application module (in this case): `ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { DashboardComponent } from './dashboard/dashboard.component'; @NgModule({ declarations: [AppComponent, DashboardComponent], imports: [BrowserModule, AppRoutingModule, DragDropModule], providers: [], bootstrap: [AppComponent], }) export class AppModule {} ` Sorting Items in a Single Orientation Layout As you may find in the Drag and Drop documentation, you can set the orientation of any list using the property cdkDropListOrientation` as the next example shows: `html {{timePeriod}} ` If no orientation is configured, the directive cdkDropList` will set `vertical` as the default orientation. On other hand, the CSS code for the main container of that list could look like this: `css example-container { display: flex; flex-direction: row; } ` Support on a Mixed Orientation Layout You may assume that updating the CSS of the above main container and setting the flex-wrap: wrap` property should be enough to support a mixed orientation layout. For example: `css .example-container { display: flex; flex-wrap: wrap; flex-direction: row; gap: 10px; margin: 10px; } ` Saddly you may find some issues with the Drag & Drop behavior after applying those changes. I had to deal with these problems recently in a project, and the good news is I found a helpful workaround_ that, at least on my side, is working fine. Sorting Items in a flex-wrap Layout One possible solution for supporting a mixed layout through flex-wrap: wrap` is the use of a wrapper element via `cdkDropListGroup` directive. You may consider that any `cdkDropList` that is added under a group will be connected to all other lists automatically. `html {{ item }} ` Then, the associated TypeScript code for the previous template may need to consider an array of elements to generate the list: `ts // dashboard.component.ts import { Component, ElementRef, ViewChild } from '@angular/core'; @Component({ selector: 'corp-dashboard', templateUrl: './dashboard.component.html', styleUrls: ['./dashboard.component.css'], }) export class DashboardComponent { public items: Array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; } ` Next, we'll need to handle the cdkDragEntered`, `cdkDragMoved` and `cdkDragDropped` events, which are related to the CdkDrag element(an item that can be moved inside a `CdkDropList` container). `ts // dashboard.component.ts import { CdkDragDrop, CdkDragEnter, CdkDragMove, moveItemInArray, } from '@angular/cdk/drag-drop'; @Component({ ... }) export class DashboardComponent { @ViewChild('dropListContainer') dropListContainer?: ElementRef; dropListReceiverElement?: HTMLElement; dragDropInfo?: { dragIndex: number; dropIndex: number; }; dragEntered(event: CdkDragEnter) { const drag = event.item; const dropList = event.container; const dragIndex = drag.data; const dropIndex = dropList.data; this.dragDropInfo = { dragIndex, dropIndex }; const phContainer = dropList.element.nativeElement; const phElement = phContainer.querySelector('.cdk-drag-placeholder'); if (phElement) { phContainer.removeChild(phElement); phContainer.parentElement?.insertBefore(phElement, phContainer); moveItemInArray(this.items, dragIndex, dropIndex); } } dragMoved(event: CdkDragMove) { if (!this.dropListContainer || !this.dragDropInfo) return; const placeholderElement = this.dropListContainer.nativeElement.querySelector( '.cdk-drag-placeholder' ); const receiverElement = this.dragDropInfo.dragIndex > this.dragDropInfo.dropIndex ? placeholderElement?.nextElementSibling : placeholderElement?.previousElementSibling; if (!receiverElement) { return; } receiverElement.style.display = 'none'; this.dropListReceiverElement = receiverElement; } dragDropped(event: CdkDragDrop) { if (!this.dropListReceiverElement) { return; } this.dropListReceiverElement.style.removeProperty('display'); this.dropListReceiverElement = undefined; this.dragDropInfo = undefined; } } ` Let's explain when those methods are invoked: - The dragMoved` method will be invoked once the user starts to drag an item. It's expected to run it several times while being moved around the screen. In other words, the event will be fired for every pixel change in the position. - The dradEntered` method will be invoked once the user has moved an item **into a new container**, which is, another `cdkDropList` element. - The dragDropped` method will be invoked after the user drops the item inside a `cdkDropList`(container element). One important aspect of this solution is the use of the cdkDragEntered` event to identify when the user has moved an element in the screen. Also, in order to avoid a weird behavior of the `.cdk-drag-placeholder` element, there is a DOM manipulation for it just to make sure the current layout is not broken. Then, the data model is changed at the end through the `moveItemInArray` method, which is part of the CDK drag-drop API Live Demo Wanna play around with this code? Just open the Stackblitz editor: A Simplified Version If you're curious about a simplified workaround_, you can take a look at this demo, which is the initial version for this solution. Keep in mind it doesn't include the fix for the `.cdk-drag-placeholder` element handling I described above. Source Code of the Project Find the complete project in this GitHub repository: angular-cdk-sorting-drag-drop. Do not forget to give it a star ⭐️ and play around with the code. Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work....

How to integrate Web Components using Lit in Angular cover image

How to integrate Web Components using Lit in Angular

In previous posts, I explained how to create projects in Lit and TypeScript from scratch. Also, I covered different topics about Web Components, and Single-Page Applications, using them. In this tutorial, I'll explain the needed steps to integrate these Web Components in Angular. Spoiler: You can find the source code of a demo project at the end. Project Setup Prerequisites You'll need to have installed the following tools in your local environment: - Node.js**. Preferably the latest LTS version. - A package manager**. You can use either NPM or Yarn. This tutorial will use NPM. Creating the Angular Project Let's create a project from scratch using the Angular CLI tool. `bash ng new angular-lit-web-components --routing --prefix corp --style css --skip-tests ` This command will initialize a base project using some configuration options: - --routing`. It will create a routing module. - --prefix corp`. It defines a prefix to be applied to the selectors for created components(`corp` in this case). The default value is `app`. - --style css`. The file extension for the styling files. - --skip-tests`. It avoids the generations of the `.spec.ts` files, which are used for testing. Install Lit > Lit is a simple library for building fast, lightweight web components. Lit is available via npm, let's install it as a new dependency for the current project. `bash npm install --save lit ` Find more information about Lit here. Install Web Components Polyfills There are several ways to install the Web Components polyfills. In this case, we'll install it using npm. `bash npm install --save @webcomponents/webcomponentsjs ` Then you can use the webcomponents-loader.js`, which allows loading a minimum polyfill bundle. Update the Angular Configuration You can load the polyfills using a ` tag into the `index.html` file. However, the _Angular way_ to do it is by adding a new `asset` and `script` configurations into your `angular.json` file as follows: `json "architect": { "build": { ... "options": { ... "assets": [ "src/favicon.ico", "src/assets", { "glob": "{loader.js,bundles/*.js}", "input": "nodemodules/@webcomponents/webcomponentsjs", "output": "nodemodules/@webcomponents/webcomponentsjs" } ], "scripts": [ "nodemodules/@webcomponents/webcomponentsjs/webcomponents-loader.js" ] ... }, "configurations": { ... }, ... }, } ` Both input` and `output` properties must be relative to the project's root path. The same applies to the script imported. Creating your Web Components using Lit The project setup has been completed, and now it's time to create our first web component using Lit. Let's create an src/web-components/card-user` folder, and then create two files: `card-user.ts` and `user.ts`. The user.ts` file will define a common model used by the Web Component implementation, and the Angular project: `ts // user.ts export interface User { id: number; fullName: string; role: string; avatar?: string; } ` Next, let's define the TypeScript code for our Web component into card-user.ts` file: `ts // card-user.ts import { LitElement, html, css } from 'lit'; import { property, customElement } from 'lit/decorators.js'; import { User } from './user'; @customElement('card-user') export class CardUser extends LitElement { static styles = css :host { display: block; } .card { box-shadow: 0 10px 10px 0 rgba(0, 0, 0, 0.5); max-width: 160px; } .card-content { padding: 10px; } ; @property({ type: Object }) user?: User = { id: 0, fullName: 'Luis Aviles', role: 'Software Engineer', }; render() { if (this.user === undefined) { return ''; } return html ${this.user.fullName} ${this.user.role} Edit ; } private handleEdit() { this.dispatchEvent( new CustomEvent('edit', { detail: this.user, }) ); } } ` The previous class defines a new custom element_ as a brand new _widget_ for our project. - The @customElement` decorator allows the component definition using a name for it: `card-user`. It is applied at the class level. - The static styles` attribute defines the styles for the component using a tagged template literal (`css`). - The @property` decorator, which allows declaring properties for the custom element in a readable way. - The render` method returns the HTML content through a template literal (`html`). This function will be called any time the `user` property changes. You can read more about how to define a component using Lit here. Using Web Components in Angular Import the Web Component Before using any Web Component in an Angular component, we'll need to make sure to import its definition as follows. `ts // app.component.ts import { Component } from '@angular/core'; import '../web-components/card-user/card-user'; // ` as part of any template. In this case, let's use it in our `app.component.html` file: `html ` Once you save these changes, you may find an error in your command-line interface (the place where you're running ng serve`). `text Error: src/app/app.component.html:1:1 - error NG8001: 'card-user' is not a known element: 1. If 'card-user' is an Angular component, then verify that it is part of this module. 2. If 'card-user' is a Web Component, add 'CUSTOMELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. 1 ~~~~~~~~~ src/app/app.component.ts:7:16 7 templateUrl: './app.component.html', ~~~~~~~~~~~~~~~~~~~~ Error occurs in the template of component AppComponent. ` Fortunately, the previous error message is significant enough to understand that we'll need to apply a change in the app.module.ts` file, which is the main module of our application. `ts // app.module.ts import { NgModule, CUSTOMELEMENTS_SCHEMA } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, AppRoutingModule], providers: [], bootstrap: [AppComponent], schemas: [CUSTOMELEMENTS_SCHEMA], }) export class AppModule {} ` The important part here is to import the CUSTOM_ELEMENTS_SCHEMA` from the `@angular/core` package, and then use it in the `schemas` property. You can find more information about it here. Save your changes again, and the magic will happen :-) Using the Property Binding It is convenient to know that we can not only import external components in our application but also make use of the Property binding. To do that, let's define a user` property in the `app.component.ts` file: `ts // app.component.ts export class AppComponent { user: User = { id: 2, fullName: 'Luis', role: 'Software Engineer', avatar: 'https://luixaviles.com/images/avatar@2x.png', }; } ` Then, we can update the related template, and use the brackets notation to bind the property: `html ` Even better, you can create an array of users to be able to render them using other nice features from our favorite framework, such as the structural directive *ngFor`. `html ` Using the Event Binding It's time to listen and respond to user actions through the Event binding_ with Angular. As you saw before, the `corp-user` component is able to dispatch an `edit` event once the "Edit" button is clicked. Let's use the parentheses notation this time to bind a method: `html ` Finally, let's create the edit` method in our component. `ts // app.component.ts //... export class AppComponent { //... edit(event: Event) { const user = (event as CustomEvent).detail; console.log('Edit user', user); } } ` The edit` method receives a generic `Event`. However, the _Custom Element_ has sent a `User` object through the `CustomEvent` detail. Then, we can use the `as syntax` operator from TypeScript and get access to it using the `detail` property. The next screenshot shows the results in the browser's console. Source Code of the Project Find the complete project in this GitHub repository: angular-lit-web-components. Do not forget to give it a star ⭐️ and play around with the code. Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work....

Getting Started with Here Maps and Web Components using Lit cover image

Getting Started with Here Maps and Web Components using Lit

In the past months, I have been actively working on the development of geolocation-oriented solutions in web applications. My experience has been good so far, taking into account the availability of modern tools and libraries, with a wide range of features that even enable the use of interactive maps and location services. In this article, I'll explain how to integrate Here Maps through web components, using Lit and TypeScript, in just a few steps. Introduction Let's start by explaining the technologies that we will be integrating into this post, step by step. Lit Lit is the next-generation library for creating simple, fast, and lightweight web components. One of the benefits of using Lit is the power of interoperability, which means that your web components can be integrated within any framework, or can even be used on their own. This is possible since we're talking about native web components that can be rendered in any modern web browser. If you're interested in learning more about this technology, you can take a look at the LitElement Series that has been published in this blog. Here Maps Let's say your application requires real-time navigation, the creation of custom maps, or the ability to visualize datasets over a map. HERE Maps meet these requirements without too much effort. In fact, there is a Maps API for JavaScript, which can be used to build web applications using either JavaScript or TypeScript. Note** Although this guide provides an API key for testing purposes only, you may need to register your app, and get your API Key to move forward with your applications. Creating the Project Project Scaffolding An easy way to initialize an application is through the project generator from the Open Web Components initiative. They provide a set of tools and guides to create a web components application, adding the testing, linting, and build support in the beginning. Let's create the project using a command-line interface `bash npm init @open-wc Select "Scaffold a new project" (What would you like to do today?) Select "Application" (What would you like to scaffold?) You can select any option for (What would you like to add?) Yes (Would you like to use TypeScript?) Mark/Select "Testing", "Demoing" and "Building" (Would you like to scaffold examples files for?) spa-heremaps-lit (What is the tag name of your application/web component?) Yes (Do you want to write this file structure to disk?) Yes, with npm (Do you want to install dependencies?) ` Pay attention to the project structure that is generated. Also, take a look at your package.json` file to review the dependencies and scripts that have been added. Update to Lit In case you generated a project based on LitElement, it shouldn't be complicated to replace dependencies with lit`: - Install the last version of Lit `bash npm install --save lit ` - Uninstall LitElement` and `lit-html` library `bash npm uninstall lit-element lit-html ` Also, you can make sure to update the imports as follows to make it work: `ts import { LitElement, html, css } from 'lit'; import { customElement, property } from 'lit/decorators.js'; ` Next, make sure the preview of the application is working fine using the following command: `bash npm run start ` Creating a Map Component Installing Here Maps Before creating a web component that will render a map, you are required to load the API code libraries in our index.html` file: `html ` You can see HERE Maps API for JavaScript Modules for more information about each imported script. Since the initial project is based on TypeScript, we may need to install the type definitions for the HERE Maps API. `bash npm install --save-dev @types/heremaps ` Now we're ready to go and code the web component using TypeScript. The Map Component Implementation Let's create a src/map/map.ts` file with the following content: `ts // map.ts import { LitElement, html, css } from 'lit'; import { customElement, property } from 'lit/decorators.js'; @customElement('spa-map') export class Map extends LitElement { static styles = css :host { display: block; width: 100%; height: 100vh; } ; @property({ type: Object }) location: H.geo.IPoint = { lat: 40.71427, lng: -74.00597, }; render() { return html ; } } ` The above code snippet allows the definition of a new custom element_ through the `@customElement` decorator. Also, it defines the `location` value as a component property so that it can be configured later as: `html ` Also, the render` method is ready to return a `TemplateResult` object which contains a `div` element that will be _rendered_ in all the available space. The `` element is the HTML container in which the map will be rendered later. To finish the component implementation, we'll need to write the firstUpdated` method. `ts // map.ts @customElement('spa-map') export class Map extends LitElement { // Other methods/properties defined above... firstUpdated() { const platform = new H.service.Platform({ apikey: '', }); const defaultLayers = platform.createDefaultLayers(); const divContainer = this.shadowRoot?.getElementById( 'spa-map' ) as HTMLDivElement; const map = new H.Map(divContainer, defaultLayers.vector.normal.map, { center: this.location, zoom: 10, pixelRatio: window.devicePixelRatio || 1, }); const behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map)); const ui = H.ui.UI.createDefault(map, defaultLayers); window.addEventListener('resize', () => map.getViewPort().resize()); } } ` Before creating the map object, it's required to initialize the communication with the back-end services provided by HERE maps. In that way, an H.service.Platform` object is created using the `` value you got in previous steps. However, most of the code in the previous block has to do with the initialization of the map: - An instance of H.Map` class is created specifying the container element, the map type to use, the geographic coordinates to be used to center the map, and a zoom level. - In case you need to make your map interactive, an H.mapevents.Behavior` object can be created to enable the event system through a `MapEvents` instance. - On other hand, the default UI components for the map can be added using the H.ui.UI.createDefault` method. - The resize` listener can be used to make sure the map is rendered in the entire container. Using the Map Component As a final step, we'll need to update the spa-heremaps-lit.ts` file: `ts import { LitElement, html, css } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import './map/map'; @customElement('spa-heremaps-lit') export class SpaHeremapsLit extends LitElement { static styles = css :host { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; font-size: calc(10px + 2vmin); color: #1a2b42; max-width: 960px; margin: 0 auto; text-align: center; } ; render() { return html `; } } ` Since this class defines a new custom element_ as a container for the map component, it's required to import the previous component definition using `import './map/map'`. Then, inside the class definition we can find: - The static styles` attribute defines the styles for the component using a tagged template literal(`css`) - The render` method returns the HTML content through a template literal(`html`). - The HTML content uses the previous custom element we created: ` The screenshot shows the map rendered in the main component of the application. Source Code of the Project Find the latest version of this project in this GitHub repository: spa-heremaps-lit. If you want to see the final code for this article, take a look at 01-setup-lit-heremaps tag. Do not forget to give it a star ⭐️ and play around with the code. Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work....

How to Solve CORS using a Proxy Server and Koa cover image

How to Solve CORS using a Proxy Server and Koa

No matter how much experience you have in software development, you've likely had to deal with the CORS problem. The good news is that there are now new and different ways to solve this issue, from using an extension for your favorite browser, to defining a custom solution. In this post, I'll explain how to implement a basic Proxy Server using Node.js so that you can use it even if you're using a different programming language for your main application. The Problem Let's suppose you're building a Single-Page Application, and it's running fine in your local environment http://localhost:8000`. At some point, you'll need to perform HTTP requests to an existing server to obtain data, or even authenticate you. All these services are running below `https://app.mydomain.com`. At the time you make the requests, you may have an error like the one shown below: `txt Access to fetch at 'https://app.mydomain.com' from origin 'http://localhost:8000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. ` In this particular case, the browser will restrict the cross-origin HTTP requests for security reasons. The Solution In the problem explained above, there is a Single-Page Application that needs access to resources or data. So, there is a security issue on the frontend side, and it can be solved by running a server in just a few steps. Koa For the Proxy Server implementation, we're going to use Koa, which is a web framework for Node.js. > Koa is a new web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation for web applications and APIs. It seems that there are very good reasons to use it, right? :-) Prerequisites You'll need to have installed the following tools in your local environment: - Node.js**. Preferably the latest LTS version. - A package manager**. You can use either NPM or Yarn. This tutorial will use NPM. Koa Installation Before installing Koa and other dependencies, let's create a folder for the Proxy Server: `bash mkdir proxy-server cd proxy-server ` Now, let's install Koa as the first dependency `bash npm install koa ` As the next step, let's create a proxy.js` file with the following content: `js const Koa = require("koa"); const app = new Koa(); app.listen(3000); console.log("listening on port 3000"); ` This is the simplest way to create an HTTP server using Koa. You can run it through node proxy.js` command. Of course, it is not very useful yet, but it will help as a basis for implementing the proxy. Koa Proxy and CORS It's time to install the next dependencies: @koa/cors` and `koa-proxies`: `bash npm install @koa/cors koa-proxies ` - @koa/cors` is a package to enable CORS (Cross-Origin Resource Sharing) with Koa. - koa-proxies` is a package powered by http-proxy, which is a full-featured HTTP proxy for Node.js Now, let's update the proxy.js` file, including `@koa/cors`, to enable CORS in the current implementation. `js const Koa = require("koa"); const cors = require("@koa/cors"); const app = new Koa(); const port = process.env.PORT || 3000; app.use(cors()); app.listen(port); console.log(listening on port ${port}`); ` As you can see, the Koa object gets available through new Koa()`, and right after that, you can apply a middleware using the `app.use()` method. - cors()**, enable CORS with default options, and allow several HTTP Methods. Take a look the available Koa middleware list for more reference. Proxy Server As the last step, let's configure the Koa proxy using the installed koa-proxies` library. `js const Koa = require("koa"); const cors = require("@koa/cors"); const proxy = require("koa-proxies"); const app = new Koa(); const port = process.env.PORT || 3000; app.use(cors()); app.use( proxy("/", { target: "https://app.mydomain.com", changeOrigin: true, logs: true, }) ); app.listen(port); console.log(listening on port ${port}`); ` Again, the app.use()` method enables the use of `proxy()` as a middleware. Pay attention to the used options: - target**, the URL string to be parsed - changeOrigin**, changes the origin of the host header to the target URL - logs**, enable default logs The TypeScript Solution The previous solution works fine using JavaScript only. In case you require using TypeScript, you'll need to create the tsconfig.json` file first: `bash tsc --init ` This command will auto-generate the tsconfig.json` file using default options for the compiler. Let's apply a couple of updates before moving forward: `json { "compilerOptions": { "target": "es5", "module": "commonjs", "outDir": "./dist", "strict": true, "esModuleInterop": true } } ` Next, let's install the TypeScript types for koa` and `@koa/cors` packages: `bash npm install --save-dev @types/koa @types/koacors ` Finally, create the proxy.ts` file with the following content: `ts // proxy.ts import Koa from "koa"; import cors from "@koa/cors"; import proxy from "koa-proxies"; const app: Koa = new Koa(); const port: number | string = process.env.PORT || 3000; app.use(cors()); const proxyOptions: proxy.IKoaProxiesOptions = { target: "https://app.mydomain.com", changeOrigin: true, logs: true, }; app.use(proxy("/", proxyOptions)); app.listen(port); console.log(listening on port ${port}`); ` As you may see in the previous code snippet, it's about using the right types, and configuring the TypeScript compiler. You can run the proxy.ts` file using this command: `bash npm run start ` Which will run the TypeScript compiler followed by the generated script: tsc && node dist/proxy.js`. Source Code of the Project Find the complete project in this GitHub repository: cors-proxy-server-koa. Do not forget to give it a star ⭐️, and play around with the code. Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work....

How to Implement an Event Bus in TypeScript cover image

How to Implement an Event Bus in TypeScript

In real-world software development, design patterns offer reusable solutions and, most prominently, make code easier to maintain. The Event Bus idea is generally associated with the Publish-subscribe pattern: > Publish–subscribe is a messaging pattern through which message senders, called "publishers", do not program the messages to be sent directly to specific receivers, called "subscribers". Instead, they classify published messages without knowledge of which subscribers sent them. In other words, an Event Bus can be considered as a global way to transport messages or events to make them accessible from any place within the application. In this blog post, we'll use TypeScript to implement a general-purpose Event Bus for JavaScript applications. Inspiration The concept of an Event Bus comes from the Bus Topology or _Bus Network_, in which nodes are directly connected to a common half-duplex link called a "bus". Every station will receive all network traffic as seen in the image below. In a software context, you can assume an object instead of a computer. Then, a message can be triggered from any object through the Bus, and it can be sent using an event, even including some data. Let's move forward with a TypeScript implementation of this pattern. Project Setup Prerequisites You'll need to have installed the following tools in your local environment: **Node.js**. Preferably the latest LTS version. **A package manager**. You can use either NPM or Yarn. This tutorial will use NPM. Initialize the Project Let's create the project from scratch. First, create a new folder using event-bus-typescript` as the name: `txt mkdir event-bus-typescript ` Next, let's initialize the project using NPM`, and basic TypeScript configurations: `txt npm init -y tsc --init ` Update the scripts` section from the `package.json` file: `json { "scripts": { "start": "tsc && node dist/main.js" }, } ` Proceed to update the tsconfig.json` file with the configurations listed below. `json { "compilerOptions": { "target": "esnext", "module": "commonjs", "outDir": "./dist", "strict": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true } } ` Finally, create the src` folder to contain the source code files. Project Structure The previous project already contains the main configurations ready to go with TypeScript. Open it in your favorite code editor, and make sure to have the following project structure: `ts |- event-bus-typescript |- src/ |- package.json |- tsconfig.json ` Event Bus Implementation For the Event Bus implementation, let's create a src/event-bus/event-bus.ts` file. Creating the Model To ensure robust typing within the TypeScript context, let's make sure to define a couple of Interfaces_ for the data model. `ts // event-bus.ts export interface Registry { unregister: () => void; } export interface Callable { [key: string]: Function; } export interface Subscriber { [key: string]: Callable; } export interface IEventBus { dispatch(event: string, arg?: T): void; register(event: string, callback: Function): Registry; } ` Pay attention to the IEventBus` interface, which represents a **contract** for the future Event Bus class implementation. `ts // event-bus.ts export class EventBus implements IEventBus { private subscribers: Subscriber; private static nextId = 0; constructor() { this.subscribers = {}; } public dispatch(event: string, arg?: T): void { const subscriber = this.subscribers[event]; if (subscriber === undefined) { return; } Object.keys(subscriber).forEach((key) => subscriberkey); } public register(event: string, callback: Function): Registry { const id = this.getNextId(); if (!this.subscribers[event]) this.subscribers[event] = {}; this.subscribers[event][id] = callback; return { unregister: () => { delete this.subscribers[event][id]; if (Object.keys(this.subscribers[event]).length === 0) delete this.subscribers[event]; }, }; } private getNextId(): number { return EventBus.nextId++; } } ` Pay attention to the public methods: The `dispatch` function makes use of the TypeScript generics to enable capturing the right type of parameters at the moment the method gets called. An example of its use will be provided at the end of this article. The `register` function receives an event name and a _callback_ function to be invoked. In the end, it returns a `Registry` object to enable a way of _unregistering_ the same event. The Singleton Pattern Since the Event Bus can be accessed from any place in the application, it's important to have a unique instance. Then, you can implement the Singleton Pattern in the existing class as follows. `ts export class EventBus { private static instance?: EventBus = undefined; private constructor() { // initialize attributes here. } public static getInstance(): EventBus { if (this.instance === undefined) { this.instance = new EventBus(); } return this.instance; } } ` Here are the main points of this Singleton class: A static instance is defined to have the unique reference of an object of this class. The constructor method is private, since creating an object from any place is not allowed. The `getInstance()` method makes sure to instantiate an object of this class only once. Using the Event Bus In order to explain the use of the brand-new Event Bus, we'll need to create a src/main.ts` file. `ts import { EventBus } from './event-bus/event-bus'; EventBus.getInstance().register('hello-world', (name: string) => { if(name) console.log('Hello ' + name); else console.log('Hello world'); }); EventBus.getInstance().dispatch('hello-world', 'Luis'); EventBus.getInstance().dispatch('hello-world'); EventBus.getInstance().dispatch('hello-world'); ` Once you run npm run start` command, you should see the following output: `txt Hello Luis Hello world Hello world ` However, we can have full control of the initial subscription too: `ts const registry = EventBus.getInstance().register('hello-world', (name: string) => { if(name) console.log('Hello ' + name); else console.log('Hello world'); }); EventBus.getInstance().dispatch('hello-world', 'Luis'); EventBus.getInstance().dispatch('hello-world'); registry.unregister(); EventBus.getInstance().dispatch('hello-world'); ` Let's explain what's happening now: The first line performs a call to the _register_ method from the Event Bus instance. Then, a reference of the `Registry` object is available through the `registry` variable. Later, it's possible to perform the `registry.unregister()` call to avoid dispatching the last "hello world" call. Here's the output result of those operations: `txt Hello Luis Hello world ` Live Demo Wanna play around with this code? Just open the embedded Stackblitz editor: Source Code of the Project Find the complete project in this GitHub repository: event-bus-typescript. Do not forget to give it a star ⭐️ and play around with the code. Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work. Be ready for more articles about Lit in this blog....

Drawing HTML Elements upwards/downwards dynamically in the Screen cover image

Drawing HTML Elements upwards/downwards dynamically in the Screen

A couple of weeks ago, I start writing a custom component that allows rendering a list of other components dynamically. One of its features shows the list up or down, according to the space available in the viewport (or the screen). A solution to this type of problem may require the use of the web APIs available through the Window interface. The Web APIs API comes from Application Programming Interface_, and according to MDN: > Application Programming Interfaces (APIs) are constructs made available in programming languages to allow developers to create complex functionality more easily. They abstract more complex code away from you, providing some easier syntax to use in its place. In the same way, the Web currently comes with a large number of Web APIs we can use through JavaScript(or TypeScript). You can find more detailed information about them here. The Window Interface The Window interface represents a window containing a DOM document. In the practical world, we usually use the global window` variable to get access to it. For example, open the browser's console, and type the following command there: `js window.document ` Once you run the previous command, you should get a reference to the current document_, which is contained by the window object. In Google Chrome, you'll see a selection effect applied over the whole page. Give it a try! Let's try with another example. Type the next command in your browser's console: `js window.history ` The output will be something like this: As you can see, the result is a History` object. This is a read-only reference, and can be used to change the current browser history as the next example shows: `js const history = window.history; history.back(); ` All the above examples were about access to properties. However, the window` object also provides useful methods. For example: `js window.open(); window.find('pattern'); ` The first method call will open a new window browser, and the second one will search the given string within the current view. Using the APIs First, let's define the HTML template of the list to be rendered dynamically: `html Item 1 Item 2 Item 3 Item 4 Item 5 Item 6 Item 7 ` Now, let's apply some styles so that it stands out from the container. ` .list { display: none; border: 1px solid #00458b; min-width: 200px; position: absolute; background: #eee; } ` As you may note, the list won't be rendered by default(display: none`), and we're going to set its position from the code. Listening to a Mouse Click Event Let's create a TypeScript file (index.ts`) so that we can start using the Web APIs, and the `window` object. As the next step, we'll need to "catch" the mouse click event globally. Then, the window` reference can be helpful for that: `ts // index.ts window.addEventListener('click', (event: MouseEvent) => { console.log('click', event.x, event.y); }); ` Once you run the previous code snippet, the browser's console will print the click event coordinates. Getting the List from the DOM The HTML List we created can be accessed using the following JavaScript code: `ts const listElement = window.document.querySelector('.list') as HTMLDivElement; ` The `window.document` will return a reference to the _Document_ object contained in the window. The `querySelector` method will return the first element that matches with the selector(`.list`). Since TypeScript is being used, we can use the `as` syntax to perform a type assertion. Rendering the List Dynamically Before rendering the list dynamically, we'll need to perform some calculations based on the list size, and the container. Let's get the list size using the getBoundingClientRect()` method: `ts const boundingRect = listElement.getBoundingClientRect() as DOMRect; ` This method returns a DOMRect` object, which contains the coordinates and the size of the element with other properties. `ts const viewportHeight = window.innerHeight; ` The window.innerHeight` property returns the interior height of the window, in pixels. Let's read the mouse click event coordinates too: `ts let { x, y } = event; ` In this case, we'll do a calculation for the y-axis coordinate: `ts if (event.y + boundingRect.height >= viewportHeight) { console.log('render upwards'); y = y - boundingRect.height; } else { console.log('render downwards'); } ` The above code snippet will make sure the list can fit below. Otherwise, it will render it upwards the click position. As a final step, we'll need to update the visibility of the list, and update the position of if after every click event: `ts listElement.style.display = 'block'; listElement.style.left = ${x}px`; listElement.style.top = ${y}px`; ` Live Demo Want to play around with this code? Just open the embedded Stackblitz editor: Conclusion In this article, I described a useful technique to rely on existing Web APIs to solve a problem. Keep in mind this example is a proof of concept only and you may need to perform additional calculations or accessing different window` properties. Either way, feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work....

How to Handle Time Zones using DateTime and Luxon cover image

How to Handle Time Zones using DateTime and Luxon

Raise your hand if you've ever had issues dealing with time zones, or even if you've asked, "How do I convert a Date object to another time zone in JavaScript?" In my personal experience, this kind of requirement can become a big problem for developers if the date-handling-related concepts are not clearly understood, or the right tools are not used. Using Dates and Time Zones in JavaScript Let's suppose you have a date representation from an external API, and you need to convert the date to any desired time zone. The best option for this is using a representation that meets the ISO 8601 standard. As an example of this, we can set a date like `2021/06/10 02:20:50` in UTC. Then, the standard notation for this date will be `2021-06-10T02:20:50+00:00`. On other hand, the JavaScript language provides a Date` object that represents a single moment in time. You can create a Date object in different ways: `js let date; date = new Date(); // Get the current date date = new Date("2021-06-10T02:20:50+00:00"); // An object representation of given string date date = new Date(new Date()); // Creates an object representation from another one ` Also, we can set a time zone value to any given Date` object as follows: `js let stringInput = "2021-06-10T02:20:50+00:00"; let timeZone = "America/LosAngeles"; const dateObject = new Date(stringInput).toLocaleString("en-US", { timeZone, }); console.log(dateObject); // Prints: 6/9/2021, 7:20:50 PM ` The toLocaleString` method returns a string with a language-sensitive representation of the `Date` object. At the same time, this method supports optional arguments where you can configure the time zone. Find more information about this method here. As you can see, the output date matches the configured time zone (GMT-7). However, we have a string representation of the date, and it would be much better if we work with a JavaScript object instead. Luxon Luxon is considered an evolution of Moment.js- a very popular library for date handling in the JavaScript ecosystem. As the Luxon project says: > Luxon is a powerful, modern, and friendly wrapper for JavaScript dates and times. Indeed, this library solves most of the common problems related to Date handling: - Date internationalization - Time zones and Offsets - Calendars support - Dates Formatting - Dates Parsing - Dates Math (Add/Subtract days, months, etc) - Dates Validation - and More... The DateTime Object The most important part of the Luxon library is the DateTime` object. It can be considered a wrapper of the native `Date` object along with a timezone, and a local configuration. The simplest way of creating a DateTime` object is as follows. `ts import { DateTime } from "luxon"; let dateTime = DateTime.local(); console.log("Current Date", dateTime.toISO()); // 2021-06-22T21:11:45.638-04:00 ` The method toISO()` will return an ISO 8601-compliant string representation of the `DateTime` object. Also, you can create a DateTime` in a specific time zone. `ts // Create a DateTime in a Specific Timezone let zone = "America/Denver"; let dateTime = DateTime.fromObject({ zone, }); console.log("Current Date", dateTime.toISO()); // 2021-06-22T19:11:45.640-06:00 ` As you can compare with the previous example, the time output is different because of the use of America/Denver` as the time zone. Of course, there is a way to create a custom date in a specific time zone: `ts let dateTime = DateTime.fromObject({ 'America/Denver', }).set({ day: 1, month: 5, year: 2021, }); console.log("Custom date", dateTime.toISO()); //2021-05-01T19:11:45.641-06:00 ` The set` method allows overriding specific properties such as `year`, `month`, `day`, etc. Converting a DateTime to a different Time Zone Now let's suppose we have a DateTime` object, and we need to convert it to a different time zone. `ts let dateTime = DateTime.fromObject({ 'America/Denver', }).set({ day: 1, month: 5, year: 2021, }); // Convert existing date to another Timezone dateTime = dateTime.setZone("America/LaPaz"); console.log("Custom date, America/LaPaz", dateTime.toISO()); //2021-05-01T21:11:45.641-04:00 ` Configuring the Default Time Zone What happens when the whole application needs to run every date in a specific time zone? Just suppose you have defined a configuration within your app to allow the selection of a time zone at any time. To solve this problem, you don't need to use the time zone string here and there. The Settings` class, instead, comes to the rescue: `ts import { Settings } from "luxon"; // Configure the time zone Settings.defaultZoneName = "America/Denver"; console.log(Settings.defaultZoneName); // Reading the configured time zone. ` The defaultZoneName` can be used as a `set` or `get` method for the default time zone when you're working with the library. In the same way, the Settings` class contains other methods to configure Luxon's behavior. Then, when you're creating a new DateTime` object again, it will take the configured time zone by default. `ts dateTime = DateTime.local(); console.log("Configured defaultZoneName", dateTime.toISO()); //2021-06-22T19:21:54.362-06:00 ` Pay attention to the offset value, which corresponds now with America/Denver`. Validate a Time Zone In case you define a user entry point to configure the time zone globally, it is important to validate the text before causing problems with the DateTime` objects. A useful way to do it is through, again, a DateTime` object: `ts const timeZone = "America/NotDefined_TZ"; const myDateTime = DateTime.local().setZone(timeZone); console.log("timeZone valid", myDateTime.isValid); // Prints 'false' ` Now try again with a valid time zone, for example, America/Los_Angeles`. Live Demo Wanna play around with this code? Just open the embedded CodeSandbox editor: Conclusion In this article, I described a couple of useful methods for using Luxon for Time Zone handling using either JavaScript or TypeScript. Personally, I consider it a very useful library, and it also avoids rewriting and testing your own code for handling dates and time zones, which could save you a lot of time. Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work....