markwpearce / roku-mvvm

Example project is an effort to show how a MVVM architecture can work with Roku SceneGraph.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ROKU-MVVM

This example project is an effort to show how an MVVM architecture can work with Roku SceneGraph.

Application

This example project allows you to add multiple counters/incrementers to the screen. CounterCollectionView has buttons to add or remove the counters. When a counter is added, the view reacts to add additional CounterView components.

The counters themselves are initialized to zero, but their values are stored in the registry on each update. This means that each time the app is launched, if a counter had been previously created, it will be automatically set to the last stored value.

Getting Started

  1. Run npm install
  2. Create an .env file like:
ROKU_HOST=<IP address of Roku Device>
ROKU_PASSWORD=<Developer password of Roku Device>
  1. Run from command line: npm run sideload
  2. Run from VSCode

Architecture

The Model-View-ViewModel architecture pattern aims to separate the view/user-interface from the business logic of the app. In this example, that is achieved through using SceneGraph components that extend from BaseView and BaseViewModel.

Model

The model is a component that encapsulates simple state or data. In order to observe fields on the model, and so it can be passed across component boundaries, the model must be a SceneGraph node. There is no specific base component for these models, although in many cases, the SceneGraph node ContentNode could be used.

View

The view is responsible for display, navigation/focus and signaling events based on user interaction only. Any updates to the view are based on observing properties/fields of the view model. In this example, all views extend the component BaseView. Using the function bindViewModelField views can bind values directly from the view model, or receive events when a view model's field changes.

View Model

The view model is responsible for handling commands generated by the view associated with user interaction, coordinating those events with any changes that may happen to the underlying data. It is event-driven based on observing fields of the view. It may have an underlying model that represents the internal data. In this example, all view models extend the component BaseViewModel.

Bonus: Managers

In order to encapsulate specific processes (either for data management, external communication, handling specific view logic like focus or screen stacks, etc.), managers are used. These are singleton components that all other components have access to. Managers have public functions for the specific tasks they can do. Managers extend the component BaseManager. Managers are analogous to services in some other frameworks.

Types

This project uses Brighterscript Version 1.

Brighterscript V1 allows rich typing of components and other entities. Specific types declarations for each aspect of the app are included in files named interfaces.bs in the same folder. These are then included in each component scope via an import statement.

When declaring the types for a SceneGraph component, there are two different interfaces that should be created: one to represent the properties on m, and potentially an interface narrowing of the component node's fields.

Example

In the CounterViewModel component, the model field is defined as a node, but for our purposes, we know it will be the specific node of subtype CounterModel. This we extend the given type roSGNodeCounterViewModel with the specifics:

interface CounterViewModelTyped extends roSgNodeCounterViewModel
  model as roSGNodeCountModel
end interface

Likewise, inside the component, we know there will be specific properties available on m. One of these is m.top, which will be the specific type CounterViewModelTyped:

interface CounterViewModelM extends BaseViewModelM
  top as CounterViewModelTyped
end interface

Then at the top of each file specific to that component, the interfaces.bs file in imported, and we tell the compiler what to treat m as using the typecast statement:

import "interfaces.bs"
typecast m as CounterViewModelM

Brighterscript can now validate against those types, ensuring type-safety at code- and compile-time for all properties and fields for that component.

Launching your app

This project assumes that you will be using VSCode as your code editor.

  1. Go to the Run and Debug panel.

  2. Select the option Launch roku-mvvm (dev)

NPM Commands available

  • build: Builds your project with brighterscript. Includes source maps.

  • build:prod: Builds your project without source maps.

  • lint: Lints your source files with @rokucommunity/bslint

  • lint:fix: Lints your source files and applies automatic fixes.

  • format: Formats your source files with brighterscript-formatter

  • format:fix: Formats your source files and applies automatic fixes.

This project was created using npx create-roku-app

About

Example project is an effort to show how a MVVM architecture can work with Roku SceneGraph.


Languages

Language:BrighterScript 99.3%Language:Bluespec 0.7%