Creating a detail info screen

What you will learn

This lesson will explain :

  • How to create Module,
  • How to route with parameters,
  • How to implement Deeplink,
  • How to focus a specific child,
  • How to implement animations between screens.

The final result :

Final result

Introducing modules

What’s Module in Dana’s perspective ?

A module allows you to separate a complete feature from your application. You can add or remove a module in your application according to the profile of your application in the configuration file.

What is doable in module ?

In a module you can :

  • declare and register new routes
  • add/overwrite style, i18n, configuration
  • add behavior to a component

If you check in your current tutorial project, a folder named modules has been created. In this folder you can create as many modules as you need. Here, one is already created named ‘fip’ (Full Info Page). The name is defined in the package.json, inside the ‘fip’ folder.

In the fip module, you will find :

  • modules/fip/package.json : definition of the module in package.json file.
  • modules/fip/scripts/FipModule.js : [moduleName]Module.js similar to Application and inherits from AbstractModule.
  • modules/fip/scripts/FipRoutes.js : [moduleName]Router.js similar to AppRoutes and inherits from $Routes.
  • modules/fip/scripts/ui/screens/* : specific or override screens of your module.
  • modules/fip/scripts/ui/views/* : specific or override views of your module.

Add module in your application

This module use theme values, they need to be added in statics of AppTheme :

TRANSPARENT: "#00000000",
H1_FONT_SIZE: 42,
H2_FONT_SIZE: 35,
DEFAULT_FONT_SIZE: 20,
SCREEN_HORIZONTAL_MARGIN: 50,
SCREEN_VERTICAL_MARGIN: 30,

///////////////////////////////
// FIP
///////////////////////////////
FIP_IMAGE_WIDTH: 1422,
FIP_IMAGE_HEIGHT: 800,
IMAGE_RATIO: 16 / 9

To add a module in your application, you just have added “modules” in your profile, which is an array of module name.

Here we want to add the module fip :


"base-css": {
    "base": {
        "name": "Css",
        "vendors": [
            "@dana/renderer-css",
            "@dana/vendor-components",
            "customBE"
        ],
        "modules": [
            "fip"
        ]
    }
},

"base-lightning": {
    "base": {
        "name": "Lightning HTML5",
        "vendors": [
            "@dana/renderer-lightning-html5",
            "@dana/vendor-components",
            "customBE"
        ],
        "modules": [
            "fip"
        ]
    }
},

Congrats !! The module fip is available in your application, but not used yet. You will need to route to this module.

Routing with parameters

The module is properly loaded but not use yet. The module share one route defined in FipRouter.js named fipContent displaying the $FipContentScreen.

fipContent: function (data) {
    return $FipContentScreen.display(data);
}

As you can see, the route fipContent require data to be displayed. The FipContentScreen needs the data to display the content information.

In CatalogScreen.js you will route to the FipContentScreen and add in parameters of route the needed data object.

To display the information of a content, this screen needs to get the data of the selected content. To send the selected content to the screen, the options object will allow you to send data the screen requires to be displayed properly.

/**
 * @override
 */
ok: function (key) {
    if (this.scrollView.isFocused === true) {
        const data = this.scrollView.getItemView().getItem();
        const options = {
            contentId: data.id
        };

        return this.route("fipContent", options);
    }
}

Then on screen side, if you open the FipContentScreen.js you will get the given data in parameter of the prepateData method.

So by using the VodService and the contentId the screen are able to get datas.

/**
 * Prepare screen data: get the business data to feed the views
 * View should always be filled with business model(s).
 *
 * It must return a Object.
 *
 * @param {Object} [context] The context shared by the screens during display
 * @param {string} [context.contentId] The id of the content to display information
 * @returns {Promise<Object,Error>} returns a promise resolved with the data
 */
prepareData: function (context) {
    return this.vodService.getContentDetails(context.contentId);
},

The value returned in this method will be used to create the data value of the screen, and can be used in connectData as shown below :

/**
 * Connect the data prepared by {@link prepareData} to the views.
 * Use the data property, which is the return value of {@link prepareData}.
 *
 * @param {Object} [context] The context shared by the screens during display
 */
connectData: function (context) {
    this.contentInfo.data = this.data;
    this.image.url = this.data.imageUrl;
},

Then if you want to return to the previous screen from FipContentScreen, you can override the back method behavior in the screen file :

/**
 * @override
 */
back: function (key) {
    return this.routeToPrevious();
},

Now, you can reach the full information page by pressing OK on any item of the rails and back to the catalog to navigate and select another content.

In some cases, you may want to display directly the information page without launching the app or search the content and click on it to display the information.

Dana propose a mechanism called deeplink, making this behavior possible on each platform .

On browser, Dana uses the URL mechanism to handle the deeplink.

First, the screen FipContentScreen need contentId parameter to get datas and fill their views with them.

http://localhost:9000/template-lightning.html?contentId=1115

Then, Dana need to know which screen you want to be displayed.

http://localhost:9000/template-lightning.html?contentId=1115#fipContent

After this url built, you can use this link in your browser, and you will reach directly the FipContentScreen whitout route by the catalog screen.

Previously, when the user display the information screen, he used the catalog screen to select a content. So when the user press Back, the RouterManager can display the previous route.

Caution

In a deeplink case, there is no previous route cause, the information screen is the first route and the user is blocked.

To fix this behavior, you can override the back method of FipContentScreen to handle the route when there is no previous route.

/**
 * @override
 */
back: function (key) {
    if (this.routerManager.previousRouteName == null) {
        return this.route("catalog", {
            animations: {
                show: "catalogEnter",
                hide: "fipContentExit"
            }
        });
    } else {
        return this.routeToPrevious({
            animations: {
                show: "catalogEnter",
                hide: "fipContentExit"
            }
        });
    }
},

This way the user isn’t blocked. Other way to fix this, is to display the menu on this screen allowing the user to reach the catalog screen.

Focusing a specific child in a screen

By default, Dana focus the first focusable view in the screen’s children. If you want to override this behavior a method is at your disposal _getChildToFocus.

In FipContentScreen, the first view in children is contentInfo. So as it is, this is the first focused view. This view is a simple view just displaying data, not a focusable view.

To modify this, you just have to return the view you want to be focused like below :

/**
 * @override
 */
_getChildToFocus: function () {
    return this.playButton;
},

Now, the play button is the focused view when displaying the screen.

Animations between screens

The mechanism of routing between screen allows you to defined animations when you show or hide a screen. This mechanism is based on the route parameters.

Caution

Animations are available only on views.

Create animation

In FipContentScreen.js add keyword animations which is an object of {string, function}.

  • string : the key defining the name of the animation
  • function : the function implementing the full animation

First, create the animations for FipContentScreen :

  • The first one will be used when the screen will be shown fipContentEnter.
  • The second one will be used when the screen will be hidden fipContentExit.
animations: {
    fipContentEnter: function () {
        let screenAnim = this.addPropertyAnimation("x", {
            delay: 0,
            duration: 250,
            from: 100,
            to: $Theme.FULL_SCREEN_WIDTH + 100
        });

        screenAnim.start();
        return [
            screenAnim
        ];
    },
    fipContentExit: function () {
        let screenAnim = this.addPropertyAnimation("x", {
            delay: 0,
            duration: 250,
            from: $Theme.FULL_SCREEN_WIDTH + 100,
            to: 100
        });

        screenAnim.start();
        return [
            screenAnim
        ];
    }
},

Secondly, create the animations for CatalogScreen

  • The first one will be used when the screen will be shown catalogEnter.
  • The second one will be used when the screen will be hidden catalogExit.
animations: {
    catalogEnter: function () {
        let screenAnim = this.addPropertyAnimation("x", {
            delay: 0,
            duration: 250,
            from: -($Theme.FULL_SCREEN_WIDTH + 100),
            to: 100
        });

        screenAnim.start();
        return [
            screenAnim
        ];
    },
    catalogExit: function () {
        let screenAnim = this.addPropertyAnimation("x", {
            delay: 0,
            duration: 250,
            from: 100,
            to: -($Theme.FULL_SCREEN_WIDTH + 100)
        });

        screenAnim.start();
        return [
            screenAnim
        ];
    }
}

Use animation between screens

Configure the animation when the user display the FipContentScreen from CatalogScreen. Remember, you can send data to a screen with the route method. You can to use this mechanism to define the animations in option parameter.

When you route to another screen you can define the animations property in options, this property handle show and hide animations. The value of those properties are the name of the animation.

The show animation will be applied on the screen that will be displayed. The hide animation will be applied on the screen that will be hidden.

In our case, you want to apply an animation between CatalogScreen and FipContentScreen, the screen that will be shown is the FipContentScreen so, you can define as show animation the fipContentEnter animation. Then the screen that will be hidden is the CatalogScreen so, you can define as hide animation the catalogExit animation.

Here the result :

/**
 * @override
 */
ok: function (key) {
    if (this.scrollView.isFocused === true) {
        const data = this.scrollView.getItemView().getItem();
        const options = {
            contentId: data.id,
            animations: {
                show: "fipContentEnter",
                hide: "catalogExit"
            }
        };

        return this.route("fipContent", options);
    }
},

Then configure the animation when the user want to return to the CatalogScreen from FipContentScreen.

In this case, you want to apply an animation between FipContentScreen and CatalogScreen, the screen that will be shown is the CatalogScreen so, you can define as show animation the catalogEnter animation. Then the screen that will be hidden is the FipContentScreen so, you can define as hide animation the fipContentExit animation.

Here the result :

/**
 * @override
 */
back: function (key) {
    return this.routeToPrevious({
        animations: {
            show: "catalogEnter",
            hide: "fipContentExit"
        }
    });
},

Summary

In this lesson you have learned :

  • How to create and use a module,
  • How to send data to another screen by routing api,
  • How to use DeepLink feature,
  • How to use animation between screens