KnockoutJS afterRender callback when all nested Components have been rendered?

后端 未结 3 905
一向
一向 2021-02-15 21:22

I have a hierarchy of nested KnockoutJS Components using 3.2.0. It\'s working very well but I\'m looking to execute some code once my entire hierarchy of components has been loa

相关标签:
3条回答
  • 2021-02-15 21:48

    Here is what worked for me. I did not try it in all possible variations such as mixing sync and async components, or using custom component loaders.

    There is a method in KO 3.3.0 that all components loading goes through:

    ko.components = { get: function(componentName, callback) { ...

    the get method is invoked with a desired componentName and when component has been loaded - a callback is invoked.

    So all you need to do is wrap ko.components.get and callback and increment pendingComponentsCount on each call, and decrement it after callback is executed. When count reaches zero it means that all components were loaded.

    25 lines of JS code (using underscorejs).

    You also need to handle a special case where ko.applyBindings did not encounter any components, in which it also means that all components (all zero of them) were loaded.

    Again, not sure if this works in every situation, but it seems to be working in my case. I can think of few scenarios where this can easily break (for example if somebody would cache a reference to ko.components.get before you get to wrap it).

    0 讨论(0)
  • 2021-02-15 21:57

    I've written a knockout library that triggers an event when all components have been loaded and bound. It uses reference counting, similar to referencing counting used for garbage collection. I extensively use components in my project(s), including nesting many levels deep, and I can't live without knowing when everything is "ready to go". I haven't spend much time on documentation of usage, but the basics are there.

    Git Hub wiki: https://github.com/ericraider33/ko.component.loader/wiki

    Fiddle: https://jsfiddle.net/ericeschenbach/487hp5zf/embedded/result/

    Usage HTML:

    <div id="ko-div">
      Status: <span data-bind="text: loading() ? 'Loading' : 'Done'"></span>
      <br><br>
      <test-panel></test-panel>
    </div>
    

    Usage JS:

    var pageModel = { 
      loading: ko.observable(true), 
        completedCallback: function (childRef) { 
        pageModel.loading(false); 
        childRef.testValue(childRef.testValue()+1);  
      }
    };
    
    var tpRef = ko.componentLoader.ref.child({ completedCallback: pageModel.completedCallback});
    var tpModel = { 
      attached: function(element) { return tpRef; },
      testValue: ko.observable(5)
    };
    
    ko.components.register('test-panel', {
        viewModel: function() { return tpModel; },
        template: '<div data-bind="attached: true">Test Panel<br>From Code <span data-bind="text: testValue"></span></div>'
    });
    
    
    ko.componentLoader.setOptions({ verbose: true });
    ko.applyBindings(pageModel, $('#ko-div')[0]);
    
    0 讨论(0)
  • 2021-02-15 22:12

    If you'r working with ko.components this might be of use:

    1) Create a deferred object to keep track of each component loading

    var statusX = $.Deferred()
    var statusY = $.Deferred()
    

    2) Inform knockout to tell you when the component is loaded and ready

    ko.components.get('x-component', statusX.resolve) //Note: not calling resolve, but passing the function
    ko.components.get('y-component', statusY.resolve)
    

    3) Synch up both status deferreds

    $.when(statusX.promise(), statusY.promise())
     .done( function allComponentsLoaded(componentX, componentY){ 
                //Both components are ready here 
                //Note the arguments from the function comes via
                //ko->jquery deferred resolve
               });
    
    0 讨论(0)
提交回复
热议问题