7

I'm building a "tour guide" for my angular powered website.

I looked around for options and Intro.js seemed like the best fit. It had an Angular Directive already ready and everything: Angular Intro.js.

Everything worked as expected, until I had to add a step to first (and only the first) a DOM object that is being injected by a ng-repeat binding. I gave all ng-repeat items a unique ID (via $index) but Intro.js just fails to acknowledge it.

I'm guessing Intro is trying to find the DIV by the ID specified, but since the ng-repeat isn't complete yet, there's no DIV by that name.

I made a plunker where you can see that it' working on static content but fails to aknowledge the objects inside the ng-repeat.

Relevant code:

HTML:

<!-- Works -->
<div id="static">This is static content.</div>

<!-- Doesnt work -->
<div id="item{{$index}}" ng-repeat="item in items">
    {{ item.name }}
</div>

Angular Controller:

  $scope.IntroOptions = {
        steps:[
        {
            element: document.querySelector('#static'),
            intro: "This is static content"
        },
        {
            /** ID "item0" belongs to the first element on the ng-repeat **/
            element: document.querySelector('#item0'),
            intro: "Doesnt work!"
        }
        ],
        showStepNumbers: false,
        exitOnOverlayClick: true,
        exitOnEsc:true,
        nextLabel: '<strong>NEXT!</strong>',
        prevLabel: '<span style="color:green">Previous</span>',
        skipLabel: 'Exit',
        doneLabel: 'Thanks'
    };

Plunker: http://plnkr.co/edit/kE8P5Kq2Y5CVWEYgyBIo?p=preview

  • Assuming the reason above is the reason this isn't working, how do I make the directive wait for the DOM to be "ready"?

  • If my assumption is wrong, why isn't it working then?

pedropeixoto
  • 1,633
  • 2
  • 27
  • 52

4 Answers4

2
.directive('onLastRepeat', function() {
    return function(scope, element, attrs) {
        if (scope.$last) setTimeout(function(){
            scope.$emit('onRepeatLast', element, attrs);
        }, 50);
};});

Use this directive like this :

<div id="item{{$index}}" ng-repeat="item in items" on-last-repeat>
    {{ item.name }}
</div>

in your controller wait for finish the loop and call introjs

$scope.$on('onRepeatLast', function (eve, elem) {
//call intro
});
0

The most clueless approach I'd attempt at first would be to wrap the dynamic content:

<div id="dynamic">
    <div id="item{{$index}}" ng-repeat="item in items">
        {{ item.name }}
    </div>
</div>

The scope variable then becomes

$scope.IntroOptions = {
    steps:[
    {
        element: document.querySelector('#static'),
        intro: "This is static content"
    },
    {
        element: document.querySelector('#dynamic'),
        intro: "This is dynamic content"
    }
    ],
// ... cut ...

It looks like your original code is deadlocking: the view depends on the data ($scope.items) which depends on the view (IntroOptions)

Alex
  • 23,004
  • 4
  • 39
  • 73
  • That would just highlight the DIV surrounding all the items. I only want to highlight the first one. I realize I didn't make myself clear on the question, sorry about that. I edited it to clarify that point. – pedropeixoto Feb 26 '14 at 13:12
  • Ok, now I get it. I'll mess with the code some more. – Alex Feb 26 '14 at 13:43
0

You can observe for the $viewContentLoaded event that $rootScope will broadcast.

Loc Nguyen
  • 9,471
  • 5
  • 22
  • 27
0

I know this is quite old, but if anyone stumbles upon this answer, the best thing to do is to use a getter, it gets evaluated the moment it needs to access the property:

$scope.IntroOptions = {
    steps:[
    {
        element: document.querySelector('#static'),
        intro: "This is static content"
    },
    {
        /** ID "item0" belongs to the first element on the ng-repeat **/
        get element(){return document.querySelector('#item0')},
        intro: "Doesnt work!"
    }
    ],
    showStepNumbers: false,
    exitOnOverlayClick: true,
    exitOnEsc:true,
    nextLabel: '<strong>NEXT!</strong>',
    prevLabel: '<span style="color:green">Previous</span>',
    skipLabel: 'Exit',
    doneLabel: 'Thanks'
};
Matias Quaranta
  • 13,907
  • 1
  • 22
  • 47