benmarch/angular-ui-tour

Multi-page Tours configuring from JS not from HTML

Closed this issue · 1 comments

I am using the exact versions of the following:

  • Browser: Chome Version: 60.04
  • AngularJS: 1.6.2
  • Angular Bootstrap: 2.5
  • Angular UI Tour: 0.8.0

I have installed this library via: bower

I have observed the following behavior:

I checked the documentation how it's possible to navigate between view-s and load the next tour steps dynamically configured from HTML.
I already achieved similar adding the next steps dynamically from JS after the acual location change is successfully done, and it works fine when I press only Next all the time. But when I press the Back, then things started to get complicated:

  • When I go back by changing the state, the previously created steps are still on the DOM, but it seems these are not attached to the proper element (probably because it was destroyed, and it is not implemented to look for it again).

The only way to handle this that I need to implement a mechanism to remove and readd steps based on the user's Next/Prev movement?

This is how I expected it to behave:

Here is my tour config, and all related step configs:

/**
 * Created by nbalazs on 28/04/16.
 */

angular.module( 'onboarding.tour', [
  'bm.uiTour',
])

.service('onboardingTourService',['$state', '$transitions', 'uiTourService', '$q', onboardingTourService]);

function onboardingTourService($state, $transitions, uiTourService, $q) {

  var tour = null;
  var stepMapper = null;

  var stateToSteps = function() {
    return {
      start: [
        {
          stepId: 'welcome-message',
          selector: 'body',
          order: 10,
          orphan: true,
          title: 'Welcome!',
          content: 'ALL online! ',
          onNext: function () {

            if ($state.current.name === state.first) {
              addTourSteps(tour, $state.userHomeState());
              return $q.resolve();
            }
            else {
              $state.go('state.first');
              return tour.waitFor("home-notifications");
            }

          }
        }

      ],
      'state.first': [
        {
          selector:"#home-notifications",
          stepId: 'home-notifications',
          order: 20,
          title: 'Lorem ipsum ',
          content: 'Lorem ipsum dolor sit amet, pro cu brute accommodare, vis ad minim eripuit cotidieque...'
        },
        {
          selector:"#home-available",
          stepId: 'home-available',
          order: 30,
          title: 'Lorem ipsum 2',
          placement: "left",
          content: 'Lorem ipsum dolor sit amet, pro cu brute accommodare, vis ad minim eripuit cotidieque',
        },
        {
          selector:'a[ui-sref="home.trallala"]',
          stepId: 'home-what',
          order: 40,
          title: 'Lorem ipsum 3',
          placement: "bottom",
          content: 'stb...',
          onNext: function () {
              $state.go('state.second');
              return tour.waitFor('create-button');
          }
        }
      ],
      'state.second': [
        {
          selector:"#create-button",
          stepId: 'create-button',
          order: 50,
          title: 'Lorem ipsum 4',
          content: 'You can submit your case with this button. Lorem ipsum dolor sit amet, pro cu brute accommodare, vis ad minim eripuit cotidieque...',
          onPrev: function() {
            $state.go('state.first');
            return $q.resolve();
          }
        }
      ]
    };
  };

  var deregisterTransitionEvent = $transitions.onSuccess( {}, function(trans) {

    // Ongoing onboarding
    if (tour) {
      addTourSteps(tour, $state.current.name);
    }

  });


  this.start = function () {

    tour = uiTourService.createDetachedTour('onboardingTour', {
      backdrop: true,
      onEnd: tourEnded,
      appendToBody: true
    });
    
    stepMapper = stateToSteps(tour, $state);

    addTourSteps(tour, 'start');

    tour.start();
  };


  function addTourSteps (tour, ofState) {

    var steps = stepMapper[ofState];
    if (!steps.added) {
      steps.forEach(tour.createStep.bind(tour));
      steps.added = true;
    }

  }

  function tourEnded() {
    tour = null;
    stepMapper = null;
  }
  
  this.setupTour = function(tour) {
    addTourSteps(tour);
  };

}

Hey @balazsnemeth, it looks like this is because once the element is found it stores it for later. I can add a check to make sure the element is still in the DOM or query for it again.