Implement custom loading message DURANDAL

305 Views Asked by At

Hi i've got a durandal app that send data trought ajax but i dont know how to implement the loading indicator , here is the codes:

this is the view that loads the data loadinbox.html

<div class="modal-content messageBox">
    <div class="modal-header">
        <h3>LOGIN</h3>
    </div>
    <div class="modal-body">
       <h3>Entre com suas credenciais.</h3>
       <form data-bind="submit: ok">
           <input type="text" data-bind="value: login" placeholder="CPF"  class="form-control autofocus" />
           <input type="text" data-bind="value: senha" placeholder="Senha" class="form-control autofocus" />
       </form>
    </div>
    <div data-bind="if: $parent.loading">
        <img src="img/loading.gif"/>
    </div>

    <div class="modal-footer">
        <button class="btn btn-success" data-bind="click: ok, active: $parent.loading">Login</button>
    </div>
</div>

this is the model that loads the data loginBox.js

define(function (require) {
    var dialog = require('plugins/dialog');


    var loading = ko.observable();

    var loginBox = function(){
        this.login = '';
        this.senha = '';
        this.loading = false;
    };


 loginBox.prototype.ok = function () {
     this.loading =true;

     $.ajax({
         type: "post", 
         data: { "LoginForm[cpf]" : this.login, "LoginForm[password]" : this.senha , 'ajax':'login-form' },
         url: localStorage['baseurl']+localStorage['router']+'site/login',
         success: function (data){
             console.log(data);
         },
         error: function (request, status, error){
             console.log(request.status);
             console.log(status);
             console.log(error);
         },
         complete: function (data) {
             alert('hqweuiqhioehqio');
             this.loading =false; 
         }
     });
 };


 loginBox.show = function() {
     return dialog.show(new loginBox());
 };    

 return loginBox;

});
2

There are 2 best solutions below

2
On

On the surface, your approach is sound, but your approach to modules in Durandal is a little muddled. For example, you've declared loading twice, once as a scalar and once as an observable.

So, let's create an instance module (which means that we're going to return a constructor function):

loginBox.html (view)

<div class="modal-content messageBox">
    <div class="modal-header">
        <h3>LOGIN</h3>
    </div>
    <div class="modal-body">
        <h3>Entre com suas credenciais.</h3>
        <form data-bind="submit: ok">
           <input type="text" data-bind="value: login" placeholder="CPF"  class="form-control autofocus" />
           <input type="text" data-bind="value: senha" placeholder="Senha" class="form-control autofocus" />
        </form>
    </div>
    <div data-bind="if: $parent.loading()">
        <img src="img/loading.gif"/>
    </div>

    <div class="modal-footer">
        <button class="btn btn-success" data-bind="click: ok, active: $parent.loading()">
            Login
        </button>
    </div>
</div>

Notice that I changed your if binding to this:

"if: loading()"

referencing loading with parentheses. This performs an immediate eval using the default value supplied to the observable, and then a re-eval when the observable changes.

Also, it may be necessary to change "click: ok, active: $parent.loading()" to click: $parent.ok.bind($parent), active: $parent.loading(). Check your context using the debugger when the #ok function is entered.

A note on logic: It seems to me that what you might mean in the modal footer is

active: !$parent.loading()

Should the OK button really be active when the form is loading data?

loginBox.js (module instance approach)

define (
    [
        'plugins/dialog',
        'knockout'
    ],
    function (
        dialog,
        ko) {

        var LoginBox = function () {

            this.login = '';
            this.senha = '';
            this.loading = ko.observable(false);
        };

        LoginBox.prototype.ok = function () {
            var _vm = this;

            this.loading(true);

            $.ajax( {
                type: "post", 
                data: { "LoginForm[cpf]" : this.login, "LoginForm[password]" : this.senha , 'ajax':'login-form' },
                url: localStorage['baseurl']+localStorage['router']+'site/login',
                success: function (data) {
                    console.log(data);
                },
                error: function (request, status, error) {
                    console.log(request.status);
                    console.log(status);
                    console.log(error);
                },
                complete: function (data) {
                    alert('hqweuiqhioehqio');
                    _vm.loading(false);
                }
           });
        };

        LoginBox.prototype.show = function() {
            dialog.show(this);
        };  

        return LoginBox;

    };
);

Take note of my treatment of this.loading. It is an observable, and observables are updated using the approach I show above (remember, they are functions). When you assign true in this manner--this.loading = true--you override the observable itself and turn it into a non-observable scalar. So, when the value later changes (from false to true to false), the view is not updated.

Also note that you must import KnockoutJS.

Another issue: you have a this reference issue in your #complete function. Note that I do this at the top of your #Ok function:

var _vm = this;  //Some people are inclined to this format: var that = this;

and, then, in your #complete function, I do this:

_vm.loading(false);

Using this in your #complete function references the #complete function itself, not the viewModel. We have to save a reference to this outside the #complete function.

There was another problem: #show was not on the prototype.

1
On

I use a utility called Block UI in my App JS file I call it like this using the Global Ajax setup. By doing it this way you only do it once and then every Ajax call will show your loading gif and hide when any Ajax calls starts and finishes

HTML: in the index.html page(your main page)

  <div id="throbber" style="display:none;">
            <img src="/images/gears.gif" />
        </div>

Javascript

$(document).ready(function () {    
        $.blockUI.defaults.css.border = 'none';
        $.blockUI.defaults.css.backgroundColor = 'transparent';
        $.blockUI.defaults.message = $('#throbber');
        $.blockUI.defaults.showOverlay = true;
        $.blockUI.defaults.overlayCSS.backgroundColor = "#fff";

        $(document).ajaxStart(function () {
                $.blockUI();            
        }).ajaxStop(function () {
            $.unblockUI();
        });
    });

CSS:

#throbber{
    border:1px solid #346789;
    padding: 15px;
    background-Color: #fff;
    -moz-border-radius:0.5em;
    border-radius:0.7em;
    opacity:0.8;
    filter:alpha(opacity=80);
    color: #000 ;
    width:133px;
    height:133px;
    left: 50%;
    top: 40%;
    position: fixed;
 }