2

I am in the process of moving my partial views to view components, as components according to Microsoft is significantly better than partial views, especially at handling complex views..

For the partial views that I'm now porting to view components, I have a lot of jQuery script. Normally I invoke my jQuery components in my $(document).ready() function like so:

$(document).ready(function(){
    Component1.init();
    Component2.init();
    ....
})

This logic was very useful because these views have to be updated several times. Therefore, when having partial views, the components were re-initialized because the $(document).ready() function would always fire when the partial view document was ready after update.

However, moving to view components, I still try to do the same. Unfortunately, I realized that the $(document).ready() is only fired the first time that the view component is ready - Meaning that, when updating the view component, the $(document).ready() function is not fired and hence not the jQuery components initialized. I'm using a standard Ajax call to target a controller which returns the viewcomponent - Everything works fine, except that the $(document).ready() function isn't fired after update.

Note

A workaround would be to initialize my components in the ajax success response. This works but is however not desirable.

  • What is going on here?
  • Why does my $(document).ready() function not fire after updating the view component?
  • How would I correctly solve this, so that all my jQuery components gets initialized once the view component is ready?

Example:

//view with component
<div id="Component">
    <vc:mycomponent lead="Model.Lead"></vc:mycomponent>
</div>

//Script Inside component
<script>
    $(document).ready(function () {
        Component.init();
    });
    var Component = (function () {
        var $button;
        var $component

        var init = function() {
             cacheDom();
             handlers();
        }
        var cacheDom = function(){
            var $button = $('#button1')
            var $component = $('#Component')
        }
        var handlers = function () {
            $button.on('click', function() {
                handleclick();
            })
        }
        var handleClick = function(){
            $.ajax({
                url: 'Controller/ViewComponent',
                type: 'GET',
                success: function (response) {
                    $component.html(response);
                }
            });
        }
        return {init : init}
    })

</script>
Jeppe Christensen
  • 1,680
  • 2
  • 21
  • 50
  • Can you add a concrete example of something that worked before, and doesn't work now (stripped down to the minimum to show what should work)? The onReady function should not have been triggered before when you're doing AJAX calls, unless the content you were loading had their own script files. – Tieson T. Apr 16 '20 at 22:13
  • I just updated my question. I'm not sure it makes any difference. – Jeppe Christensen Apr 16 '20 at 22:23
  • It does. Just one more clarification: is the script loaded inside a section (i.e. `@section scripts`, or similar)? – Tieson T. Apr 16 '20 at 22:24
  • It's not! Just a plain ol' – Jeppe Christensen Apr 16 '20 at 22:25

2 Answers2

1

There is a difference in how these scripts will run. When you do this on the initial view,

<div id="Component">
    <vc:mycomponent lead="Model.Lead"></vc:mycomponent>
</div>

<script>
    $(document).ready(function () {
        Component.init();
    });
    var Component = (function () {
        var $button;
        var $component

        /* ... your code ... */

        return {init : init}
    })
<script>

that $(document).ready(function () { ... }); is triggered when the whole page is loaded.

When you reload this content via an AJAX call, even though that $(document).ready(function () { ... }); is still there, it's never going to be triggered - it's only triggered when the DOM is originally constructed. See https://learn.jquery.com/using-jquery-core/document-ready/ and this Stack Overflow answer, which also includes a simple workaround. For your code, I'd imagine it would be as simple as adding

Component.init();

somewhere in the complete/done handler of your AJAX code.

If it was working before... seems like it should not have, unless your AJAX call is returning a complete page and not a partial view (assuming the load event could run before you discard whatever you're not injecting into the page).

Tieson T.
  • 20,774
  • 6
  • 77
  • 92
1

I'll give you answers to two situation of which you only experienced the second:

Problem: Eventhandlers (such as button clicks) are not working anymore after replacing the HTML after an AJAX call:
Solution:

Replace

$( ".myClass" ).click(function() {
  // ...
});

with

$('body').on('click', '.myClass', function () {
    // ..
});

Problem: How to insert JavaScript from a ViewComponent? (this is the problem you originally mentioned)
Solution: This has something to do with the order of when the ViewComponent is rendered and when other scripts (such as jQuery) are loaded.

This is a perfect answer: Javascript in a View Component
You can create a tag helper that deferes the execution of JavaScript after jQuery has been loaded.

A couple of days ago, I used this exact place to insert JavaScript that was called after the ViewComponent was rendered again after a client-side Ajax call.

<script type="text/javascript" on-content-loaded="true">
    console.log('this is being executed after loading the view component');
</script>
citronas
  • 19,035
  • 27
  • 96
  • 164
  • This is just wierd. I get what you did there, but why can I not use selectors as I used to? I don't really like doing `$('body').on('click', elem, func())` – Jeppe Christensen Apr 17 '20 at 08:51
  • I guess it depends where you register the event handler. If you have the JavaScript code in your view (where the view components will be rendered into), you can change to `$('body')....` if you want to work it out of the box. As far as I understand, it has something to do with the way the objects are inserted into the DOM and when. If you have any more knowledge regarding this matter, please share it ;-) It's also possible to rebind the handlers after your `$component.html(response);` call. But using it via `$('body')....` it's one way to not forget to rebind the handlers. – citronas Apr 17 '20 at 09:46