1

Having difficulty running JQuery code in a ViewModel and executing the code in a View. The environment is run using Durandal JS framework. Unfortunately, anything from a simple alert('Hello?') to running 60-second timer, isn't working. The timer just stays at 59:59. None of the JQuery code is working/running. Not even a simple $('h1').css('color','red');.

How do I run JQuery in a Durandal / Knockout view? Here's an example of a feature timer I'm trying to work in my View.

shell.html
The timer is found below the footer with a reset button.

<div class="wrapper">

    <!-- navigation -->
    <nav>
        <div class="container">...</div>
    </nav>
    <!-- end: navigation -->


    <!-- body -->
    <div class="container page-host" data-bind="router: { transition:'entrance' }"></div>
    <!-- end: body -->


    <!-- footer -->
    <footer>
        <div class="container">
            <div class="row">

                <div class="col-xs-12">...</div>

            </div>
        </div>
    </footer>
    <!-- end: footer -->


    <!-- timer -->
    <div class="timer affix-top" data-spy="affix" data-offset-top="50">
        <span id="countdown">59:59</span>
        <p id="reset" tabindex="0">
            <strong class="hidden-sm hidden-xs">Click here to reset timer</strong>
            <strong class="hidden-lg hidden-md">Reset timer</strong>
        </p>
    </div>
    <!-- end: timer -->

</div>

shell.js
The JQuery code needs to go in here, but everything I've tried doesn't work. Below is the JQuery code to run the timer.

define(['plugins/router', 'durandal/app'], function (router, app) {
    return {
        router: router,
        search: function() {
            //It's really easy to show a message box.
            //You can add custom options too. Also, it returns a promise for the user's response.
            app.showMessage('Search not yet implemented...');
        },
        activate: function () {
            router.map([

                { route: '', title:'Page Title', moduleId: 'viewmodels/setup', nav: true },
                { route: 'setup', moduleId: 'viewmodels/setup', nav: true },
                { route: 'review', moduleId: 'viewmodels/review', nav: true }

            ]).buildNavigationModel();

            return router.activate();
        }
    };
});

JQuery code (60-Second Timer)
This is the code I'm trying to insert into shell.js to display a timer on the page.

// TIMER: COUNTDOWN FROM 60 MINUTES WHEN PAGE IS LOADED

$(document).ready(function() {

  function countdown(element, minutes, seconds) {

    var time = minutes * 60 + seconds; // CREATE VARIABLE: SET TO 60 MINUTES (FOR ONE HOUR)
    var interval = setInterval(function() {

      var el = document.getElementById(element);

      $('#timer_ok, #timer_cancel, #dialog_overlay').on('click keypress', function() {
        $('#dialog').fadeOut(300);
        $('#dialog_overlay').fadeOut(300);
      });

      $(document).keyup(function(e) {

        // WHEN CLICKING "ESC" KEY CLOSE DIALOG WINDOW
        if (e.keyCode == 27) {
          $('#dialog').fadeOut(300);
          $('#dialog_overlay').fadeOut(300);
        }
      });

      // WHEN CLICKED, RESET THE TIMER...
      $("#reset").on('click keypress', function() {
        time = 3600; // SET TIME TO 60 MINUTES
      });

      var minutes = Math.floor(time / 60);
      if (minutes < 10) minutes = "0" + minutes;

      var seconds = time % 60;
      if (seconds < 10) seconds = "0" + seconds;

      var text = minutes + ':' + seconds;
      el.innerHTML = text;
      time--;

    }, 1000);
  }

  countdown('countdown', 60, 00); // START THE TIMER INSIDE THE ELEMENT ('ID', MINUTES, SECONDS)

});

main.js
We're using RequireJS to run the plugins. Adding this in case this is necessary to determine how the JQuery should be called in the shell.js file.

requirejs.config({
    paths: {
        'text': '../vendor/require/text',
        'durandal':'../vendor/durandal/js',
        'plugins' : '../vendor/durandal/js/plugins',
        'transitions' : '../vendor/durandal/js/transitions',
        'knockout': '../vendor/knockout/knockout-3.4.0',
        'bootstrap': '../vendor/bootstrap/js/bootstrap',
        'jquery': '../vendor/jquery/jquery-1.9.1'
    },
    shim: {
        'bootstrap': {
            deps: ['jquery'],
            exports: 'jQuery'
       }
    }
});

define(['durandal/system', 'durandal/app', 'durandal/viewLocator', 'bootstrap'],  function (system, app, viewLocator) {
    //>>excludeStart("build", true);
    system.debug(true);
    //>>excludeEnd("build");

    app.title = 'EV-CIS: Defects and Recalls Reporting';

    app.configurePlugins({
        router:true,
        dialog: true
    });

    app.start().then(function() {
        //Replace 'viewmodels' in the moduleId with 'views' to locate the view.
        //Look for partial views in a 'views' folder in the root.
        viewLocator.useConvention();

        //Show the app by setting the root view model for our application with a transition.
        app.setRoot('viewmodels/shell', 'entrance');
    });
});

Please help.

Eric Carr
  • 69
  • 7
  • I just created a [fiddle](https://jsfiddle.net/adigas/dsm5gca4/) with your countdown method and it works fine. Check the console for any errors. – adiga Nov 10 '17 at 07:16
  • 1
    Also, move the `click` and `keyup` event handler outside of `setInterval`. You are adding event handlers **every** second. After one minute, the `document` will have 60 `keyup` event handlers! – adiga Nov 10 '17 at 07:18
  • Thanks for helping me set up the timer in JQuery. However, the issue I'm having is running JQuery using the Durandal JS framework that runs Knockout JS. It's not running any JQuery code the way the existing framework is set up. I will update my posting. – Eric Carr Nov 10 '17 at 13:54
  • Possible duplicate of [Make VueJS and jQuery play nice](https://stackoverflow.com/questions/43240789/make-vuejs-and-jquery-play-nice) – Roy J Nov 10 '17 at 13:55
  • You said timer stops at 59:59, so I thought there was some issue with that. Instead of using `document.ready`, you can add the `countdown` function to `shell`. Then call that `function` inside shell's [`attached` lifecycle event](http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks.html#combined-lifecycle). `attached` function is triggered after the shell is attached to the DOM. – adiga Nov 10 '17 at 13:59
  • Thanks, @adiga, I'm new to Durandal and Knockout, so I will give it shot. Could you possibly provide an example of how to call that function inside the shell? Even if it's a simpler JQuery call (ex: $('h1').css('color','red');) I'm trying to wrap my brain around attaching lifecycle events. – Eric Carr Nov 10 '17 at 16:30

1 Answers1

1

You can use the compositionComplete Lifecycle Callback to add all the jquery functionality you'd want to hook after the DOM is ready.

define(['plugins/router', 'durandal/app'], function (router, app) {
    return {
        router: router,
        search: function() { },
        activate: function () { },
        compositionComplete: function() { 
           // keep the click, keyup, keypress handlers here, outside setInterval
          var minutes = 60, seconds = 0;
          var time = minutes * 60 + seconds;

          var interval = setInterval(function () {

              var el = document.getElementById("countdown");

              var minutes = Math.floor(time / 60);
              if (minutes < 10) minutes = "0" + minutes;

              var seconds = time % 60;
              if (seconds < 10) seconds = "0" + seconds;

              var text = minutes + ':' + seconds;
              el.innerHTML = text;
              time--;

          }, 1000);

        }
    };
});

Knockout has keyup and click bindings. So, you can use them instead of jquery event handlers as well.

adiga
  • 34,372
  • 9
  • 61
  • 83
  • 1
    That worked! Added this to the shell.js file and was also able to run multiple jquery events. Thanks! – Eric Carr Nov 10 '17 at 20:16