JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 26
  • Score
    100M100P100Q66725F

Package Exports

  • @studiohyperdrive/ngx-tour
  • @studiohyperdrive/ngx-tour/package.json

Readme

Angular Tools: NgxTours (@studiohyperdrive/ngx-tours)

ngx-tour is light-weight and heavily customizable package to create a help tour through an application using the CDK overlay.

Installation

Install the package first:

npm install @studiohyperdrive/ngx-layout

Versioning and build information

This package will follow a semver-like format, major.minor.patch, in which:

  • major: Follows the Angular major version
  • minor: Introduces new features and (potential) breaking changes
  • patch: Introduces bugfixes and minor non-breaking changes

For more information about the build process, authors, contributions and issues, we refer to the ngx-tools repository.

Concept

With the tour approach of ngx-tour, we aim to create a very light-weight bare bones approach to the help tours. The package requires the user to provide their own styling and components for the steps shown during the tour, ensuring a maximized customizability.

Implementation

The implementation of the package consists of a three individual parts, being the NgxTourService, the NgxTourItemDirective and the NgxTourStepComponent abstract class.

Setup

ngx-tour uses the Angular CDK so it does require an initial setup in order for it to be used properly throughout the application. The following styles need to be imported at root level in order to function correctly.

    @import '@angular/cdk/overlay-prebuilt.css';

Throughout the tour, we want to visualize individual steps to the user, usually including a way to navigate between the steps of the tour.

In order to do that, we need to provide a default component that is an implementation of the NgxTourStepComponent abstract. We do this by using the provideNgxTourConfiguration util in our main app providers array.

// main
providers: [
    ...
    provideNgxTourConfiguration(CustomTourStepComponent)
]

NgxTourService

The core of the tour is the NgxTourService, which is a singleton service that handles all tour related methods and observables.

We define a tour by providing an array of tour steps to the tour service and running the startTour method. At it's core, a tour step only has two required properties, being title and content.

this.tourService.startTour([{title: 'Hello', content: 'World'}]).subscribe()

By default, this step will be rendered in the middle of the screen. Of course, in real world applications, we want to render the step next to a highlighted element. We can do this using by adding the tourItem tag of the NgxTourItemDirective to an element, and provide the tag to the step. By doing so, the tour will locate the item, and attach the step to it.

    <p tourItem="helloWorld">Hello world!</p>
this.tourService.startTour([{
    title: 'Hello', 
    content: 'World', 
    tourItem:'helloWorld'
    }]
    ).subscribe()

Now whenever the tour navigates, it will try to find the provided element and attach the step to this element. We can provide where we want to render this step by passing a position, which can be above, left, right or below. By default, this is below.

When highlighting a step, the service automatically provides a cutout in the backdrop around the element to highlight it. By default, this cutout has a margin of 5px, but this can be overwritten by using the cutoutMargin property.

this.tourService.startTour([{
    title: 'Hello', 
    content: 'World', 
    tourItem:'helloWorld', 
    position: 'top',
    cutoutMargin: 10
    }]
    ).subscribe()

Sometimes, elements in the UI require some time before they're visible, for instance because we're waiting for a loading spinner. We can add an optional amount of time we wish to wait before the tour skips the current steps and moves on to the next step. This can be provided by the delay property. By default, the tour service will wait 100 ms before moving on to the next step.

this.tourService.startTour([{
    title: 'Hello', 
    content: 'World', 
    tourItem:'helloWorld', 
    delay: 2000
    }]
    ).subscribe()

ngx-tour also allows for actions to be run throughout the tour. For each step, we have the ability to run provided functions before, when and after an element is visible. We can do this by providing a beforeVisible, onVisible and afterVisible method respectively. This can be useful for use cases where we want to route during the tour.

this.tourService.startTour([{
    title: 'Hello', 
    content: 'World', 
    tourItem:'helloWorld', 
    beforeVisible: () =>  {
        this.router.navigate(['second-page'])
    },
    onVisible: (step, index) => {
        return this.analyticsService.sentEvent({stepReached: index});
    },
    afterVisible: () => {
        this.router.navigate(['third-page'])
    }
    }]
    ).subscribe()

Individual steps in the tour can be further customized by either disabling the backdrop or providing a custom component to deviate from the default component. This can be done by using the disableBackdrop and the component property respectively. If you wish to provide extra data to the component outside of the of the mandatory title and content, you can do so by providing it using the data property.

this.tourService.startTour([{
    title: 'Hello', 
    content: 'World', 
    component: SpecialIntroductionStepComponent,
    disableBackdrop: true,
    data: {userName: 'Mark'}
    }]
    ).subscribe()

Next to the tour, we can also provide a closing function and a startIndex to the startTour method. This can be used to route back to the start page whenever a user has gone through a tour or can allow us to start a tour at a specific index.

this.tourService.startTour(
    [...],
    (step, index) => {
        return this.analyticsService.tourStoppedAt(index);
    },
    2
    ).subscribe()

Finally, the NgxTourService also provides a set of Observables we can listen to. Using tourStarted$ and tourEnded$ respectively, we can listen to the start and/or end of the tour.

Using currentStep$, currentIndex$, previousStep$ and currentTour$, we can listen to the states of the current step, the previous step and the currently displayed tour.

NgxTourItemDirective

The NgxTourItemDirective is used to highlight elements during the tour. Simply using the tourItem tag will match the provided input with the corresponding step.

When an item is highlighted, the item also gets the ngx-tour-item-active class.

NgxTourStepComponent

The NgxTourStepComponent presents us with 5 Inputs and one Output we need to handle the tours. By default, the two most important Inputs are title and content, which correspond with the two data properties we passed in the step. Additionally, the amount of steps in the tour and the current index of the step can be visualized using amountOfSteps and currentIndex. To maximize customisability, we can also pass a data property to the component. This data can be anything, and can be used to enrich a step.

In order to navigate through the tour and close it when needed, the component has an Output called handleInteraction that takes three possible states, being next, back and close. Each of these interactions will continue the tour, go back in the tour or close the tour respectively.

Known issues

When the tour requires routing between multiple pages, we suggest including a onClose function that routes back to the original page when the tour closes. This ensures that the tour will go back to the initial page, regardless of where the user decides to close the tour. It should be noted though, that this can sometimes cause issues with the changeDetection, of which we currently don't have an in-package solution. The current fix is to run the change detection manually after the routing, which can be done in the onClose function.

this.tourService.startTour(
    [...],
    () => {
                return from(this.router.navigate([''])).pipe(
                    tap(() => {
                        this.cdRef.detectChanges();
                    })
                );
            }
)