Forum Discussion

kvanderwende's avatar
7 years ago

roUrlTransfer on Item Select

I have downloaded samples and searched the forum and I am having trouble getting something to work.  My situation is this.  I have a database that returns JSON for a grid screen. I have functions to use roUrlTransfer to retrieve the data.  The grid is shows (podcasts).  Each show has 1 or more seasons with episodes.  I don't want to load anything until it is needed.  So the grid just displays the shows.  When one is selected I want to retrieve the seasons and episodes for that show.

My home scene works fine because I am getting the items for the grid during init.  I understand that I cannot use roUrlTranfer from a render node.  I tried following an example using Task but when the task is initialized I don't have a selected item so there is nothing to return.  I tried running the task when the item is selected but I still get an error that says "BRIGHTSCRIPT: ERROR: roUrlTransfer: creating MAIN|TASK-only component failed on RENDER thread".

XML
<component name="ShowTask" extends="Task" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://devtools.web.roku.com/schema/RokuSceneGraph.xsd">
<interface>
    <field id="showIndex" type="int" value="0"/>
    <field id="showContentAA" type="assocarray"/>
</interface>
<script type="text/brightscript" uri="pkg:/components/Tasks/ShowTask.brs" />
</component>


BRS
sub init()
    m.top.functionName = "getContent"
end sub

function getContent() as void
    print "getContnent"
    if m.top.showIndex <> invalid
        m.top.showContentAA = getShowLeaves(m.top.showIndex)
    end if
end function


I am not sure how to get that to work.  I followed the Simple Task example and I only ever see it hit getContent one time.

m.showTask = CreateObject("roSGNode", "SimpleTask")
m.showTask.ObserveField("showIndex", "onIndexChanged")
m.showTask.control = "RUN"


If I set showIndex when the item is selected getContent never fires.  I think I am not fully grasping how Tasks work.

4 Replies

  • The Task is a background process with a call back by the observe field.

    You should be initiating the run command when the item is selected and then observe the field that has the content returned from the remote source.

    Once the roUrlTransfer has returned the data set the interface field that should contain that data with the returned data and that should then trigger the observe field on the main thread from the calling script.

    FYI, I have read that it is better to process the JSON on the main thread as tasks can be quite slow, though this could impact UI performance for large amounts of data.
  • "gazer" wrote:
    The Task is a background process with a call back by the observe field.

    You should be initiating the run command when the item is selected and then observe the field that has the content returned from the remote source.

    Once the roUrlTransfer has returned the data set the interface field that should contain that data with the returned data and that should then trigger the observe field on the main thread from the calling script.

    FYI, I have read that it is better to process the JSON on the main thread as tasks can be quite slow, though this could impact UI performance for large amounts of data.

    Thank you for the feedback.  I think my problem was WHERE to run the task.  I have experimented with loading everything from main but was not sure how much data is too much.  I have 15 shows with 1-9 seasons with episodes in each season.  Currently around 900 episodes (podcasts)
    I am going to attempt to get everything working with loading in main then revisit using a Task because at some point I will hit the point where loading everything up front will be a performance hit.
  • coldrain's avatar
    coldrain
    Binge Watcher
    I think you should use the task like this:

    Init the showTask:
    m.showTask = CreateObject("roSGNode", "SimpleTask")
    m.showTask.ObserveField("showContentAA", "receiveContentFromServer")


    When you select an item from the grid, assign the showIndex value (don't observe this field) and call m.showTask.control = "RUN", then the task will be triggered and call your getContent function. Then the getContent function will retrieve the data from the server and assign to showContentAA which will fire the function receiveContentFromServer.

    Btw, I don't load everything upfront but only category and don't see any performance hit.
  • "coldrain" wrote:
    I think you should use the task like this:

    Init the showTask:
    m.showTask = CreateObject("roSGNode", "SimpleTask")
    m.showTask.ObserveField("showContentAA", "receiveContentFromServer")


    When you select an item from the grid, assign the showIndex value (don't observe this field) and call m.showTask.control = "RUN", then the task will be triggered and call your getContent function. Then the getContent function will retrieve the data from the server and assign to showContentAA which will fire the function receiveContentFromServer.

    Btw, I don't load everything upfront but only category and don't see any performance hit.

    Thank you so much.  I was observing the wrong field.  I am caching things and show loading indicator if we have to hit the server.  I had a working version of this a few years ago then things became deprecated.  That is why I have terminology like "showLeaves."
    ShowTask.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <component name="ShowTask" extends="Task" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://devtools.web.roku.com/schema/RokuSceneGraph.xsd">
    <interface>
        <field id="showID" type="string" alwaysNotify="true"/>
        <field id="showContentArray" type="array" alwaysNotify="true"/>
    </interface>
    <script type="text/brightscript" uri="pkg:/source/utils.brs" />
    <script type="text/brightscript" uri="pkg:/components/Tasks/ShowTask.brs" />
    </component>

    ShowTask.brs
    sub init()
        m.top.ShowID        = m.top.findNode("ShowID")
        m.top.showContentArray = m.top.findNode("showContentArray")
        m.top.functionName  = "executeTask"

    end sub

    function executeTask() as void
        if m.top.ShowID <> ""
           m.top.showContentArray = getShowLeaves(m.top.showID.ToStr())
        end if
    end function

    Set up task in init() of HomeScene.brs
        m.showTask = CreateObject("roSGNode", "ShowTask")
        m.showTask.ObserveField("showContentArray", "setContentFromServer")


    OnRowItemSelected
    Function OnRowItemSelected()
       m.gridScreen.visible = "false"
       selectedItem = m.GridScreen.focusedContent
       if m.seasonsEpisodesAA.DoesExist(selectedItem.id) then
            m.tlnShowScreen.showGridContent = m.seasonsEpisodesAA[selectedItem.id]
            ShowScreen(m.tlnShowScreen)
        else
            m.loadingIndicator.text = "Loading " + selectedItem.title + "..."
            m.loadingIndicator.control = "start"
            m.showTask.ShowID = selectedItem.id
            m.showTask.control = "RUN"
        end if

    End Function


    SetContentFromServer
    Function setContentFromServer()
        selectedItem = m.GridScreen.focusedContent
        
        ShowRowItems = createObject("RoSGNode","ContentNode")
        for each itemAA in m.showTask.showContentArray
            row = createObject("RoSGNode","ContentNode")
            row.Title = itemAA.Title
            for each episodeAA in itemAA.Episodes
                item = createObject("RoSGNode","ContentNode")
                item.SetFields(episodeAA)
                row.appendChild(item)
            end for
            ShowRowItems.appendChild(row)
        end for
        m.seasonsEpisodesAA.AddReplace(selectedItem.id,ShowRowItems)
        
        m.seasonsEpisodesAA.AddReplace(selectedItem.id, ShowRowItems)
        m.tlnShowScreen.showGridContent = m.seasonsEpisodesAA[selectedItem.id]
        m.loadingIndicator.control = "stop"
        ShowScreen(m.tlnShowScreen)
    End Function