Views and screen

What you will learn

In this lesson, you will create a new button View and a new Screen from scratch. The new screen will have two “buttons”. This should be the final result :

Final result

View overview

You previously worked in a file called LoaderView that inherits from View class. A View is composed of primitives (like in LoaderView) but can also be composed of other views. In short terms, View elements are containers. They do not have any rendering, but allow you to manage groups of elements and re-use them wherever you want in your application. Moreover, View have the capability to be focused and blurred.

Creating a “button” like View

New file creation

First of all, add new style property in your AppTheme :

export default $AbstractTheme.declare("AppTheme", {
    statics: {
        COLOR_WHITE: "#FFFFFF",
        COLOR_BLACK: "#000000",
        COLOR_BLUE_LIGHT: "#00adec",
        H2_FONT_SIZE: 35
    }
});

Your ButtonView will be composed of a TextPrimitive and a RectanglePrimitive.

Create a new file named ButtonView.js in scripts/app/ui/views with the following content :

import $View from "@View";
import $TextPrimitive from "@TextPrimitive";
import $RectanglePrimitive from "@RectanglePrimitive";
import $Theme from "@Theme";

/**
 * Button
 *
 * @name ButtonView
 * @class
 * @extends View
 */
export default $View.declare("ButtonView", {
    statics: /** @lends ButtonView */ {
        HORIZONTAL_MARGIN: 20,
        VERTICAL_MARGIN: 5,
        TEXT_HEIGHT: 41
    },

    children: {
        rectangle: {
            class: $RectanglePrimitive
        },
        text: {
            class: $TextPrimitive
        }
    },

    style: {
        width: ({text, statics}) => text.width + 2 * statics.HORIZONTAL_MARGIN,
        height: ({statics}) => statics.TEXT_HEIGHT + 2 * statics.VERTICAL_MARGIN,
        text: {
            isAuto_width: true, // eslint-disable-line @dana/no-use-width-height-auto,
            height: ({parent}) => parent.statics.TEXT_HEIGHT,
            x: ({parent}) => parent.statics.HORIZONTAL_MARGIN,
            y: ({parent}) => parent.statics.VERTICAL_MARGIN,
            size: $Theme.H2_FONT_SIZE,
            color: $Theme.COLOR_WHITE,
            text: "Home"
        },
        rectangle: {
            width: ({parent}) => parent.width,
            height: ({parent}) => parent.statics.TEXT_HEIGHT + 2 * parent.statics.VERTICAL_MARGIN,
            borderRadius: 12,
            fillColor: $Theme.COLOR_BLUE_LIGHT
        }
    }
});

Adding it as a child of a screen

To see the button, you need to add it to a Screen. Open SplashScreen, add import of ButtonView and add a new child called button that is using your ButtonView.

import $ButtonView from "@ButtonView";
export default $AppScreen.declare("SplashScreen", {
    children: /** @lends SplashScreen.prototype */ {
        button: {class: $ButtonView},
        loader: {class: $LoaderView}
    }
});

Refresh your page, and you will see your button appear in the top-left corner.

Allow text customization

Actually, text of your button is always “Hello”. Create a method in the ButtonView to update text property of its child text.

Caution

Keyword methods allow to declare functions for your class.

export default $View.declare("ButtonView", {
    methods: /** @lends ButtonView.prototype */ {
        setText: function(text) {
            this.text.text = text;
        }
    }
});

this.text allows to access to your TextPrimitive child named text and this.text.text allows you to access to the text property of this textPrimitive.

To see it in action, you need to use prepareData method in SplahScreen. Make a call on the button to setText to be able to set a new text.

export default $AppScreen.declare("SplashScreen", {
    methods: /** @lends SplashScreen.prototype */ {
        connectData: function () {
            this.button.setText("Hello World");
        }
    }
});

Once validated, remove the import, the child and the call to setText from your SplashScreen because you are going to create a new screen dedicated for this button.

Screen overview

The class Screen inherits from class View. So yes, a screen is a view and has the same purpose to group elements. But screens can do more than views. Screens will be responsible to manage user interactions, collect datas, listen to events… It controls inputs and events.

When creating TV applications, it is common to talk about screens as it is common to talk about pages on a website. It is important to organise a complete application between multiple screens.

Allow text customization

Create a new Screen

Create a new file named CatalogScreen.js in scripts/app/ui/screens with the following content :

import $AppScreen from "@AppScreen";
import $ButtonView from "@ButtonView";

/**
 * Catalog
 *
 * @name CatalogScreen
 * @class
 * @extends AppScreen
 * @screen
 */
export default $AppScreen.declare("CatalogScreen", {

    children: /** @lends CatalogScreen.prototype */ {
        button: {class: $ButtonView}
    },

    methods: /** @lends CatalogScreen.prototype */ {
        //////////////////////////////////////////////////////////////////////////////////////////
        // EXTENSION POINTS
        //////////////////////////////////////////////////////////////////////////////////////////

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

        /**
         * 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
         * @override
         */
        connectData: function (context) {
            this.button.setText("Hello World");
        }
    }
});

For now, this screen is mainly filled with nothing, but it has your ButtonView as a child. And that’s all you need for the begining of this screen.

To be able to display a screen, you need to register a route. Give a name to the route matching the screen name, like for example catalog.

Open {appName}AppRoutes file. There is already an import of SplashScreen and a route named splash that displays the SplashScreen. Do the same for your catalog.

import $AbstractAppRoutes from "@AbstractAppRoutes";
import $SplashScreen from "@SplashScreen";
import $CatalogScreen from "@CatalogScreen";

/**
 *
 * @name TemplateAppRoutes
 * @class
 * @extends AbstractAppRoutes
 * @implementation
 * @routes splash
 * @routes catalog
 */
export default $AbstractAppRoutes.declare("TemplateAppRoutes", {
    methods: /** @lends TemplateAppRoutes.prototype */ {
        splash: function (data) {
            return $SplashScreen.display(data);
        },
        catalog: function (data) {
            return $CatalogScreen.display(data);
        }
    }
});

To display the new screen instead of the splash screen, you can change the first route of you project by using profiles. In app.config.json, inside the object default, you will find a key named firstRoute. Update it with your catalog :

"firstRoute": {
    "id": "catalog"
}

Caution

Changes in profiles are not hot reloaded. When updating your profiles, you will need to kill your npm start command and restart it.

You now should see your new screen with your button in the top left corner.

Summary

To summarize, during this lesson you have :

  • created a View and a Screen
  • updated a child from screen it belongs to

To go further on those concepts, you can browse the following documentation pages :