17

I have a JavaScript file AppForm.js, which I wish to reinitialize after a successful ajax post response.

The file itself contains, among others

(function(namespace, $) {
    "use strict";

    var AppForm = function() {
        // Create reference to this instance
        var o = this;
        // Initialize app when document is ready
        $(document).ready(function() {
            o.initialize();
        });

    };
    var p = AppForm.prototype;

    p.initialize = function() {
        // Init events
        this._enableEvents();

        this._initRadioAndCheckbox();
        this._initFloatingLabels();
        this._initValidation();
    };

    p._enableEvents = function () {
       //blah blah blah
       e.preventDefault();
    };

    p._initRadioAndCheckbox = function () {

    };

    p._initFloatingLabels = function () {

    };

    p._initValidation = function () {

    };

    window.materialadmin.AppForm = new AppForm;
}(this.materialadmin, jQuery)); // pass in (namespace, jQuery):

How can I do that?

$.ajax({
    url: path, type: "POST", cache: "false",
    dataType: "html",
    contentType: "application/json; charset=utf-8",
    traditional: true,
    data: JSON.stringify(postData)
}).success(function (data) {
    $("#products-list").html(data);
    //**PERFORM INIT OF JS FILE**

}).error(function (data) {

});

Thanks to Dan's answer the solution is pretty close but the events are not working since e.preventDefault(); is called.

And here is the full script

(function(namespace, $) {
 "use strict";

 var AppForm = function() {
  // Create reference to this instance
  var o = this;
  // Initialize app when document is ready
  $(document).ready(function() {
   o.initialize();
  });

 };
 var p = AppForm.prototype;

 // =========================================================================
 // INIT
 // =========================================================================

 p.initialize = function() {
  // Init events
  this._enableEvents();
  
  this._initRadioAndCheckbox();
  this._initFloatingLabels();
  this._initValidation();
 };
 
 // =========================================================================
 // EVENTS
 // =========================================================================

 // events
 p._enableEvents = function () {
  var o = this;

  // Link submit function
  $('[data-submit="form"]').on('click', function (e) {
   e.preventDefault();
   var formId = $(e.currentTarget).attr('href');
   $(formId).submit();
  });
  
  // Init textarea autosize
  $('textarea.autosize').on('focus', function () {
   $(this).autosize({append: ''});
  });
 };
 
 // =========================================================================
 // RADIO AND CHECKBOX LISTENERS
 // =========================================================================

 p._initRadioAndCheckbox = function () {
  // Add a span class the styled checkboxes and radio buttons for correct styling
  $('.checkbox-styled input, .radio-styled input').each(function () {
   if ($(this).next('span').length === 0) {
    $(this).after('<span></span>');
   }
  });
 };
 
 // =========================================================================
 // FLOATING LABELS
 // =========================================================================

 p._initFloatingLabels = function () {
  var o = this;

  $('.floating-label .form-control').on('keyup change', function (e) {
   var input = $(e.currentTarget);

   if ($.trim(input.val()) !== '') {
    input.addClass('dirty').removeClass('static');
   } else {
    input.removeClass('dirty').removeClass('static');
   }
  });

  $('.floating-label .form-control').each(function () {
   var input = $(this);

   if ($.trim(input.val()) !== '') {
    input.addClass('static').addClass('dirty');
   }
  });

  $('.form-horizontal .form-control').each(function () {
   $(this).after('<div class="form-control-line"></div>');
  });
 };
 
 // =========================================================================
 // VALIDATION
 // =========================================================================

 p._initValidation = function () {
  if (!$.isFunction($.fn.validate)) {
   return;
  }
  $.validator.setDefaults({
   highlight: function (element) {
    $(element).closest('.form-group').addClass('has-error');
   },
   unhighlight: function (element) {
    $(element).closest('.form-group').removeClass('has-error');
   },
   errorElement: 'span',
   errorClass: 'help-block',
   errorPlacement: function (error, element) {
    if (element.parent('.input-group').length) {
     error.insertAfter(element.parent());
    }
    else if (element.parent('label').length) {
     error.insertAfter(element.parent());
    }
    else {
     error.insertAfter(element);
    }
   }
  });

  $('.form-validate').each(function () {
   var validator = $(this).validate();
   $(this).data('validator', validator);
  });
 };
 
 // =========================================================================
 // DEFINE NAMESPACE
 // =========================================================================

 window.materialadmin.AppForm = new AppForm;
}(this.materialadmin, jQuery)); // pass in (namespace, jQuery):

UPDATE 1 I added window.materialadmin.AppForm.Initilize at the ajax response but the events are not working

UPDATE 2 And here is the code that does not work after the postback.

$(".ProductOnlyForDemonstation, .IncludeInMainPage, .Active")
    .on('click', 'button', function(){
        $('.sweet-overlay').toggle();
        if (jQuery("#FORM").valid()) {
            var id = $(this).attr("data-id");
            $.post("/product/DemoIncludeActive", {
                "Id": id,
                "ProductOnlyForDemonstation": $("#ProductOnlyForDemonstation-" + id).is(':checked'),
                "IncludeInMainPage": $("#IncludeInMainPage-" + id).is(':checked'),
                "Active": $("#Active-" + id).is(':checked'),
            },
            function (data) {

            }).success(function (data) {

            }).error(function () {

            });
        }
    });
John Slegers
  • 45,213
  • 22
  • 199
  • 169
OrElse
  • 9,709
  • 39
  • 140
  • 253
  • Does my answer work for you? – John Slegers Mar 27 '16 at 13:44
  • @JohnSlegers I guess i am doing something terribly wrong. Cannot read property 'initialize' of undefined – OrElse Mar 27 '16 at 13:52
  • Where is the instance of `AppForm` that you want to call `initialize` on? – Dan Prince Mar 27 '16 at 13:57
  • @DanPrince Updated as requested. I hope you mean this :) – OrElse Mar 27 '16 at 14:00
  • 2
    Can't you just put the entire file on jsFiddle or something? That would make it a lot easier for us to figure out what's going on! – John Slegers Mar 27 '16 at 14:15
  • @JohnSlegers Thank you for your efforts sir. I update the post with jsfiddle at the bottom. – OrElse Mar 27 '16 at 14:32
  • @OrElse : I updated my answer. Let me know if that helps! – John Slegers Mar 27 '16 at 14:50
  • @JohnSlegers It does not work sir. i tried window.materialadmin.AppForm.initialize(); which does the trick partially. The issue now is that the events do not fire after the postback. I guess that e.preventdefault blocks that – OrElse Mar 27 '16 at 15:11
  • @OrElse : ... and removing `e.preventDefault();` is not an option?! – John Slegers Mar 27 '16 at 15:14
  • @JohnSlegers The issue is another finally. By using window.materialadmin.AppForm.initilize(); after post-back, the events are not raised at all. – OrElse Mar 27 '16 at 15:20
  • @OrElse I have posted an anwser. You just need to print the in your server side script which you are calling in ajax. Keep the data-type option in $.ajax as "html". – shivgre Mar 31 '16 at 07:08
  • 1
    @OrElse when you say the events aren't working, what do you mean? Would it be possible for you to create a jsfiddle that demonstrates the problem? It'd be much easier for us to understand what you want to do that way. – Dan Prince Mar 31 '16 at 10:16
  • @OrElse I tryed to reproduce your issue in a JSFiddle but i do not really understand the problem. Can you have a look here a tell what is wrong? https://jsfiddle.net/ludo6577/e0xc2ysx/ (We need more informations about it) – Ludovic Feltz Mar 31 '16 at 11:07
  • The code you have in `_enableEvents` does not make sense. `e` will be undefined, since you're not defining it anywhere, so `e.preventDefault()` is probably throwing an error. Do you have any errors in your console? – Heretic Monkey Apr 01 '16 at 14:14
  • @OrElse was my solution working. As I have used the same solution in adding file upload scripts dynamically when creating new fields by loading each time the required scripts. – shivgre Apr 05 '16 at 14:56

11 Answers11

11

You can wrap your code in a global function.

(function(namespace, $) {
  "use strict";
  window.main = function() {
    var AppForm = function () {
    // ...
    };
  };

  window.main(); // you can initialize it here
)(this.materialadmin, jQuery);

And execute it if the response is successful.

.success(function (data) {
  $("#products-list").html(data);
  //**PERFORM INIT OF JS FILE**
  window.main();
}).error(function (data) {

});

Edit: It looks like you're exposing the initialize method on a global object. You can just call that init method when the AJAX response completes.

.success(function (data) {
  $("#products-list").html(data);
  //**PERFORM INIT OF JS FILE**
  window.materialadmin.AppForm.initialize();
}).error(function (data) {

});
Dan Prince
  • 29,491
  • 13
  • 89
  • 120
  • That was really helpful. Can you please check the update? – OrElse Mar 27 '16 at 13:39
  • Can you explain what it is you meant by "here is the code that does not work". Is there an error? Is something supposed to happen that doesn't? – Dan Prince Apr 03 '16 at 14:48
2

Related to UPDATE 2

Try to register your events with delegation:

$(document).on(
    'click',
    '.ProductOnlyForDemonstation button, .IncludeInMainPage button, .Active button',
    function() {
        // Your code
    }
);

I suppose you're loading something and render new page content after response, so previously registered events are not attached to new elements. With delegation you'll get your events working even after elements were added to DOM dynamically (if they match with delegating selector), because events are attached to document and bubbled from your buttons. You can attach event deeper in the DOM than document itself, but to the element containing your dynamic content (in other words: to closest element that will not be overriden after completing request).

PS. You can also add some unique class to all .ProductOnlyForDemonstation button, .IncludeInMainPage button, .Active button and delegate events to that class (shorter definition)

Wirone
  • 3,304
  • 1
  • 29
  • 48
1

some checks for the events to work after postback

1)using $("#products-list").html(data) will remove all the events attached to child elements of #products-list.

So either a)attach events once on "#products-list" only with event-delegation In jQuery, how to attach events to dynamic html elements? or b)reattach events on every child after using $("#products-list").html(data)

2) dont use .html() because it also removes all jquery data and events on children. update independent children elements instead.

Community
  • 1
  • 1
Shishir Arora
  • 5,521
  • 4
  • 30
  • 35
  • @OrElse: Can you share the html structure as well? if any of ".ProductOnlyForDemonstation, .IncludeInMainPage, .Active" elements are inside #products-list then attach events on "#products-list" only – Shishir Arora Apr 04 '16 at 09:31
1

I had experienced same issue like you. After reinitializing events,all events are not working properly.

I have tried lots and finally i have found issue.when i am reinitializing all control, all events are rebind.

so they are not fired properly. so please unbind all events related to your control and then init all control agian and bind all event.


Updated answer

if you are using jQuery 1.7 or onwarads then add following code:

$(".ProductOnlyForDemonstation, .IncludeInMainPage, .Active").off();
$('[data-submit="form"]').off('click');
$('textarea.autosize').off('focus');
$('.floating-label .form-control').off('keyup change');
//-----------------
//**PERFORM INIT OF JS FILE**

before this line. //**PERFORM INIT OF JS FILE**

and you are using jquery below 1.7 then use following code:

$(".ProductOnlyForDemonstation, .IncludeInMainPage, .Active").unbind();
$('[data-submit="form"]').unbind('click');
$('textarea.autosize').unbind('focus');
$('.floating-label .form-control').unbind('keyup change');
//-----------------
//**PERFORM INIT OF JS FILE**

before this line. //**PERFORM INIT OF JS FILE**

for more help related to unbind click here.

for more help related to off click here. i hope this will help.

Vishal Patel
  • 1,715
  • 16
  • 26
1

In order to call a function you should take into account the following points below:

The function should be defined in the same file or one loaded before the attempt to call it.

The function should be in the same or greater scope then the one trying to call it.

So, the following example should work:

You declare function fnc1 in first.js, and then in second you can just have fnc1();

first.js :

function fnc1 (){
    alert('test');
}

second.js :

fnc1();
index.html :

<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
Murat Yıldız
  • 11,299
  • 6
  • 63
  • 63
0

You could add the line namespace.initialize = p.initialize; at the end of your code :

(function(namespace, $) {
    "use strict";

    /* .......  */

    // =================================================
    // DEFINE NAMESPACE
    // =================================================

    namespace.AppForm = new AppForm;
    namespace.initialize = p.initialize;
}(this.materialadmin, jQuery)); // pass in (namespace, jQuery):

Then, p.initialize becomes available globally as materialadmin.initialize, and you can call it from another file like this :

materialadmin.initialize();
John Slegers
  • 45,213
  • 22
  • 199
  • 169
0

Maybe two solutions

First solution

Create a file js with your functions who will reload.

<script language="text/javascript">
   function load_js()
   {
      var head= document.getElementsByTagName('head')[0];
      var script= document.createElement('script');
      script.type= 'text/javascript';
      script.src= 'source_file.js';
      head.appendChild(script);
   }
</script>

And in your success :

.success(function (data) {
  $("#products-list").html(data);      
   load_js();
}).error(function (data) {

});

2nd Solution

Like the first solution : Create a file js with your functions who will reload.

Use use getScript instead of document.write - it will even allow for a callback once the file loads.

Description: Load a JavaScript file from the server using a GET HTTP request, then execute it.

So you can try this :

.success(function (data) {
    $.getScript('your-file.js', function() {

}).error(function (data) {

});

or simply :

jQuery.getScript('my-js.js');

You will try, and tell me if that helps.

Greg
  • 826
  • 5
  • 21
  • And maybe try with .load() ? more info here http://stackoverflow.com/questions/19433766/jquery-ui-re-initializing-a-function-after-loading-content-via-ajax – Greg Mar 30 '16 at 12:34
  • Tried the first solution, but the events are not raised after postback – OrElse Apr 03 '16 at 10:22
0

It should be simple by printing content of this at top of your ajax url script :

<script src="your-js-to-be-initialized.js"></script>

Your jquery ajax code will remain the same. You just need to print the script on each request so that it is reinitialized and binds to your elements.

$.ajax({
    url: path.php, type: "POST", cache: "false",
    dataType: "html", contentType: "application/json; charset=utf-8",
    traditional: true,
    data: JSON.stringify(postData)
}).success(function (data) {
    $("#products-list").html(data);

    //**PERFORM INIT OF JS FILE** 
    //path.php should echo/print the <script src="your-js-to-be-initialized.js">

}).error(function (data) {

});
shivgre
  • 1,163
  • 2
  • 13
  • 29
  • @OrElse it seems like you have not seen my answer, I have used this very similar solution on a page with dynamically added file upload fields by clicking a "Add more" button. You just need to return all the jquery/javascript that is needed for your dynamically generated html to work in your ajax response and you should be good. Try my solution it is guaranteed to work. – shivgre Apr 05 '16 at 15:07
0

I looked at your edit history and saw you did

p._enableEvents = function () {
    var o = this;

    // Link submit function
    $('[data-submit="form"]').on('click', function (e) {
        e.preventDefault();
        var formId = $(e.currentTarget).attr('href');
        $(formId).submit();
    });

    // Init textarea autosize
    $('textarea.autosize').on('focus', function () {
        $(this).autosize({append: ''});
    });
};

If this is still how you enable your events, I suspect the cause might be you have more than one subscription on form click and textarea focus after reinitializing on your ajax callback. I suggest try only do other init tasks, and exclude event bindings in your callback function.

YSJ
  • 382
  • 2
  • 14
0

Try make it like this:

(function($) {
    "use strict";

    var materialadmin = {};

    var AppForm = function() {
        //closure
        var self = this;

        (function(){
        //todo: init events
        };)();

        //<your AppForm class's props here...>
    };

    materialadmin.Init = function(){
        //create instance of AppForm calss for materialadmin object
        materialadmin.appForm = new AppForm();
    }

    return materialadmin;
//*}(jQuery)); //  syntax mistake, i'm sorry)).*
})(jQuery);

$(document).ready(function(){
    materialadmin.Init();
});

$.ajax({
  url: path, 
  type: "POST", 
  cache: "false",
  dataType: "html", 
  contentType: "application/json; charset=utf-8",
  traditional: true,
  data: JSON.stringify(postData),
  success: function (data) {
    $("#products-list").html(data);
    
    materialadmin.Init();
  },
  error: function(){
    alert('error')}
});
IgrDi
  • 625
  • 6
  • 4
0

As you're using jQuery validator, you can use Validator's resetForm method in order to reset your form.

For this purpose, you can expose a reset method like follows:

    p.reset = function () {
        // Reset managed form
        $('.form-validate').data('validator').resetForm();

        // Reset custom stuff
        this._initRadioAndCheckbox();
        this._initFloatingLabels();
    };

Note that in order to reset your form correctly after posting your request, you need to isolate event binding from the init stuff, for instance the following event binding should move from _initFloatingLabels to _enableEvents:

    // Link submit function
    $('[data-submit="form"]').on('click', function (e) {
        e.preventDefault();
        var formId = $(e.currentTarget).attr('href');
        $(formId).submit();
    });

Finally, you just have to call window.materialadmin.AppForm.reset() in your POST request's callback.

ngasull
  • 4,206
  • 1
  • 22
  • 36