Skip to main content
Version: Next

Contexts

The purpose of contexts is to be able to store and retrieve data from any places in the application. There are several use cases such as

  • Application State. When you want to store some information at the application level that can be consumed by any components in the application.
  • Iterators. From inside an iterator (using Map, For Each or similar bricks), you often want to be able to read and/or write data in the parent context where the iterator has been called.
  • Complex scenarios when it could be useful to set or retrieve information from a particular component or brick.
    See the list of possible bricks

Understanding contexts in Draw

When Draw runs an application, it builds a tree of contexts, from the application down to any screen, components and bricks that need to run.

context-tree

From the image above you can understand that there is a context for each "layer".

  • First the application is executed: creates an Application Context
  • The application runs the screenflow: creates the Screenflow Context
  • The screenflow runs a screen: creates the Screen Context
  • The screen runs
    • Visual components: each of these will have their own Context
    • Anonymous functions such as On Load: each of these will have their own Context

The Application Context values are inheritted down the tree in sequence by Screenflow Context, then by Screen Context and so on.
Values can be overriden at any stage down the tree.

info

Contexts are based on runtime execution of the application. They are unique for each instantiation of application.

Also, when a component or screen is removed, its context is destroyed.

Using Contexts to handle Application State

When you need several parts of the application to share data, you have several options

  • Use Data Model. Using Data Model makes total sense when dealing with business data. Especially when this data is persisted inside the application. It could however be cumbersome to go through a Data Model for application state.
  • Bind the data through properties at each layer between the components. Binding properties is well suited for components that, by nature, have strong coupling. Also, it has the benefit to very clearly link the data and help for discoverability.
  • Use contexts to store and retrieve data from the application context. Using the Application State enable loosely coupled components and scale better when the application grows.

Let's take a look at how to do the latter.

Storing Data in the Application State

Use the brick Set in Application Context

set-in-application-context

You can pass any value and assign it to a key. This key will be used by other bricks to retrieve the assigned value. As an input of the key you can use any string or an Enum value.

Retrieving Data from the Application State

Use the brick Get From Current Context

get-from-current-context

Use the key to target which value you want to retrieve from the context. This must align with a same key set somewhere else from a brick such as Set In Application Context.

tip

Wait, but I see that there is a Get From Application Context brick. Shouldn't I use this one instead?

You could use this brick indeed. However there is subtle difference. When reading context data, the system will go up the tree of contexts until it finds a context that holds a value for a specific key. It means that it will anyway read from the Application Context if no other context assigns a value to the same key "in between".

  • Use Get From Current Context when you want to create a generic behaviour that will use Application Context if nothing is set in between but can be overriden by another context.
  • Use Get From Application Context when you want to be 100% sure that the data you retrieve is from the Application Context regardless of any overriden values from contexts in between.

In most cases the end result is similar, but choose the pattern depending on how generic you want your component/brick to be.

Using Contexts to communicate between a Map, For Each, ... and its lambda function

Some bricks accept what we call a lambda function as an argument. It is a function that will be executed from the brick itself. A Map brick for example accepts a mapper input, which is a lambda function.

Map brick used somewhereThe lambda function (mapper)
context-map-mapper-inputcontext-map-mapper-empty

In terms of Context we have a tree that looks like

  • Application Context
    • Screenflow Context
      • Screen Context
        • Any Visual Component Context in between
          • Context where the Map brick is used (Brick Context)
            • Context of the lambda function (TODO: correct name) (Lambda Context)
tip

This is a simplification, focusing only on the composition Contexts. In reality there are other Contexts in between but they are abstracted away when using the Contexts bricks.

Pass Data from the Brick Context to the Lambda Context

This is by far the most common use case. As we cannot set inputs in the lambda, we need to use Contexts to pass data to the lambda.

  • Use Set In Current Context from the Brick Context
  • And use Set From Current Context in the Lambda Context with the same key

(example below)

Set Data in the Brick Context from the Lambda Context

This can also be useful when the lambda processes some data that must be passed back to the Brick Context.

  • Use Set In Parent Context from the Lambda Context
  • And use Get From Current Context in the Brick Context
Map brick
context-map
The lambda function (mapper)
context-map-mapper

Complex scenarios using Contexts

We provide a set of low-level bricks to enable all sorts of scenarios with Contexts. Here the list of bricks available:

Getting a Context

  • Get Current Context: return the current context where this brick is placed
  • Get Parent Context: return the parent context from where this brick is placed
  • Get Application Context: return the application context
  • Get Screen Context: return the screen context
  • Get Parent Of Context: return the parent context from the one given in input
  • Get Closest Context: return the closest parent context of a specified type. The model type input must receive the tag of the model we want to find. For example, to find the closest "Screen" context, we pass the tag of the Screen model.

Setting Value in a Context

  • Set In Context: assign a value to the context given in input
  • Set In Application Context: assign a value to the application context
  • Set In Screen Context: assign a value to the screen context
  • Set In Current Context: assign a value to the current context where the brick is placed
  • Set In Parent Context: assign a value to the parent context from where this brick is placed

Getting value from a Context

  • Get From Context: return the assigned value from the context given in input
  • Get From Current Context: return the assigned value from the current context where the brick is placed
  • Get From Application Context: return the assigned value from the application context
  • Get From Screen Context: return the assigned value from the screen context

Removing a value from a Context

  • Clear In Context: clear the assigned value in the given context

Known limitations

You should not use the same name for a Context key than the name of an input or output of the brick where this key is used. This will cause a conflict in the behaviour of the contexts brick.

If a key defined in some other location than the current brick, happens to have the same name as an input or ouput of the brick we are working on, in which the Context needs to be accessed, renaming the conflicting input or output parameter is the recommended workaround to solve this situation.
This limitation will be addressed in a future release.

context-key-conflict