LogoLogo
DocsUse CasesCreator GuideIntegration Guide
  • Introduction
  • Why Monterosa?
  • Platform Buyer FAQ
  • Engagement Use Cases
    • Event Centres
    • Prediction Games
    • Voting
    • Trivia Challenges
    • Competitions & Sweepstakes
    • Sports Data Visualisation
    • Native Apps
    • Global Replication
  • Core Platform Concepts
    • Platform Components
    • App Settings
    • Schedule & Events
    • Timeline
    • Elements
    • Extensions
    • EnMasse Mesh
  • 🎨Creator Guide
    • Welcome
      • Getting an Account
    • Spaces
      • Projects
        • Events
          • Event Templates
          • Event Settings
          • Timeline
            • Elements
            • OpenAI Integration
            • Live Stats
            • Preview
            • Embedding
              • Embedding an Element
          • Analytics
        • Project Settings
          • Localisation
          • Experience URL
          • Content Creation Limits
        • App Settings
        • Activity Log
      • Content
        • Asset Library
        • Automation
          • 🔃Bulk Content Uploader
          • ⚡Live Data Connector
      • Setup
        • Space Settings
        • Apps
        • Integrations
          • Opta Graphics
          • Tagboard
        • Extensions
          • Identify
          • Gamify
          • Forms
          • Geo-Verify
          • Content Bridge
          • Vote Verification
    • Experiences
      • Experience Creator
        • Customising your UI
        • Retrieving Event URLs
    • Team
      • Two-Factor Authentication
      • Setting Up SSO to log in the Monterosa platform
      • User Roles
    • Scaling & Capacity
    • Creator FAQs
  • 🚀Integration Guide
    • Overview
    • Standalone Pages
    • Web Page Embedding
      • Hosting an Experience on your domain
      • Auto-resizing embedded Experiences
    • Native App Embedding
    • Embed using the SDK
    • Discovering Events Programmatically
    • Audience Profiles
      • Audience Profiles Dataset Reference
        • Users dataset
        • Projects dataset
        • Events dataset
        • Elements dataset
        • Interactions dataset
        • Leaderboard (Event) dataset
        • Leagues dataset
        • Form entries dataset
      • Monterosa Identifiers
    • Integrating IAM & SSO
      • Simple integration with SDK & JWT
        • Advanced SDK integration
      • JWT based SSO integration
      • Ad-hoc SSO integration
      • SSO Integration guidelines
    • Consent Management integration
      • Client-Side Data Storage Explained
    • Analytics integration
    • Integration FAQs
  • 📱FanKit
    • Overview
    • How it works
    • Elements
      • Polls & Voting
        • Brand Survey
        • Poll
        • Reaction Rater
        • Ladder Poll
        • Swing Poll
        • Smash Rater
        • Category Wrapper
        • Category Vote
        • Standalone Vote
        • Team Selector
      • Predictors
        • Predictor
        • Sweepstake
        • Number Predictor
        • Podium Predictor
        • Score Predictor
        • Score Predictor (High)
        • Player Predictor
      • Trivia
        • Trivia
        • Free Type Trivia
        • Ladder Trivia
        • Sorting Trivia
        • Word Trivia
        • Video Trivia
        • Number Trivia
      • Editorial & Media
        • Article
        • External Article
        • Commentary
        • Gallery
        • Image
        • Video
        • Video Carousel
        • Shareable URL
        • Social Embed
        • Form
        • Element Carousel
        • Match Event
        • Substitution
        • Goal
        • Time Event
      • Quiz
        • Quiz Wrapper
        • Quiz Question
      • Series Prediction
        • Series Wrapper
        • Series Score Predictor
        • Series Predictor
        • Series Number Predictor
        • Series Player Predictor
      • Multi Rater
        • Ratings Wrapper
        • Ratings Slider
      • Sports Statistics
        • Opta Widget
        • Stat Callout
        • Stat Comparison
      • Gamification
        • Scoreboard (Event)
        • Scoreboard (Round)
        • Leaderboard (Project)
        • Leaderboard (Event)
        • Leaderboard (Round)
        • Leaderboard (Group)
    • Tips, tricks, tutorials & guides
      • Advanced CSS Overrides
      • Using points and leaderboards
      • Content containers
    • Display Modes
    • Converters
      • Access Gate
      • Form
      • Cards
      • Share Image
      • Featured Links
      • Sponsors
      • Promo Banner
      • Google Ads
    • Localisation
    • Supported Browsers
    • Release Notes
  • 👣DEVELOPER GUIDE
    • Overview
    • Platform Terminology
    • Interaction SDK
      • Download & Initialise the SDK
      • Supported Devices & Operating Systems
      • Roadmap
      • Release Notes
        • React Native
          • v0.2.2
        • JavaScript
          • v0.16.16
          • v0.16.15
          • v0.16.14
          • v0.16.13
          • v0.16.12
          • v0.16.11
        • iOS
          • v0.16.12
          • v0.16.11
        • Android
          • v0.16.13
      • SDK FAQs
    • Embedding Apps
      • Static Embedding
      • Dynamic Embedding
        • Web Behaviours
        • iOS Behaviours
        • Android Behaviours
        • Preloading
        • Caching Content
        • Shared Storage
      • Configure your app
      • Communicate with embedded apps
      • Video Stream Sync
      • React Native
        • Events in React Native SDK
      • Integrate with Flutter
    • Building Apps
      • Quick Start Guide
      • How to build your first app
        • Getting started
        • Get your app running locally
        • Install the Interaction SDK
        • Define your App
        • Set up Studio
        • Develop your App
        • Enable Dynamic Configuration
      • Core SDK Functions
        • Getting and displaying Events
        • Managing Elements
        • Reacting to live updates
        • Dynamic runtime configuration
        • Time
        • Localisation
    • Content Automation
      • Discovering Events
      • Connecting to Google Sheets
      • Connecting to Zapier
      • Connecting to Sports Feeds
    • App Specs
      • App Spec Reference
        • Root Spec
        • Elements Spec
        • Project Settings Spec
        • Event Settings Spec
        • Expressions (beta)
        • Data Sources (beta)
        • External data source
        • Field Types
          • Boolean
          • Collection
          • Colour
          • Datetime
          • External
          • File
          • Freetext
          • Image
          • List
          • Number
          • WYSIWYG
        • Field, Field set, Field section
      • Customising App Specs
    • Available APIs
    • Integrating TV Graphics
    • Advanced Topics
      • Listings
      • Accesssing user concurrency values from your app
      • Custom analytics Extension
      • Front-end app communication protocols
      • Multi Vote
      • Reveal answer on vote
  • ⚙️CONTROL API
    • Overview
    • Objects Definitions
    • Element Reference
    • Organisations
      • Get Organisations
      • Get Organisation
      • Create Organisation
      • Update Organisation
      • Delete Organisation
    • Spaces
      • Get Spaces
      • Get Space
      • Create Space
      • Update Space
      • Delete Space
    • Apps
      • Get Apps
      • Get App
      • Create App
      • Reload App
    • App Settings
    • Projects
      • Get Projects
      • Get Project
      • Create Project
      • Update Project
      • Delete Project
    • Events
      • Get Events
      • Get Event
      • Get Event Service Feeds
      • Create Event
      • Create Event From Template
      • Update Event
      • Delete Event
    • Event Templates
      • Get Event Templates
      • Get Event Template
      • Create Event Template
      • Update Event Template
      • Delete Event Template
    • Elements
      • Get Elements
      • Get Element
      • Get Event Template Elements
      • Create Element
      • Update Element
      • Delete Element
    • Assets
      • Get Assets
      • Upload Assets
      • Update Asset
      • Delete Asset
      • Get Asset Media Types
      • Get Asset Uploaders
      • Asset Tags
        • Add Tag to Asset
        • Remove Tag from Asset
    • Tags
      • Get Tags
      • Get Tag
      • Create Tag
      • Update Tag
      • Delete Tag
  • ⚙️REFERENCE
    • Public Feeds
      • Graphics Feed
    • Listings
    • Elements
    • Event History
    • Demographics
    • Identify
    • Gamify
      • Gamification Mechanics
      • Public API
      • Private API
    • Geo-Verify
    • Browser Support
    • Audience API (Preview)
      • Authentication
        • Monterosa Access Token
        • Public client authentication
      • Batched operations
      • Loyalty API
      • Connections API
    • Image Resizing API
  • ⛓️Extensions
    • Overview
    • Instance handshake
    • Project handshake
    • Project delete
    • Tabs
    • Event Tabs
    • Setup
    • Listings notification
    • Elements notification
    • Analytics
    • Assets
    • Event-level feed
    • Webhooks
  • ⚖️COMPLIANCE
    • Data Requests
    • Data Sub-processors
    • Hosting Locations
    • Third party software
    • ISO27001
  • 🗒️RELEASE NOTES
    • Studio
      • v29.0
      • v28.0
      • v27.0
      • v26.0
      • v25.0
    • FanKit
      • v24.41.0
      • 24.42.0 Preview
    • Archive
      • v24 releases 2022-23
Powered by GitBook
On this page
  • One-way Elements
  • Interactive Elements
  • Responding to an Interactive Element
  • Displaying the results of an Interactive Element
  • Revealing the correct option or result

Was this helpful?

  1. DEVELOPER GUIDE
  2. Building Apps
  3. Core SDK Functions

Managing Elements

How to respond to new Elements and display them

Last updated 10 months ago

Was this helpful?

Within an Event, creators or automations will be publishing Elements – bi-directional content units that have certain properties and behaviours built in, as defined in the . In general, there are two types of Elements:

  1. Interactive Elements: those that are bi-directional in nature and allow your users to interact with the content. For example by providing live results and seeing how other people have responded. You might choose to define an interactive Element to behave like a poll, containing a question, a number of possible answer options and a duration.

  2. One-way Elements: those that are broadcast to all, but do not require a response. For example an information unit, like a message, promotion or image gallery.

An Event may include multiple Elements. For example if you create an Event for a soccer game, an Element might be a result prediction appearing before the game starts and another that triggers whenever a goal is scored.

Interaction is the purpose of the platform, but we'll start with simpler one-way Elements.

One-way Elements

Let's look at how to use non-interactive One-way Elements in your application.

The snippet below retrieves the array of Elements already existing within the Event and illustrates how you can use its contentType to differentiate each Element type. In the example, we determine a single type of Element being received - goalScored.

async function displayElements(event) {
  try {
    const elements = await getElements(event);

    for (const element of elements) {
      switch (element.contentType) {
        case 'goalScored':
          displayGoalScored(element);
          break;
      }
    }
  } catch (e) {
    console.error('Something went wrong!', e);
  }
}

function displayGoalScored(element) {
  // In this function we know the element is a goal scored, so we can 
  // equally make some assumptions about what data will be available.
  // As you can see below, given the control you have over the App 
  // Spec, you can provide via Studio as much data as you want to support,
  // in this example the goal scorer name, number, and the amount of goals
  // they scored today, so you can add some extra fanfare
  // in your UI when they score a hat trick!
  const {
    fields: {
      goalScorerName,
      goalScorerNumber,
      goalScorerGoalsToday,
    },
  } = element
  
  // Draw the UI based on your UI framework.
}
func displayElements(in event: Event) {
    event.getElements { [weak self] result in
        guard let self = self else { return }
        do {
            let elements = try result.get()
            elements.forEach { element in
                switch element.contentType {
                case "goalScored":
                    self.displayGoalScored(element)
                default:
                    // Element type not recognised.
                    return
                }
            }
        } catch {
            // Treat the error
        }
    }
}

func displayGoalScored(_ element: Element) {
    // In this function we know the element is a goal scored, so we can 
    // equally make some assumptions about what data will be available.
    // As you can see below, given the control you have over the App 
    // Spec, you can provide via Studio as much data as you want to support,
    // in this example the goal scorer name, number, and the amount of goals
    // they scored today, so you can add some extra fanfare
    // in your UI when they score a hat trick!
    let goalScorerName = element.fields["goalScorerName"]
    let goalScorerNumber = element.fields["goalScorerNumber"]
    let goalScorerGoalsToday = element.fields["goalScorerGoalsToday"]
    
    // Draw the UI based on your UI framework.
}
fun displayElements(event: Event) {
    event.getElements {
        it.onFailure {
            // Treat the error, `it` is a throwable
        }
        it.onSuccess {
            it.forEach { element ->
                when (element.contentType) {
                    "goalScored" -> displayGoalScored(element)
                    else -> {
                        // element type not recognised
                    }
                }
            }
        }
    }
}

fun displayGoalScored(element: Element) {
    // In this function we know the element is a goal scored, so we can 
    // equally make some assumptions about what data will be available.
    // As you can see below, given the control you have over the App 
    // Spec, you can provide via Studio as much data as you want to support,
    // in this example the goal scorer name, number, and the amount of goals
    // they scored today, so you can add some extra fanfare
    // in your UI when they score a hat trick!
    val goalScorerName = element.fields["goalScorerName"]
    val goalScorerNumber = element.fields["goalScorerNumber"]
    val goalScorerGoalsToday = element.fields["goalScorerGoalsToday"]
    
    // Draw the UI based on your UI framework.
}

getElements will return a list of elements sorted so that the most recent element is first, and the oldest is last.

Additionally, you can subscribe to updates to a given Element using the following snippet:

// Called when the results of an interactive element change
const unsubscribeOnElementResults = onElementResults(
    element, 
    (results) => { console.log(results) }
);

// Called when the element is updated
const unsubscribeOnElementUpdated = onElementUpdated(
    element, 
    () => { console.log(element) }
);

// Called when the state of the element is updated
const unsubscribeOnElementStateChanged = onElementStateChanged(
    element, 
    () => { console.log(element) }
);

Interactive Elements

Interactive Elements get fans involved and make your experiences more rewarding. They have the following properties which differ from One-way Elements:

  • State: open or closed state, where open means the user can still interact with the Element. The timespan where the user can vote can be obtained through the duration property.

  • Question and AnswerOptions: The Element usually will have a question associated with it for the user to answer by interacting with some potential answers. Through the App Spec you can specify what data is included in either the Question and AnswerOptions. This enables you to create rich questions and answers which could include videos, images, links to other content on your application, ...

  • Results: Results bring a sense of belonging into your users interaction, by letting your users know how their peers are answering a question. You will receive these values both as a total vote count as well as a percentage of the total votes.

The snippet below updates the previous one to add a new type of element, a poll:

async function displayElements(event) {
  try {
    const elements = await getElements(event);

    for (const element of elements) {
      switch (element.contentType) {
        case 'poll':
          displayPoll(element);
          break;
        case 'goalScored':
          displayGoalScored(element);
          break;
      }
    }
  } catch (e) {
    console.error('Something went wrong!', e);
  }
}

function displayPoll(element) {
  const {
    question: {
      text: questionText,
      imageURL: questionImageURL,
    },
    answerOptions,
    state,
    fields: {
      poll_custom_field: pollCustomField,
    }
  } = element;
  
  const answers = answerOptions.map(({ text }) => text);
  
  // Draw the poll
}
func displayElements(in event: Event) {
    event.getElements { [weak self] result in
        guard let self = self else { return }
        do {
            let elements = try result.get()
            elements.forEach { element in
                switch element.contentType {
                case "poll":
                    self.displayPoll(element)
                case "goalScored":
                    self.displayGoalScored(element)
                default:
                    // Element type not recognised.
                    return
                }
            }
        } catch {
            // Treat the error
        }
    }
}

func displayPoll(_ element: Element) {
    let question = element.question!["text"]
    let questionImageURL = element.question!["imageURL"]
    
    let answers = element.answerOptions!.map { answerOption in
        return answerOption["text"] as! String
    }
    
    let pollCustomField = element.fields["poll custom field"]
    
    let state = element.state
    
    // Draw the poll
}
fun displayElements(event: Event) {
    event.getElements {
        it.onFailure {
            // Treat the error, `it` is a throwable
        }
        it.onSuccess {
            it.forEach { element ->
                when (element.contentType) {
                    "poll" -> displayPoll(element)
                    "goalScored" -> displayGoalScored(element)
                    else -> {
                        // element type not recognised
                    }
                }
            }
        }
    }
}

fun displayPoll(element: Element) {
    val question = element.question!!["text"]
    val questionImageURL = element.question!!["imageURL"]

    val answers = element.answerOptions!!.map {
        it["text"]
    }

    val pollCustomField = element.fields["poll custom field"]
    val state = element.state;
    
    // Draw the poll
}

Responding to an Interactive Element

Once you have displayed the answer options on screen, submit the user's answer using the answer()method.

In the snippet below we show how an answer can be submitted, alongside its error handling so you can contextualise to the user if anything goes wrong. We assume that the variable userAnswer contains the 0-based index of the answer option selected by the user.

function sendAnswer(element, index) {
  try {
    // index should be e.g. 0 to the count of options available - 1
    answer(element, index);
    // Your answer was sent successfully
  } catch {
    if (e instanceof MonterosaError) {
      switch (e.code) {
        case AnswerError.OptionIndexOutOfRange:
          // The index is outside the range of possible options
          break;
        case AnswerError.AboveMaxVoteOptions:
          // You voted for more indexes than is allowed
          break;
        case AnswerError.BelowMinVoteOptions:
          // You voted for fewer indexes than is allowed
          break;
        case AnswerError.AboveMaxVotesPerUser:
          // You voted with a higher total value than is allowed
          break;
        case AnswerError.AboveMaxVotesPerOption:
          // You voted with a higher value than is allowed in a single option
          break;
        case AnswerError.VotedOnNonInteractiveElement:
          // You tried to vote on a non interactive element
          break;
        case AnswerError.VotedOnClosedElement:
          // You voted on closed element
          break;
        default:
          // Shouldn't occur, but be ready for it as more cases could be added in the future.
          break;
      }
    } else {
      // Shouldn't occur
    }
  }
}
func answer(element: Element, with index: Int) {
    do {
        try element.answer(with: index)
        // Your answer was sent successfully
    }
    catch let err as Element.AnswerError where err == .optionIndexOutOfRange {
        // The index is outside the range of possible options
    }
    catch let err as Element.AnswerError where err == .aboveMaxVoteOptions {
        // You voted for more indexes than is allowed
    }
    catch let err as Element.AnswerError where err == .belowMinVoteOptions {
        // You voted for fewer indexes than is allowed
    }
    catch let err as Element.AnswerError where err == .aboveMaxVotesPerUser {
        // You voted with a higher total value than is allowed
    }
    catch let err as Element.AnswerError where err == .aboveMaxVotesPerOption {
        // You voted with a higher value than is allowed in a single option
    }
    catch let err as Element.AnswerError where err == .votedOnNonInteractiveElement {
        // You tried to vote on a non interactive element
    }
    catch let err as Element.AnswerError where err == .votedOnClosedElement {
        // You voted on closed element
    }
    catch {
        // Shouldn't occur
    }
}
fun answer(element: Element, index: Int) {
    try {
        element.answer(index)
        // Your answer was sent successfully
    } catch (e: ElementVoteError){
        when (e.errorType) {
            ErrorType.OPTION_INDEX_OUT_OF_RANGE -> // The index is outside the range of possible options
            ErrorType.ABOVE_MAX_VOTE_OPTIONS -> // You voted for more indexes than is allowed
            ErrorType.BELOW_MIN_VOTE_OPTIONS -> // You voted for fewer indexes than is allowed
            ErrorType.ABOVE_MAX_VOTES_PER_USER -> // You voted with a higher total value than is allowed
            ErrorType.ABOVE_MAX_VOTES_PER_OPTION -> // You voted with a higher value than is allowed in a single option
            ErrorType.VOTED_ON_NON_INTERACTIVE_ELEMENT -> // You tried to vote on a non interactive element
            ErrorType.VOTED_ON_CLOSED_ELEMENT -> // You voted on closed element
            else -> {
                // Shouldn't happen but in case new errors 
                // are added in the future.
            }
        }
    }
}

Elements can be configured to allow for multiple answers through their App Spec using three values:

  • Maximum votes per user: This specifies how many votes a user can cast, either on the same option or on multiple options. E.g. a user can emit 5 votes for a given question, either all 5 on the same option, or spread through multiple options.

  • Maximum and Minimum options per vote: This specifies the minimum and maximum options a user must include in their vote. E.g. a user must pick at least 2 options and at most 3.

In some scenarios, you may wish to validate that a given answer is correct before attempting to submit it. For instance, this could be helpful if you want to highlight in red that the user's choice is not a valid response and accompany with some explanatory text on how to correct it.

To that effect, we offer you a method called validateAnswer() that lets you perform the same validations the answer() method performs, and reports any potential errors in the same manner.

// You can validate your answer at any point, so as to provide
// feedback to your user about what went wrong:
try {
  validateAnswer(element, index);
} catch (e) {
  // The same error handling as was done when calling `answer`
}
// You can validate your answer at any point, so as to provide
// feedback to your user about what went wrong:
do {
    try element.validateAnswer(index)
}
catch {
    // The same error handling as was done when calling `answer`
}
try {
    element.validateAnswer(index)
} catch (e: ElementVoteError) {
    // The same error handling as was done when calling `answer`
}

Displaying the results of an Interactive Element

After the user selects their response to the Element, you may want to show them what other people are saying. The platform collects responses and periodically sends aggregated stats back to all connected clients. Here's how you receive those results:

function displayPoll(element) {
  // ...
  const { results } = element;

  if (results === null) {
    // We don't have yet results, so reflect the case on the UI
    return;
  }

  // We are able to show results to the user
  const voteCount = results.map((result) => result.votes);
  const votePercentage = results.map((result) => result.percentage);
}
func displayPoll(_ element: Element) {
    // ...
    let results = element.results
    
    if let results = results {
        // We are able to show results to the user
        let voteCount = results.map { $0.voteCount }
        let votePercentage = results.map { $0.votePercentage }
    } else {
        // We don't have yet results, so reflect the case on the UI
    }
}
fun displayPoll(element: Element) {
    val results = element.results
    if (results != null) {
        val voteCount = results.map { it.votes }
        val votePercentage = results.map { it.percentage }
    }
}

Revealing the correct option or result

You can also display to the user which of all the options is the correct option to select, which is a critical feature when building for instance a Quiz.

if (element.userAnswer === null) {
  return;
}

const correctOptionIndex = element.correctOption;
// Highlight the correct option from your collection of options

The following snippet showcases how we check if the user has already submitted an answer, and if so, we obtain the correct option. Notice that depending on how you configure your Element in the , the correct option may be revealed at different times, so it's possible that the user has answered, but the real answer is not yet received. This is common when building Prediction Elements.

👣
App Spec
App Spec