Adding vendors
What you will learn
In this lesson, you will learn what vendors are. You will implement a VodService
in a CustomBE
vendor and create your custom models to retrieve contents.
Vendors overview
Vendors are used to implement framework abstraction to real use cases. For example, Dana exposes abstract services linked to TV domain’s functionalities needs like VodService
, PvrService
, SystemInfoService
… In real use case, those services will probably need a different back-end or middleware / device connection according to projects or according to deployement countries of the same project. Thanks to abstraction at Dana level, your project will be able to use the same interfaces. Then, to add implementation, you will use vendors.
Note
It is common to use vendors for external dependencies like back-end or middleware.
When you created this project with create-dana-app
, passed option --tutorial
created a vendor for this tutorial. A folder named vendors
has been created. In this folder you can create as many vendors as you need. Here, the one already is named customBE
. The name is defined in the package.json
, inside the customBE
folder. In scripts/services
, you will find the CustomBEVodService
which is an implementation of AbstractVodService
.
Caution
Take a look to CustomBEVodService
JSDOC. There is an annotation @implementation
above the class declaration. This is mandatory in Dana to declare implementations.
An abstract defines the public APIs, you should consider implementations are offering same public APIs. When defining an abstract, Dana will also associate an alias. As a result an AbstractXxxService
, with an implementation MyXxxService
, will be used with the alias XxxService
. During profile resolution, alias will be resolved with the correct implementation or abstraction.
Retrieve data from a service
In the CatalogScreen
, you are going to retrieve a catalog through the VodService
. To begin, log the results to understand how it works.
Caution
Things to know :
- services are singletons, you should not instanciate them,
- to use services always use their alias not the final class name,
- screens are responsible for retrieving data in
prepareData
methods, - screens are responsible for setting data in
connectData
methods, - results of
prepareData
is stored in the Screen’s propertydata
To use a service, declare it as a property of your class. Then, call getCatalog
method, and log the result.
Caution
Keyword properties
allow you to declare properties on the instance of your class. Properties can be classic JavaScript types (string
, number
, boolean
…) but can also be Dana’s object. In that case use class
keyword to indicate the class to use.
import $VodService from "@VodService";
/**
* Catalog screen
*
* @name CatalogScreen
* @class
* @extends AppScreen
* @screen
* @property {VodService} vodService Instance of $VodService
*/
export default $AppScreen.declare("CatalogScreen", {
properties: {
vodService: {class: $VodService}
},
methods: {
prepareData: function (context) {
return this.vodService.getCatalogs();
},
connectData: function (context) {
console.log(this.data);
}
}
});
Observe your debug console, an error as been thrown : You must implement AbstractVodService._getCatalogs
. As project is not using any implementation of VodService
, it is using abstraction. Looking at CustomBEVodService
, you can see that there is an implementation of multiple protected methods including _getCatalogs
. To use this implementation instead of abstraction, you should add this vendor to your profile.
{
"base-lightning": {
"base": {
"name": "Lightning HTML5",
"vendors": [
"@dana/renderer-lightning-html5",
"customBE"
]
}
},
"base-css": {
"base": {
"name": "Css",
"vendors": [
"@dana/renderer-css",
"customBE"
]
}
}
}
Caution
Declaration order of the vendors have importance. Implementations choices are based on this order, resulting last implementation override to be the choosen one.
Do not forget to launch back your profile to see expected result in the debug console :
> CatalogList
> 0 : AbstractCatalog
> 1 : AbstractCatalog
Retrieve custom contents
CustomBE
is already implementing some of the methods you need in VodService
. You are going to implement one of them: _getContents
. This one should retrieve contents for a category, here movies. A JSON file is available to give you contents to parse. You need to transform those raw datas into Content
. Using models is important to guarantee that all your implementations will return same structure of information.
Import JSON file and Content
:
import contents from "contents.json!";
import $Content from "@Content";
Note
!
is used to import JSON files. You do not need to write path to file, juste write its name.
Implement _getContents
method :
_getContents: function (categoryId) {
const result = contents.movies[categoryId].map(element => new $Content({data: element}));
return Promise.resolve(result);
}
In CatalogScreen
, update prepareData
to call getContents
with a category id part of the JSON. For example :
prepareData: function (context) {
return this.vodService.getContents("forYou");
}
Observe in the debugger console what you retrieved :
> ContentList
> AbstractContent
> AbstractContent
...
As for the service, you can see there is no implementation for Content
so abstract is used. If you take a look at properties of AbstractContent
, you can observe title or description are null
. To populate them based on information coming from the JSON, you need to create an implementation of Content
. This one will do the mapping between keys in the JSON and keys on the model.
Create a new file CustomBEContent
under vendors > customBE > scripts > services > model
.
import $AbstractContent from "@AbstractContent";
/**
*
* @name CustomBEContent
* @model
* @extends AbstractContent
* @implementation
*
* @property {number} id
* @property {string} title
* @property {string} description
* @property {array} genres
*/
export default $AbstractContent.declare("CustomBEContent", {
properties: /** @lends CustomBEContent.prototype */ {
id: ({data}) => data.identifier,
title: ({data}) => data.name,
description: ({data}) => data.summary,
genres: ({data}) => data.types
}
});
Note
Properties in Model
are using lazy loading. Unless you tried to access to property, its value is not calculated. Once binding function has been executed once, its value is stored for next access.
Observe the new logs in your debug console. You now have CustomBEContent
instead of AbstractBEContent
, and properties are well populated.
Summary
To summarize, during this lesson you have :
- added a new vendor to your profiles,
- created a custom model,
- implemented a method of a service using your custom model
To go further on those concepts, you can browse the following documentation pages :
- Vendor // TODO add vendors link
- Property // TODO add property link