Forum Stats

  • 3,876,211 Users
  • 2,267,082 Discussions
  • 7,912,470 Comments

Discussions

How to navigate to navigate to another jsff page and then execute a method

User_Q9C3J
User_Q9C3J Member Posts: 21 Green Ribbon

Hello, my JDeveloper version is Studio Edition Version 12.2.1.4.0 and I am relatively new to all of this, so please bear with me.

Basically, I want to navigate from one .jsff view to another .jsff view and then execute a function from a managed bean immediately.

My setup: on the first .jsff view there is an af:table with a certain column containing links. If I click on a link in one row, I want to navigate to another .jsff view, open up a specific af:showDetailItem in an af:panelTabbed, and select a certain row in the af:table inside this af:showDetailItem that corresponds to the row I clicked in the first .jsff view.

I know it is built into the task flow framework to allow a method call and then a switch to a view, but I need the other way around because this method needs to be able to interact with the components on the second page (opening a tab and selecting a row in the table).

I looked around a lot online and a lot of people reference https://blogs.oracle.com/aramamoo/entry/an_epic_question_how_to in response to similar questions, but it seems this article is no longer active or I do not have access to it. I also have read it is possible to navigate to a view and then have an invisible field in the UI have a 'rendered' property that will call a function in the bean and you can execute your functionality there. I was wondering if there is a more standard way to solve this that is more built into the framework. If I am also going about this the wrong way, please let me know.

If anyone has encountered something similar, any help would be appreciated.

--Scott M.

Best Answer

  • User_Q9C3J
    User_Q9C3J Member Posts: 21 Green Ribbon
    Answer ✓

    I solved this.

    Basically I think the problem was that I was calling setCurrentRow() on the wrong instance of the view object. I accessed the right instance of the view object by doing this:

        BindingContext bc = BindingContext.getCurrent();

        DCBindingContainer container = bc.findBindingContainer("<page_def>");

        DCIteratorBinding it = container.findIteratorBinding("<iterator>");

    ViewObjectImpl vo = it.getViewObject();

    Change this to reflect your desired view object.

    I think the reason this wasn't working for me was that the iterator I was looking for wasn't in my getCurrentBindingsEntry() since it was on the page I was navigating to, so I had to manually look up the right binding container.

«1

Answers

  • dvohra21
    dvohra21 Member Posts: 14,692 Gold Crown

    Navigation is only for navigation between pages. To subsequently call a method, is not included in navigation, and some automation may need to be performed. If the method call is for unit testing, the automation library may be used.

    https://www.oracle.com/corporate/accessibility/templates/t2-10649.html

  • User_Q9C3J
    User_Q9C3J Member Posts: 21 Green Ribbon

    The method call is not for testing. It is to display certain things on the page.

  • dvohra21
    dvohra21 Member Posts: 14,692 Gold Crown

    Call managed bean method on page load. Several blogs on the subject such as "Call method on page load of JSFF (JSF fragment) in Oracle ADF" Also some code snippets:

    JSF 1.2 / 2.x

    Use @PostConstruct annotated method on a request or view scoped bean. It will be executed after construction and initialization/setting of all managed properties and injected dependencies.

    @PostConstruct
    public void init() {
        // Do your stuff here.
    }
    

    This is strongly recommended over constructor in case you're using a bean management framework which uses proxies, such as CDI, because the constructor may not be called at the times you'd expect it.


    JSF 2.0 / 2.1

    Use <f:event type="preRenderView"> in case you intend to initialize based on <f:viewParam> too, or when the bean is put in a broader scope than the view scope (which in turn indicates a design problem, but that aside). Otherwise, a @PostConstruct is perfectly fine too.

    <f:metadata>
        <f:viewParam name="foo" value="#{bean.foo}" />
        <f:event type="preRenderView" listener="#{bean.onload}" />
    </f:metadata>
    public void onload() { 
        // Do your stuff here.
    }
    

    JSF 2.2+

    Alternatively, use <f:viewAction> in case you intend to initialize based on <f:viewParam> too, or when the bean is put in a broader scope than the view scope (which in turn indicates a design problem, but that aside). Otherwise, a @PostConstruct is perfectly fine too.

    <f:metadata>
        <f:viewParam name="foo" value="#{bean.foo}" />
        <f:viewAction action="#{bean.onload}" />
    </f:metadata>
    public void onload() { 
        // Do your stuff here.
    }
    

    Note that this can return a String navigation case if necessary. It will be interpreted as a redirect (so you do not need a ?faces-redirect=true here).

    public String onload() { 
        // Do your stuff here.
        // ...
        return "some.xhtml";
    }
    


  • Timo Hahn
    Timo Hahn Senior Principal Technical Consultant - Oracle ACE Director Member, Moderator Posts: 38,931 Red Diamond

    @dvohra21 When you copy stuff from someone else, you should at least mention that you copied the stuff from https://stackoverflow.com/questions/2451154/invoke-jsf-managed-bean-action-on-page-load

    @User_Q9C3J My understanding is that you don't need to call a method to initialize at all. Both fragments share the same data control. This means that if you make a row the current row on the first fragment, it is the current row on the second fragment too. If the fragments are part of a different bounded task flow, you might need to reset the current row, but this is another question.

    The second part is how to disclose the right tab. You can use an EL like

    #{pageflowScope.mytabbean.index eq 1}
    

    for the first tab, and

    #{pageflowScope.mytabbean.index eq 2}
    

    for the second tab, and likewise for the other tasks. This will allow you to use an index to disclose a specific showDetailItem. From the action you use to 'navigate' to the other jsff, you set the index in the bean. As the bean is in pageflowScope, both jsff fragments can access the data. The final part is to implement a disclose listener on each of the showDetailItem that will set the index in the pageflowScope bean when the user clicks on another tab. This disclosureListener should be implemented in a request scope bean, not in the same pageflowScope bean used for the index! The request scope bean can access the index in the pageflowScope bean (UI-Manager pattern: https://blogs.oracle.com/groundside/post/the-uimanager-pattern)


    Timo

  • User_Q9C3J
    User_Q9C3J Member Posts: 21 Green Ribbon

    Thank you Timo.

    However, I don't think both fragments share the same data control. Sorry if I wasn't clear about this. The second table which I would like to select a certain row is not the same data as in the first table. The first table has a column which is related to a column in the second table. So, I need to somehow select a certain row in the second table based on the selection in the first table, after the second fragment has been rendered (I believe).

  • Timo Hahn
    Timo Hahn Senior Principal Technical Consultant - Oracle ACE Director Member, Moderator Posts: 38,931 Red Diamond

    Are bore jsff part of the same page flow?

    If yes, then they share the same data control. When the user triggers the action to go to the other part, you can do the selection in the other iterator (not the table as it is not there at this point) and show the other jsff. The table should pick up the selected row and use it,


    Timo

  • User_Q9C3J
    User_Q9C3J Member Posts: 21 Green Ribbon

        BindingContext bc = BindingContext.getCurrent();

        DCBindingContainer container = bc.findBindingContainer("accountPageDef");

    DCIteratorBinding it = ((DCIteratorBinding)(container.findCtrlBinding("accountVO1").getDCIteratorBinding()));

        RichCommandLink cl = (RichCommandLink)actionEvent.getSource();

        String id = cl.getText();

    it.getCurrentRow(); // this is null!

        Key key = new Key(new Object[] {id});

    it.setCurrentRowWithKeyValue(key.toStringFormat(true));


    I have tried different iterations of this above code to try and set the current row of the iterator, and it doesn't seems to work. I'm guessing I'm doing this wrong. I get the right iterator when I print out 'it', but when I print out the current row it is null. When I try to set the current row and then print out the current row, it is still null. I don't know if I am not creating the key correctly? What do you think.

  • Timo Hahn
    Timo Hahn Senior Principal Technical Consultant - Oracle ACE Director Member, Moderator Posts: 38,931 Red Diamond

    Can you provide a reproducible test case built on the HR DB schema?


    Timo

  • dvohra21
    dvohra21 Member Posts: 14,692 Gold Crown

    "...I am relatively new to all of this.."

    How are the .jsff page fragments used? When you build a JSF page using page fragments, the page can use one or more page fragments that define different portions of the page. The DataControl for the JSF page could be the same.

  • User_Q9C3J
    User_Q9C3J Member Posts: 21 Green Ribbon

    When the second page opens and the specific tab opens, there is a table that gets rendered. That table is bound to a view object. The view object only gets queried once the tab is opened, so therefore I would need to wait until this happens to select a row in the rowset. So it is not the same data control I believe. My project actually doesn't use .jsf files, just .jsff and .jspx