Calling a function in the view from the delegate class

What I have here is a snippet of what I'm trying to do. Within an app, I have the main view class, and an input delegate to catch user input. From within the delagate ("onMenu" in this case, I want to call a function in the view class..

class MyView extends Ui.View {

function myFunct() {
//do something here
}
}

class MyViewDelegate extends Ui.BehaviorDelegate {

function onMenu() {
//I want to call myFunct() in MyView here
//any way to do it????
}
}


Is there any way to do this??
  • I think you might want to do something like below.

    using Toybox.Application as App;
    using Toybox.WatchUi as Ui;
    using Toybox.System as Sys;

    class sandboxApp extends App.AppBase {
    function getInitialView() {
    var view = new sandboxView();
    var delegate = new sandboxDelegate();
    return [ new sandboxView(), new sandboxDelegate(view) ];
    }
    }

    class sandboxView extends Ui.View {
    function myFunct() {
    Sys.println("Doing Something");
    }
    }

    class sandboxDelegate extends Ui.BehaviorDelegate {
    var mView;
    function initialize(view) {
    mView = view;
    }

    function onKey(evt) {
    var key = evt.getKey();
    if (key == Ui.KEY_DOWN) {
    mView.myFunct();
    }
    }
    }
  • I think you might want to do something like below.

    using Toybox.Application as App;
    using Toybox.WatchUi as Ui;
    using Toybox.System as Sys;

    class sandboxApp extends App.AppBase {
    function getInitialView() {
    var view = new sandboxView();
    var delegate = new sandboxDelegate();
    return [ new sandboxView(), new sandboxDelegate(view) ];
    }
    }

    class sandboxView extends Ui.View {
    function myFunct() {
    Sys.println("Doing Something");
    }
    }

    class sandboxDelegate extends Ui.BehaviorDelegate {
    var mView;
    function initialize(view) {
    mView = view;
    }

    function onKey(evt) {
    var key = evt.getKey();
    if (key == Ui.KEY_DOWN) {
    mView.myFunct();
    }
    }
    }


    I ran into this problem a while ago and eventually decided that if I was calling view methods then I was 'doing-it-wrong'. My personal best practice is that View's only implements standard 'onLayout', 'onDraw' hooks and private utility methods.

    Anything that needs to be called externally from different parts of the app either resides in the main App class, or a class that hangs directly off of it (to avoid the God-object problem).

    This has worked out well. It means that all I have to do, from anywhere in the App is something like, "Application.getApp().getFooManager().doFooLikeThing()".
  • I found a simple way to do what I wanted, and some may consider it a hack, but it works.

    In the delegate, I set a global boolean, and then call Ui_requestUpdate()

    in onUpdate(), I check the boolean, and make the call if needed. It's something that will rarely happen, and means one extra call to onUpdate() (which now only gets called every few minutes)
  • The ConnectIQ Delegate/View design really looks like the start of a Model/View/Controller pattern (where the delegate is the controller, and you have to write your application specific logic into your own model class). For simple apps it can be fine to smoosh the pieces together, but as your application gets more complicated, it is nice to separate things.
  • Former Member
    Former Member over 9 years ago
    I have also come across this problem, and I have implemented a global callback object method/function in the main view as my solution. I am not sure whether this is the best way to go about it, but things start to get very messy with lots of views and delegates. Here are some snippets of code, I welcome any feedback as to using this method.


    // global variable
    var callback;

    // defines
    const CALLBACK_COMMAND_PRESS_TO_START_CONFIRM = 1;
    const CALLBACK_COMMAND_PRESS_TO_START_CANCEL = 2;


    class WorkoutView extends Ui.View {

    function initialize() {
    callback = method(:onCallback);
    }

    function onCallback(callback_id) {

    if ( callback_id == CALLBACK_COMMAND_PRESS_TO_START_CONFIRM ) {
    // do something here, user pressed START button
    }

    else if ( callback_id == CALLBACK_COMMAND_PRESS_TO_START_CANCEL ) {
    // do something else here, user has pressed CANCEL
    }
    }

    // other functions for view edited out
    }


    class PressToStartInputDelegate extends Ui.InputDelegate
    {
    function onKey(evt) {
    if ( evt.getKey() == evt.KEY_ENTER ) {
    callback.invoke(CALLBACK_COMMAND_PRESS_TO_START_CONFIRM);
    }
    if ( evt.getKey() == evt.KEY_ESC ) {
    callback.invoke(CALLBACK_COMMAND_PRESS_TO_START_CANCEL);
    }
    }





    Doing it this way you could also pass the callback method into other delegates and views through the initialize function, although, I am still only experimenting with the above way of implementing things, so I have not gone that far into it as yet.

    What do you guys think ? Is this an ok way of trying to make things "neat" ?
  • I have a similar need, just opposite direction.

    My View class uses data fetched by a webRequest. It's 2 pieces of data, one the updated once a day and another that's even rarer, so I put them in .storage to aviod requesting them on each view. API is slow, so even in sim it takes several seconds with token ping pong and auth etc...

    Anyways. When I do the .storage.getvalues() I check the values, and if they are not there or up to date, I'd like to call the requestData() in the delegate class.

    Is there a something like the UI_requestUpdate for the delegate?? 

    How do we do this in 2023?? I suspect 8 year of "hacking" has tough Jim a trick or two :-)  

    PS: I read this also, but seems like this is also a bit of a discussion on how to....

  • Is there a something like the UI_requestUpdate for the delegate?? 

    No. WatchUi.requestUpdate() exists because you can't just call View.onUpdate() directly (e.g. when a timer expires or in response to user input) because you wouldn't have a dc to pass to to it, and even if you did, onUpdate probably wouldn't be able to update the screen, since it's not designed to be called directly. It seems that the purpose of requestUpdate() is to allow the system to update the screen on its own schedule. (For example, if you call requestUpdate() from a glance on a device which doesn't support live glances, nothing happens.)

    If there was a requestUpdate() analog for delegates, what would it look like? The only thing I can think of would be to ask the framework to simulate a button press or touch event. While there may be one or two obscure use cases where something like that would actually be useful (allowing the dev to do something which otherwise wouldn't be possible), unfortunately that functionality doesn't exist. Fortunately, there's nothing stopping you from calling any code you want, in your own delegate.

    If you have some sort of custom functionality in your BehaviorDelegate()/InputDelegate(), such as making a webRequest, can't you encapsulate it in a public function (e.g. "requestData()"), pass a delegate reference to the view that needs it, and have the view call the requestData() function on demand?

    Also, why is your data request functionality in the delegate in the first place? Do you also request data in response to a button press?

    Another alternative would be to implement some sort of dedicated communications/storage class, and pass around an instance to all views and delegates which need it.

    Yet another alternative would be to put all the communications/storage stuff in the application class, so any other class which needs that functionality can call Application.getApp(), cast the result to the correct class, and call the function it needs.

  • Also, why is your data request functionality in the delegate in the first place?

    Well. I started out with the webRequest sample code and it has it like that. It needs to be a delegate, to intercept the http response, right?

    But to be totally honest, probably because I don't fully understand what I 'm doing.... :-) I mean, I understand classes, object and to a certain extend delegates, but how it all ties together top level to form the app structure, is still somewhat a blur to me... Including the app lifecycle.

    So your statement below make my brain hurt....  

    pass a delegate reference to the view that needs it,
  • Ok, this is a really old thread from back when I was just starting with CIQ.  The way I do it today (having a delegate interact with the view is this.  In AppBase, in the getInitialView function, I do this:

        function getInitialView() as Array<Views or InputDelegates>? {
            view= new ComplexView();
            return [view,new ComplexDeligate(view)] as Array<Views or InputDelegates>;
        }

    Then in the delegate, I have this:

    class ComplexDeligate extends WatchUi.WatchFaceDelegate
    {
    	var view;
    	
    	function initialize(v) {
    		WatchFaceDelegate.initialize();
    		view=v;	
    	}
    
        function onMenu() {
            //call the function in the view
            view.doViewStuff();
            
            /or
            view.bool=!view.boolean;
            WatchUi.requestUpdate();
            // to toggle a boolean in the view and cause the display to be updated
            
            // or
            //build a menu and pass the view to the menu delegate, so it can also
            //access the view
            WatchUi.pushView(myMenu, new ComplexMenuDelegate(view), WatchUi.SLIDE_IMMEDIATE);	
            return true;
        }
    }

  • Well. I started out with the webRequest sample code and it has it like that. It needs to be a delegate, to intercept the http response, right?

    In that sample, the code that calls makeWebRequest() is in the WebRequestDelegate/BehaviorDelegate class (in the function named makeRequest()) simply because it's the press of a button that triggers the request, and that's the easiest implementation.

    In Connect IQ, the purpose of delegate classes is to receive events from the system. The type of delegate class (InputDelegates) that's returned by AppBase.getInitialView() is for handling input events.

    The important thing to understand about the makeRequest() function in that sample's WebRequestDelegate class is that it's not a standard part of Connect IQ, it's just a custom piece of code specific that sample. There's no reason it couldn't have been called something else. There's also no reason it couldn't have been placed somewhere else (like the view or the app class), except that would've made it harder for the delegate code to call it when the user presses the appropriate button.

    So your statement below make my brain hurt....  

    pass a delegate reference to the view that needs it,

    Like in Jim's comment, except that his code is an example of passing a view reference to a delegate, but for your exact purpose, it's the other way around.

    In the context of the WebRequest sample, it's not really possible to pass a delegate reference to the view in the view's constructor, because the delegate is already receiving a reference to the onReceive() method of the view, so the view has to be created before the delegate. There are other ways to give the view access to code in the delegate, but maybe this is getting a bit too complicated.

    On the other hand, you didn't say that your app's web request actually needs to be triggered by user input.

    So the easiest thing to do would be to move "makeRequest()" (or whatever function you have that requests data) to the view class, if it's the view class that needs to call it.