Using Custom Events

Right now, it’s easy for our views to invoke behavior in the rest of the application. Our data model functions, for example, are easily shared among the views. One thing that’s missing is a way for the app to trigger behavior in the views. There’s no good way for the app to reach into the scope of the view functions to make things happen.

One approach we could take is to add a button to our shiny new toolbar and have each view register a click handler when it is created. However, without a mechanism to remove that click handler, that handler and any variables in its scope will never be garbage collected. As users move through the views, our memory usage would gradually increase until the application crashes.

To avoid this problem, we can use a custom event to send messages to our views. This can be useful when the current view may or may not need to take action to respond to some other bit of behavior in our application, such as removing event handlers when the view is replaced. By having the view register an event listener, we can trigger behavior in the views without having to break the encapsulation provided by the view function or create memory leaks that destabilize the app.

One thing we need to figure out when adding these events is who is going to trigger them. In this case, the router knows when it’s about to replace the current view with another view. Sometimes, a view will hold on to resources that it needs to release when the view is replaced. Subscribing to an event gives the view a mechanism to do just that.

Instead of a library or framework to manage these events, we’re going to rely on web standards to act as the foundation, and we’re going to build a few functions on top of it that are specific to our app. At this point in the book, this approach should fill you with a certain sense of deja vu.

Specifically, we’re going to use the event system already provided by the Document Object Model (DOM), and trigger custom events that our views can listen to. DOM events are triggered on element, and they bubble up the element hierarchy. We can send events to views by triggering an event on every child of the view-container element (which should only be the view). The view can then subscribe to those events by binding an event listener to the element that it returns as the view. To trigger these kinds of events, you can make a new function in the learnjs namespace:

 learnjs.triggerEvent = ​function​(name, args) {
  $(​'.view-container>*'​).trigger(name, args);
 }

Taking advantage of the new event mechanism, we can add a Skip button for the problem view to the navbar. This button lets users skip to the next problem, even if they haven’t solved the current one. This button only shows up when the problem view is loaded, and it is removed when the problem view is removed.

To be able to act when a view is removed, you have to trigger an event when that happens. That requires a change to the router function. Before the view is replaced, you can trigger the event letting any existing views know they’re being removed. You can do this in the showView() function, after the new view has been created, with the following code:

 learnjs.triggerEvent(​'removingView'​, []);
 $(​'.view-container'​).empty().append(viewFn(hashParts[1]));

Now, you just need to make the changes to the problem view to add and remove the buttons. First, you’ll need to add the necessary behavior to the problemView() function. The following code should do the trick, once you’ve added the necessary markup for the skip-btn (we’ll do that next). Don’t forget to check if we’re at the end of the problem list. Also, note the use of jQuery’s bind function to attach an event listener to the view element.

 if​ (problemNumber < learnjs.problems.length) {
 var​ buttonItem = learnjs.template(​'skip-btn'​);
  buttonItem.find(​'a'​).attr(​'href'​, ​'#problem-'​ + (problemNumber + 1));
  $(​'.nav-list'​).append(buttonItem);
  view.bind(​'removingView'​, ​function​() {
  buttonItem.remove();
  });
 }

Next, we need to make a couple of changes to the markup. First, create a template for the button. You can add this to the templates <div> you created earlier.

 <li class=​'skip-btn'​>
  <a>Skip This Problem</a>
 </li>

With that, we should have a working Skip button. If you navigate to the problem view and then back to the landing page, you should see that the button disappears. Removing the view from the DOM using jQuery’s empty function, as we do, cleans up associated data and event handlers. So you don’t have to worry about cleaning up the cleanup function.

Now that we have this event system in place, we can use it for lots of different things. Once we start talking to web services, we could use events to inform views that data in our model has been updated, or that users have taken actions such as logging in or out. For now, we’re happy to use it for cleaning up references and preventing a memory leak. It’s time to deploy a new version of the app.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.132.235