1

I am trying to generate a tutorial module with Angular for my standard angular module (something to show the user how to use the UI, as some of our users are quite old people who need a bit of help). When doing so, I want the tutorial to get access to the main application and control everything. So far I can add data to the model and the main swallows it without any problem, making it quite easy to add mock data for the explanation.

That said, when the user clicks on the elements or interacts with them, I do not want to send any request to the server as this is a tutorial, fake, mock, so I want to disable the standard behavior at least for the tutorial elements and add my own ones (probably, go to next step on the tutorial).

But I don't want to need to use directives or booleans on the model like "isThisTutorial" or "isTutorialOn" as I want to isolate the tutorial and make it independent from the main, I don't want the main project to need to know anything about the tutorial, except for the call I make to activate it.

I thought I could try to access the elements with jQuery and override the behavior but it doesn't seem to work, as Angular still has the control (or maybe I'm doing something wrong). So, having in mind that I have access to the same scope, and I can modify the model at will, how can I take the UI control from the main Controller with my tutorial module Controller? Is that possible?

Thanks.

Update note: I found that jQuery actually works, only problem is that Angular had not yet digested the new data on the model so the element is not yet on the DOM when I try to access it. Even so, open to alternatives.

Ruben.Canton
  • 1,263
  • 3
  • 15
  • 29

2 Answers2

1

What you are trying to accomplish is fairly complex and as far as I could have found (it is been a while since I've been looking for one), no out of the box framework exists (there are some js page walkthrough framework around here, is one) that accomplish this type of control, although some will mask the UI to prevent the user control.
That said, for specific challenges you have raised, here are a few options:

  1. As far as preventing actual server requests, you can always mock these services (or the $http resource itself). Here it is done for testing purposes but you can obviously use it for other purposes, such as yours. The great thing about is that you can control exactly what comes back and so if the tutorial user creates cause the creation of entity, you can control exactly how this entity should look like and make it fit your tutorial. It also means its out the second the tutorial is done - no need to do anything about it.
    If your are using other methods (such as jQuery AJAX, there are equivalent methods).
  2. As far is event handling, you can actually override some of angular's build in directives (or any of your own) and inject some specific logic of your tutorial - that is - prevent clicking and allow only specific clicks, introducing a popup for some events, etc.
    Here is an example of overriding ngClick.
  3. You can also decorate services of angular (or yours) and injecting your own logic which can help intercepting behavior Here is a simple decorator and Here is another complex example of creating an event bus for your application, which can then be used as flag notifier for events (which can help decoupling different parts of your applications).

To Aggregate these aspects, I would create a tutorial module which will take place only in tutorial mode.

Community
  • 1
  • 1
Tomer
  • 4,382
  • 5
  • 38
  • 48
0

Well, the solution I've come to in the end, may not be the best but sharing it as it may help someone else:

First of all, I can add mock data to the model which is displayed on the UI as it should, but before accessing the DOM I need to wait for Angular to generate the elements and add them there. So I'm using angular.$apply:

    $scope.Conversations.push(mockData);
    $scope.$apply();

That is displaying an error on the Console: "Error: [$rootScope:inprog] $apply already in progress" but it works, without that it doesn't.

Now, just after that I have access to the DOM element and I can get it by using document.getelementById or as I have jQuery, just a selector $("#myId"). Once I have the element, I only need to tell Angular to "let it go" by unbinding it with this:

function RemoveAngularBounds(object) {
    angular.element(object).unbind();
}

Now, using jQuery again, I can add a "click" event to it and determine its new behavior overriding the one Angular gave it.

    var target = $("#myId");
    RemoveAngularBounds(target);
    $(target).click(function (e) {
        //e.stopImmediatePropagation();
        //e.preventDefault();
        $scope.step2();
    });

I had thought I would need to prevent propagation and prevent default but didn't, even so, leaving it there as it may help someone.

The rest is just showing some floating messages to the user around the UI. In case you need to deactivate and reactivate angular behavior on an element, I think you can try with .bind() but need to check that.

Ruben.Canton
  • 1,263
  • 3
  • 15
  • 29