trigger when router page becomes active

Hi,

if i have a pager i can easily place a trigger when the page is navigated to (in the example p1Active).

but:

is it possible to place it somewhere INSIDE the navigator page ? I want it to be called when the user navigates to that page for the first time.

<App>
    <JavaScript>
        function p1Active(){
            console.log("p1 active!");
        }
        function p2Active(){
            console.log("p2 active!");
        }        
        module.exports = {
            p1Active: p1Active,
            p2Active: p2Active,

        }
    </JavaScript>

    <DockPanel>

        <Page ux:Class="CustomPage">
            <Router ux:Dependency="router" />
            <Text Value="CustomPage"/>
        </Page>
        <Page ux:Class="CustomPage2">
            <Router ux:Dependency="router" />
            <Text Value="CustomPage2"/>
        </Page>

        <PageControl ux:Name="pager">
            <Page ux:Name="page1">
                <Activated>
                    <Callback Handler="{p1Active}"/>
                </Activated>
                <Text Value="page1"/>
            </Page>
            
            <Page ux:Name="page2">
                <!-- I can place an activated here -->
                <!-- but i want to know if it's possible -->
                <!-- to have it INSIDE the navigator -->
                
                <Router ux:Name="router" BackButtonAction="None" IsMasterRouter="false"/>
                <Navigator DefaultPath="page2c1">
                    <CustomPage Name="page2c1" router="router">
                        <!-- What should I have here ?? -->
                        <Activated>
                            <Callback Handler="{p2Active}"/>
                        </Activated>
                    </CustomPage>  
                    <CustomPage2 ux:Template="page2c2" router="router">
                    </CustomPage2>  
                </Navigator>
            </Page>

        </PageControl>

        <Grid Dock="Bottom" ColumnCount="2" Height="50">
            <Panel HitTestMode="LocalBoundsAndChildren" Background="#ff00aa">
                <Text Value="Go to Page1" TextColor="#fff" Alignment="VerticalCenter" TextAlignment="Center"/>
                <Clicked>
                    <Set pager.Active="page1"/>
                </Clicked>
            </Panel>
            <Panel HitTestMode="LocalBoundsAndChildren" Background="#aa00ff">
                <Text Value="Go to Page2" TextColor="#fff" Alignment="VerticalCenter" TextAlignment="Center"/>
                <Clicked>
                    <Set pager.Active="page2"/>
                </Clicked>
            </Panel>
        </Grid>
    </DockPanel>
</App>

EDIT: I wanted to move it to “How-to Discussions” but i don’t know how to move it once is posted

Hi zaulin,

in theory, you can put the Activated trigger in a page that is inside of a Navigator just fine. In some rare cases, you might need to add a Bypass="Never" property on it.

Have you tried the code you posted? Did it work or not?

And what are you willing to do in that second-level Callback? Maybe there’s a better way to achieve the goal.

yes I have tried the code and the “activated” is called when the class is instanced.

In my app I have 2 tabs:
Home --> with it’s own navigator/router so u can navigate to a person profile (for example)
Search nearby --> also with own navigator/router that search places nearby

the first time you click on the search tab it should search for the places near you.

Now i’m calling the “first search” function on the “tab click” but i get an error (actually i just get the error on the monitor but it works) so that’s why I ask if there is a way to trigger something when showing that page.

I don’t know if it’s clear enough :slight_smile:

Not very clear, no :slight_smile:

If you want to do something only once, the you have to store somewhere the fact that you have done this thing when you have done it. This might be a boolean variable in a stand-alone JavaScript module, for example.

Then, when you run the function X every time you land on the page, you can check the value of that boolean, launch search and update the boolean as necessary.

Now, if you’re saying there is an error, then you should report that error in a separate thread along with a self-contained reproduction, so we could check and hopefully solve it for you.

Hi,

my problem is NOT that i don’t know how to do something once :slight_smile: I just ask if there is a way of calling a JS function INSIDE the CustomPage when this page is shown.

I’ll post a more complex example as i don’t think the error i get it’s a bug

In this example there is a Pager (ux:Name=“pager”) that contains 2 pages:

ux:Name=“page1”: just text

ux:Name=“page2”: It contains a router/navigator with 2 pages:

  • CustomPage

  • CustomPage2

On the bottom there is a panel that allows you to navigate the 2 main pages.

I want that as soon as CustomPage is shown it navigates to CustomPage2. In this example I run this code once you click on the bottom bar so you get an error as when you call “runCodeInside() function on customPage.js” router is not defined (it makes sense).

So my question is:

following this example is there a way of being able to navigate from CustomPage to CustomPage2 (make runCodeInside() working) as soon as CustomPage is shown?

MainView.ux:

<App>

    <JavaScript>
        var CP = require("customPage.js");

        function p1Active(){
            console.log("p1 active!");
        }
        function runCode(){
            console.log("runCode!");
            CP.runCodeInside();
        }        
        module.exports = {
            p1Active: p1Active,
            runCode: runCode,

        }
    </JavaScript>

    <DockPanel>

        <Page ux:Class="CustomPage">
            <JavaScript File="customPage.js"/>
            <Router ux:Dependency="router" />

            <StackPanel>
                <Text Value="CustomPage"/>

                <Button Text="Click Me!" Margin="0,20,0,0">
                    <Clicked Handler="{runCodeInside}"/>
                </Button>
            </StackPanel>
        </Page>

        <Page ux:Class="CustomPage2">
            <Router ux:Dependency="router" />
            <JavaScript>
                function goBack(){
                    router.goBack();
                }
                module.exports = {
                    goBack: goBack
                }
            </JavaScript>
            <StackPanel>
                <Text Value="CustomPage2"/>
                
                <Button Text="Go Back!" Margin="0,20,0,0">
                    <Clicked Handler="{goBack}"/>
                </Button>
            </StackPanel>
        </Page>

        <PageControl ux:Name="pager">
            <Page ux:Name="page1">
                <Activated>
                    <Callback Handler="{p1Active}"/>
                </Activated>
                <Text Value="page1"/>
            </Page>
            
            <Page ux:Name="page2">

                <Router ux:Name="router" BackButtonAction="None" IsMasterRouter="false"/>
                <Navigator DefaultPath="page2c1">
                    <CustomPage Name="page2c1" router="router"/>

                    <CustomPage2 ux:Template="page2c2" router="router"/>
                </Navigator>
            </Page>

        </PageControl>

        <Grid Dock="Bottom" ColumnCount="2" Height="50">
            <Panel HitTestMode="LocalBoundsAndChildren" Background="#ff00aa">
                <Text Value="Go to Page1" TextColor="#fff" Alignment="VerticalCenter" TextAlignment="Center"/>
                <Clicked>
                    <Set pager.Active="page1"/>
                </Clicked>
            </Panel>
            <Panel HitTestMode="LocalBoundsAndChildren" Background="#aa00ff">
                <Text Value="Go to Page2" TextColor="#fff" Alignment="VerticalCenter" TextAlignment="Center"/>
                <Clicked>
                    <Set pager.Active="page2"/>
                    <Callback Handler="{runCode}"/>
                </Clicked>
            </Panel>
        </Grid>
    </DockPanel>
</App>

customPage.js:

function runCodeInside(){
	console.log("run code inside");

	router.push("page2c2");
}

module.exports = {
	runCodeInside: runCodeInside
}

Hey zaulin,

the direct answer to your question is there a way to...? is YES. However, there are a couple things.

  1. You’re using router wrong. In most cases, you only need one, put at the root of your app, and definitely NOT with IsMasterRouter="false".
  2. You’re using a single JS file both as a viewmodel: <JavaScript File="customPage.js"/> and as a stand-alone module: var CP = require("customPage.js");. You MAY NOT do that; it’s either one or the other.
  3. Even though I spent a lot of time trying to understand what it is you’re trying to achieve, I still don’t see how this might be useful. Mostly because it doesn’t make sense to “immediately redirect to another page” - then why have that first page there in the first place?!

I would suggest you to rethink your approach, formulate a good question and maybe start with a real-world description of what it is you want to make. Maybe then we could get somewhere, because this direction clearly won’t lead us anywhere.

Finally, here’s some code that I could jumble together as a proof-of-concept:

<App>
    <Router ux:Name="router" />
    <JavaScript>
    module.exports = {
    	gotoPageOne: function() {
    		router.goto("page1", {});
    	},
    	gotoPageTwo: function() {
    		// we have to pass full path to first subpage, because
    		// we might have navigated to the second subpage already
    		router.goto("page2", {}, "page2c1", {});
    	}
    };
    </JavaScript>
    <DockPanel>
        <PageControl>
            <Page ux:Name="page1">
                <Text Value="page1"/>
            </Page>
            <Page ux:Name="page2">
                <Navigator ux:Name="subNav" DefaultPath="page2c1">
                    <CustomPage ux:Template="page2c1" router="router" subPages="subNav" />
                    <CustomPage2 ux:Template="page2c2" router="router"/>
                </Navigator>
            </Page>
        </PageControl>
        <Grid Dock="Bottom" ColumnCount="2" Height="50">
            <Panel HitTestMode="LocalBoundsAndChildren" Background="#ff00aa">
                <Text Value="Go to Page1" TextColor="#fff" Alignment="VerticalCenter" TextAlignment="Center"/>
                <Clicked>
                    <Callback Handler="{gotoPageOne}" />
                </Clicked>
            </Panel>
            <Panel HitTestMode="LocalBoundsAndChildren" Background="#aa00ff">
                <Text Value="Go to Page2" TextColor="#fff" Alignment="VerticalCenter" TextAlignment="Center"/>
                <Clicked>
                    <Callback Handler="{gotoPageTwo}" />
                </Clicked>
            </Panel>
        </Grid>
    </DockPanel>

    <Page ux:Class="CustomPage">
        <Router ux:Dependency="router" />
        <Navigator ux:Dependency="subPages" />
        <JavaScript>
		module.exports = {
		    gotoCustomPageTwo: function() {
		    	// whenever we hit the Activated, proceed to second subpage
		    	router.pushRelative(subPages, "page2c2", {});
		    }
		}
        </JavaScript>
        <Activated>
        	<Callback Handler="{gotoCustomPageTwo}" />
        </Activated>
        <Text Value="Custom Page One"/>
    </Page>

    <Page ux:Class="CustomPage2">
        <Router ux:Dependency="router" />
        <JavaScript>
            module.exports = {
                goBack: function() {
                	// and this makes no sense, because we will immediately land
                	// back on the second subpage because of that Activated
                	router.goBack();
                }
            }
        </JavaScript>
        <StackPanel>
            <Text Value="CustomPage2"/>
            <Button Text="Go Back!" Margin="0,20,0,0">
                <Clicked Handler="{goBack}"/>
            </Button>
        </StackPanel>
    </Page>

</App>

I think i was not clear at all so sorry :frowning:

first some notes:

  • I dont’ want to navigate from one to another it was just one example as said in the first post i just want to be able to run some code when the page is shown. “hello world” would be fine too but i used the “navigation” example as you can’t call it from outside the customPage.js
  • I use IsMasterRouter=“false” as in my original app i have more than one router and there is more stuff but that’s not relevant here

So pls forget about all the stuff before and i’ll try to be very clear:

let’s say I have this code:

<App>
    <Router ux:Name="router"/>
    <JavaScript>
        function p1Active(){
            console.log("p1 on the screen!");
        }

        function p2Active(){
            console.log("p2 on the screen!");
        }

        module.exports = {
            p1Active: p1Active,
            p2Active: p2Active

        }
    </JavaScript>

    <DockPanel>

        <PageControl ux:Name="pager">
            
            <Page ux:Name="page1">
                <Text Value="page1"/>
            </Page>
            
            <Page ux:Name="page2">
                <Text Value="page2"/>
            </Page>

        </PageControl>

        <Grid Dock="Bottom" ColumnCount="2" Height="50">
            <Panel HitTestMode="LocalBoundsAndChildren" Background="#ff00aa">
                <Text Value="Go to Page1" TextColor="#fff" Alignment="VerticalCenter" TextAlignment="Center"/>
                <Clicked>
                    <Set pager.Active="page1"/>
                </Clicked>
            </Panel>
            <Panel HitTestMode="LocalBoundsAndChildren" Background="#aa00ff">
                <Text Value="Go to Page2" TextColor="#fff" Alignment="VerticalCenter" TextAlignment="Center"/>
                <Clicked>
                    <Set pager.Active="page2"/>
                </Clicked>
            </Panel>
        </Grid>
    </DockPanel>
</App>

if i ask you:

  • Is there a way to execute the p1Active and p2Active functions each time page1 and page2 are shown on the screen?

you would say: “YES just add this code inside the Page tags”

<Activated>
    <Callback Handler="{p1Active}"/>
</Activated>

and i would be very happy to see that it works and i would send you a pink ferrari.

now let’s go to my real problem:

I will add a navigator with 2 pages inside page2:

<App>
    <Router ux:Name="router"/>
    <JavaScript>
        function p1Active(){
            console.log("p1 on the screen!");
        }

        function p2Active(){
            console.log("p2 on the screen!");
        }

        module.exports = {
            p1Active: p1Active,
            p2Active: p2Active

        }
    </JavaScript>

    <DockPanel>

        <Page ux:Class="CustomPage">
            <JavaScript>
                function pageOnActivated(){
                    console.log("CustomPage on the screen!");
                }

                module.exports = {
                    pageOnActivated: pageOnActivated
                }
            </JavaScript>
            <Router ux:Dependency="router" />

            <Activated>
                <Callback Handler="{pageOnActivated}"/>
            </Activated>

            <StackPanel>
                <Text Value="CustomPage"/>  
            </StackPanel>
        </Page>

        <Page ux:Class="CustomPage2">
            <Router ux:Dependency="router" />
            <JavaScript>
                function goBack(){
                    router.goBack();
                }
                module.exports = {
                    goBack: goBack
                }
            </JavaScript>
            <StackPanel>
                <Text Value="CustomPage2"/>
                
                <Button Text="Go Back!" Margin="0,20,0,0">
                    <Clicked Handler="{goBack}"/>
                </Button>
            </StackPanel>
        </Page>

        <PageControl ux:Name="pager">
            <Page ux:Name="page1">
                <Activated>
                    <Callback Handler="{p1Active}"/>
                </Activated>
                <Text Value="page1"/>
            </Page>
            
            <Page ux:Name="page2">
                <Activated>
                    <Callback Handler="{p2Active}"/>
                </Activated>

                
                <Navigator DefaultPath="page2c1">
                    <CustomPage Name="page2c1" router="router"/>

                    <CustomPage2 ux:Template="page2c2" router="router"/>
                </Navigator>
            </Page>

        </PageControl>

        <Grid Dock="Bottom" ColumnCount="2" Height="50">
            <Panel HitTestMode="LocalBoundsAndChildren" Background="#ff00aa">
                <Text Value="Go to Page1" TextColor="#fff" Alignment="VerticalCenter" TextAlignment="Center"/>
                <Clicked>
                    <Set pager.Active="page1"/>
                </Clicked>
            </Panel>
            <Panel HitTestMode="LocalBoundsAndChildren" Background="#aa00ff">
                <Text Value="Go to Page2" TextColor="#fff" Alignment="VerticalCenter" TextAlignment="Center"/>
                <Clicked>
                    <Set pager.Active="page2"/>
                </Clicked>
            </Panel>
        </Grid>
    </DockPanel>
</App>

my ONLY question is:

  • I want pageOnActivated function inside CustomPage to run each time CustomPage is shown on the screen (exactly the same way that the p1Active and p2Active functions are run when page1 and page 2 are shown on the screen)

If i use the Activated tag it just run on app start and i would like it to be executed each time it appears on the screen (when i navigate to page2 and when i click on “go Back” from CustomPage2)

Right, okay. A tiny little bit better now :slight_smile:

I think that the main problem with this structure is the following. Inside your PageControl, the pages are instances (not templates as in a Navigator), so they have to be created at start. That explains why the pageOnActivated() is called during app startup - its parent page gets created, and on that page the Navigator has to spawn the child mentioned in DefaultPath. So it gets Activated.

Now, when you later navigate to page2 on the first level, the first subpage in Navigator is already active, so it doesn’t get Activated again.

A workaround might be to navigate to full path, e.g. router.goto("page2", {}, "subpage1", {}); but that remains to be tested.

Also, I believe some changes to Activated triggers are expected to ship in the coming weeks, but there’s no more specific details available at this point. We’ll keep you posted.

yep… i knew that happened as i was working with instances (that’s why i told you i don’t think that’s a bug)

so i’ll wait for the

  <OnScreen>

trigger :slight_smile:

I just found a workaround. Adding

                <WhileVisible>
                    <Callback Handler="{pageOnActivated}"/>
                </WhileVisible>

to:

        <Page ux:Class="CustomPage">
            <JavaScript>
                function pageOnActivated(){
                    console.log("CustomPage on the screen!");
                }

                module.exports = {
                    pageOnActivated: pageOnActivated
                }
            </JavaScript>
            <Router ux:Dependency="router" />

            <WhileVisible>
                 <Callback Handler="{pageOnActivated}"/>
            </WhileVisible>

            <StackPanel>
                <Text Value="CustomPage"/> 
            </StackPanel>
        </Page>

it works :slight_smile: