AppGyver Supersonic navigating between Views and creating duplicates

1k views Asked by At

I'm using AppGyver Steroids and Supersonic to build an app and I'm having some issues navigating between views programmatically.

Based on the docs, you navigate between views like this:

var view_obj = new supersonic.ui.View("main#index");
supersonic.ui.layers.push(view_obj);

However, when I inspect things via the Chrome DevTools, it appears that a second duplicate view is created i.e. If I navigate away from the index page and then navigate back, I now have two index pages, instead of what [I think] should be one. It also doesn't close the previous view I was on.

How can I prevent this from happening and simply move to the existing view, instead of duplicating views? How do I close a view after I have navigated away from it?

Thanks.

3

There are 3 answers

7
benadamstyles On BEST ANSWER

The problem you're encountering is that you're creating a new supersonic.ui.View("main#index") every time you navigate. On top of this, I think you want to return to the same view when you navigate back to a view for the second time, i.e. you want the view to remain in memory even if it has been removed from the navigation stack with pop() (rather than pushing a new instance of that view). For this, you need to preload or "start()" the view, as described in the docs here.

I implemented my own helper function to make this easier; here is my code:

start = function(dest, isModal) {
  var viewId=dest,
      view=new supersonic.ui.View({
        location: dest,
        id: viewId
      });
  view.isStarted().then(function(started) {
    if (started) {
      if (isModal) {supersonic.ui.modal.show(view);}
      else {supersonic.ui.layers.push(view);}
    } else {
      // Start Spinner
      supersonic.ui.views.start(view).then(function() {
        if (isModal) {supersonic.ui.modal.show(view);}
        else {supersonic.ui.layers.push(view);}
        // Stop Spinner
      }, function(error) {
        // Stop Spinner
        A.error(error);
      });
    }
  });
};

Use it like start('module#view');. As a bonus, you can pass true as the second argument and it gets pushed as a modal instead.

It checks if you've already started a view - if so, it just pushes that view back onto the stack. If not, it start()s (i.e. preloads) it, then pushes it. This ensures that the view stays in memory (with any user input that has been modified) even when you pop() it from the stack.

You have to imagine that the layer stack is actually a stack in the Computer Science sense. You can only add and remove views at the top of the stack. The consequence of this is that complex navigations such as A > B > C > D > B are difficult/hacky to do (in this case, you'd have to pop() D and C in succession to get back to B).

Views will close if you pop() them, as long as you didn't start() them. If you did, and you pop() them, they remain in memory. To kill that view, you have to call stop() on it, as described in the docs I linked above.

1
Ilya On

try

var view_obj = new supersonic.ui.View("main#index");
supersonic.ui.layers.replace(view_obj);

And take a look at supersonic.ui.layers.pop();

0
ObiHill On

Thanks to LeedsEbooks for helping me get my head around this challenge. I was able to find a solution. Here is the code:

  var start = function(route_str, isModal) {

      var regex = /(.*?)#(.*)/g;
      var match_obj = regex.exec(route_str);

      var view_id_str = match_obj[2],
          view_location_str = route_str,
          view = new supersonic.ui.View({
              location: view_location_str,
              id: view_id_str
          });

      view.isStarted().then(function(started) {
          if (started)
          {
              if (isModal)
              {
                  supersonic.ui.modal.show(view);
              }
              else {
                  supersonic.ui.layers.push(view);
              }
          }
          else
          {
              // Start Spinner
              supersonic.ui.views.start(view).then(function() {
                  if (isModal)
                  {
                      supersonic.ui.modal.show(view);
                  }
                  else
                  {
                      supersonic.ui.layers.push(view);
                  }
                  // Stop Spinner
              }, function(error) {
                  // Stop Spinner
                  A.error(error);
              });
          }
      });
  };

You must ensure that your route has the format module#view as defined in the documentation.

PLEASE NOTE

There seems to some problem with the supersonic ui method for starting views. If you run the following code:

supersonic.ui.views.start("myapp#first-view");
supersonic.ui.views.find("first-view").then( function(startedView) {
    console.log(startedView);
});

You'll notice that your view id and location are identical. This seems to be wrong as the id should be first-view and location should be myapp#first-view.

So I decided to not use the AppGyver methods and create my own preload method instead, which I run from the controller attached to my home view (this ensures that all the views I want to preload are handled when the app loads). Here is the function to do this:

  var preload = function(route_str)
  {
      var regex = /(.*?)#(.*)/g;
      var match_obj = regex.exec(route_str);
      var view = new supersonic.ui.View({
          location: route_str,
          id: match_obj[2]
      });
      view.start();
  };

By doing this, I'm sure that the view will get loaded with the right location and id, and that when I use my start() function later, I won't have any problems.

You'll want to make sure that your structure.coffee file doesn't have any preload instructions so as not to create duplicate views that you'll have problems with later.

Finally, I have a view that is 2 levels in that is a form that posts data via AJAX operation. I wanted the view to go back to the previous view when the AJAX operation was complete. Using my earlier function resulted in the push() being rejected. It would be nice if AppGyver Supersonic could intelligently detect that pushing to a previous view should default to a layers.pop operation, but you don't always get what you want. Anyway, I managed to solve this using supersonic.ui.layers.pop(), which simply does what the Back button would have done.

Everything working as intended now.