Motorcykey
Visitor
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-07-2017
12:46 PM
Accessing a global object inside a SceneGraph Component
Hello,
I'm fairly new to SceneGraph. I have a library of methods, basically utility functions which I've created that I'd like to use in my SceneGraph components. Things like modifying arrays, make calls to retrieve XML from an HTTP service, and parse the XML.
After reading the documentation, it sounded like I should be using 'screen.getGlobalNode()' to assign my object of functions into a global scope that would become accessible to the SceneGraph component. However, when I take a look at the object, I'm attempting to access, all of the functions are defined as 'invalid' and attempting to access them will crash the app:
<Component: roAssociativeArray> =
{
addfavorite: invalid
analytics_parameters: "channel=23&sessionKey={0}&upc={1}&chapterId={2}&percentPlayed={3}&stopped="
bcc: <Component: roAssociativeArray>
calljsonservice_: invalid
callxmlservice_: invalid
checkkey: invalid
checklogin: invalid
checksubscription: invalid
context: <Component: roAssociativeArray>
getbookmarks: invalid
getfavorites: invalid
getkey: invalid
getloginkey: invalid
getloginkeybyuser: invalid
getsessionkey: invalid
login_key: "my_login_key"
loginmode: ""
logout: invalid
registeruser: invalid
session_key: "my_session_key"
trackevent: invalid
validateemail: invalid
validatepassword: invalid
validatereceipt: invalid
validatetransaction: invalid
}
Any variables set on the object are accessible as I would expect, but the functions themselves are not. (Basically, anything listed as "invalid" up there.)
Is there a way to store a library of globally accessible utility functions in one place that can be accessed by any SceneGraph Components?
Thank you,
-Mikey
I'm fairly new to SceneGraph. I have a library of methods, basically utility functions which I've created that I'd like to use in my SceneGraph components. Things like modifying arrays, make calls to retrieve XML from an HTTP service, and parse the XML.
After reading the documentation, it sounded like I should be using 'screen.getGlobalNode()' to assign my object of functions into a global scope that would become accessible to the SceneGraph component. However, when I take a look at the object, I'm attempting to access, all of the functions are defined as 'invalid' and attempting to access them will crash the app:
<Component: roAssociativeArray> =
{
addfavorite: invalid
analytics_parameters: "channel=23&sessionKey={0}&upc={1}&chapterId={2}&percentPlayed={3}&stopped="
bcc: <Component: roAssociativeArray>
calljsonservice_: invalid
callxmlservice_: invalid
checkkey: invalid
checklogin: invalid
checksubscription: invalid
context: <Component: roAssociativeArray>
getbookmarks: invalid
getfavorites: invalid
getkey: invalid
getloginkey: invalid
getloginkeybyuser: invalid
getsessionkey: invalid
login_key: "my_login_key"
loginmode: ""
logout: invalid
registeruser: invalid
session_key: "my_session_key"
trackevent: invalid
validateemail: invalid
validatepassword: invalid
validatereceipt: invalid
validatetransaction: invalid
}
Any variables set on the object are accessible as I would expect, but the functions themselves are not. (Basically, anything listed as "invalid" up there.)
Is there a way to store a library of globally accessible utility functions in one place that can be accessed by any SceneGraph Components?
Thank you,
-Mikey
19 REPLIES 19
joetesta
Roku Guru
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-07-2017
01:57 PM
Re: Accessing a global object inside a SceneGraph Component
rather than trying to include them as methods on the global noid, I believe you can (should?) just define global functions in source and reference them from anywhere.
For example, if you just include a file in your source folder called utils.brs and in there you can have a function;
Within the same file, define the functions '_someFunction' and '_getSomething'
Now you can instantiate myUtils from anywhere ( util = myUtils() ) and pass in variables to the defined methods ( result = util.getSomething(somevars) ).
For example, if you just include a file in your source folder called utils.brs and in there you can have a function;
Function myUtils()
this = {
someFunction : _someFunction
getSomething : _getSomething
}
return this
End Function
Within the same file, define the functions '_someFunction' and '_getSomething'
Now you can instantiate myUtils from anywhere ( util = myUtils() ) and pass in variables to the defined methods ( result = util.getSomething(somevars) ).
aspiring
Motorcykey
Visitor
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-08-2017
08:25 AM
Re: Accessing a global object inside a SceneGraph Component
Thanks for the follow-up. I think I've tried what you are suggesting, but seem to get the same output. Here is some code to go along with my issue
1) Utility Class - util.brs
I've created a test util.brs as you've suggestion with two sample functions I'd like to use across the app:
2) My SceneGraph Screen - SubscribeScreen.brs
I'm invoking 'util()' in the roSGScreen itself, because it seems that if I attempt to call it in the 'RowListScene' Component Script, then the app crashes with an error:
Invoking util() here avoids that error, which makes me assume I would then need to pass it into the Scene using a GlobalNode.
3) The SceneGraph Component script - RowListScene.brs
In turn, when I print out 'utils' up there, that is where I find:
Directly attempting to call utils.getsomething() or utils.printtest() in this file also results in error:
Does anything stick out as what might be causing the issue? It sounds like you're suggesting I shouldn't need to be using the 'screen.getGlobalNode()' function, is that correct?
Thanks for any help you are able to provide.
-Mikey
1) Utility Class - util.brs
I've created a test util.brs as you've suggestion with two sample functions I'd like to use across the app:
function util()
obj = {
printTest: _printTest
getSomething: _getSomething
}
return obj
end function
function _printTest()
print "_printTest()"
end function
function _getSomething()
print "_getSomething()"
end function
2) My SceneGraph Screen - SubscribeScreen.brs
I'm invoking 'util()' in the roSGScreen itself, because it seems that if I attempt to call it in the 'RowListScene' Component Script, then the app crashes with an error:
Function Call Operator ( ) attempted on non-function. (runtime error &he0) in pkg:/components/RowListScene.brs(5)
005: utils = util()
Invoking util() here avoids that error, which makes me assume I would then need to pass it into the Scene using a GlobalNode.
sub SubscribeScreen()
screen = CreateObject("roSGScreen")
m.port = CreateObject("roMessagePort")
screen.setMessagePort(m.port)
scene = screen.CreateScene("RowListScene")
utils = util()
m.global = screen.getGlobalNode()
m.global.id = "GlobalNode"
m.global.addFields( {utils: utils} )
screen.show()
while(true)
msg = wait(0, m.port)
msgType = type(msg)
if msgType = "roSGScreenEvent"
if msg.isScreenClosed() then return
end if
end while
end sub
3) The SceneGraph Component script - RowListScene.brs
function init()
m.top.backgroundURI = "pkg:/images/background.png"
utils = m.global.utils
' utils = util() -- this causes an error when invoked from this file
print utils
...
end function
In turn, when I print out 'utils' up there, that is where I find:
<Component: roAssociativeArray> =
{
getsomething: invalid
printtest: invalid
}
Directly attempting to call utils.getsomething() or utils.printtest() in this file also results in error:
Member function not found in BrightScript Component or interface. (runtime error &hf4) in pkg:/components/RowListScene.brs(7)
007: utils.getsomething()
Does anything stick out as what might be causing the issue? It sounds like you're suggesting I shouldn't need to be using the 'screen.getGlobalNode()' function, is that correct?
Thanks for any help you are able to provide.
-Mikey
belltown
Roku Guru
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-08-2017
09:55 AM
Re: Accessing a global object inside a SceneGraph Component
You know you can just include the file(s) containing your functions in <script> tags, right?
MyScene.xml :
MyScene.xml :
<?xml version="1.0" encoding="UTF-8"?>
<component name="MyScene" extends="Group">
<script type="text/brightscript" uri="pkg:/components/MyScene.brs" />
<script type="text/brightscript" uri="pkg:/components/HttpUtils.brs" />
<script type="text/brightscript" uri="pkg:/source/ArrayUtils.brs" />
<script type="text/brightscript" uri="pkg:/source/GeneralUtils.brs" />
Motorcykey
Visitor
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-08-2017
10:34 AM
Re: Accessing a global object inside a SceneGraph Component
I didn't! So it sounds like it's best practice that for any SceneGraph Component I create that needs access to my functions from util.brs, I would basically "require" them as a dependency as follows. Makes sense!
<script type="text/brightscript" uri="pkg:/components/util.brs" />
Motorcykey
Visitor
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-08-2017
10:40 AM
Re: Accessing a global object inside a SceneGraph Component
That is exactly what I was missing! Thank you belltown!
Veeta
Visitor
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-08-2017
11:22 AM
Re: Accessing a global object inside a SceneGraph Component
"joetesta" wrote:
rather than trying to include them as methods on the global noid, I believe you can (should?) just define global functions in source and reference them from anywhere.
For example, if you just include a file in your source folder called utils.brs and in there you can have a function;Function myUtils()
this = {
someFunction : _someFunction
getSomething : _getSomething
}
return this
End Function
Within the same file, define the functions '_someFunction' and '_getSomething'
Now you can instantiate myUtils from anywhere ( util = myUtils() ) and pass in variables to the defined methods ( result = util.getSomething(somevars) ).
joetesta,
Your example hit upon nearly the same pattern i use for my brs modules. I don't think it's been formalized or blogged about anywhere but it would be nice if more Roku developers adopted this style. I'll leave in-depth discussion for a different thread.
Motorcykey
Visitor
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-14-2017
12:48 PM
Re: Accessing a global object inside a SceneGraph Component
I'm attempting to use screen.getGlobalNode() to set a value I will need for my SceneGraph Screen. I'm finding that I can't seem to update the value after it's initially set.
1) In SubscribeScreen.brs
This is my main BrightScript Component which creates the scene. In this class, I'm also setting a variable for 'SubscriptionType' using an external class that can't seem to be called from RowListScene.brs
2) RowListScene.brs
Setting the values works when the screen is first called, however, the screen can be opened more than once. For example, logging out and logging back in again.
Current attempts to log back in just freeze the app when hitting the line:
I can't seem to call any roSGNode methods on m.global from SubscribeScreen.brs, however I can inside RowListScene.brs.
One method I thought I could use was calling m.global.Clear() to clear out the values whenever the screen is closed. This does clear the values out, but still doesn't help in updating my 'm.global.userType' value on subsequent opens.
Any suggestions on how I could update a global node value would be very helpful.
Thank you,
-Mikey
1) In SubscribeScreen.brs
This is my main BrightScript Component which creates the scene. In this class, I'm also setting a variable for 'SubscriptionType' using an external class that can't seem to be called from RowListScene.brs
scene = screen.CreateScene("RowListScene")
...
if (m.global = invalid)
m.global = screen.getGlobalNode()
m.global.id = "GlobalNode"
end if
m.global.addFields( {userType: subscriptionType} )
2) RowListScene.brs
function init()
if (m.global.userType = "Monthly")
' code for monthly users
end if
...
Setting the values works when the screen is first called, however, the screen can be opened more than once. For example, logging out and logging back in again.
Current attempts to log back in just freeze the app when hitting the line:
m.global.addFields
I can't seem to call any roSGNode methods on m.global from SubscribeScreen.brs, however I can inside RowListScene.brs.
One method I thought I could use was calling m.global.Clear() to clear out the values whenever the screen is closed. This does clear the values out, but still doesn't help in updating my 'm.global.userType' value on subsequent opens.
Any suggestions on how I could update a global node value would be very helpful.
Thank you,
-Mikey
belltown
Roku Guru
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-14-2017
01:01 PM
Re: Accessing a global object inside a SceneGraph Component
Perhaps some more code would help understand what you're trying to do. I don't see any code where you're setting userType after you've initialized it, and I don't see any code in RowListScene where you're observing the field for changes.
Motorcykey
Visitor
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-14-2017
03:07 PM
Re: Accessing a global object inside a SceneGraph Component
Here's what the original code looked like for setting the userType after it was initialized:
I hadn't thought about adding an observer to RowListScene.brs. But if I do, would the code below pick up changes to when the 'else' statement above has executed:
Thanks for the help!
-Mikey
if (m.global = invalid)
m.global = screen.getGlobalNode()
m.global.id = "GlobalNode"
m.global.addFields( {userType: "Trial"} )
else
m.global.userType = "Monthly"
end if
I hadn't thought about adding an observer to RowListScene.brs. But if I do, would the code below pick up changes to when the 'else' statement above has executed:
m.global.observeField("userType", "userTypeChanged")
function userTypeChanged()
print "USER TYPE HAS CHANGED"
m.userType = m.global.userType
end function
Thanks for the help!
-Mikey