-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to know when a component has finished updating DOM? #1475
Comments
Could you provide an example? |
+1 I was trying to create a ko plugin for supporting insertion points.
I could easily memoize this content using the preprocessNode hook, but I have no hook of when I can insert this into the rendered template. |
Here's an example of where we might want to know if a component has finished rendering: http://codepen.io/anon/pen/EGgbk?editors=100 When you refresh that page there is a brief flicker while everything loads. Ideally there'd be a way to hide all of the components until everything has finished loading. |
I built some code around this problem because I wanted to test this: Here is a mocha test that demonstrates how it works:
Now I can write tests and "measure" how long it takes. The method test_bindingDone will help you but it's hacky I guess :) |
I agree that something like this would be valuable. We should consider it for KO 3.3+. |
+1 This could be super useful for page transitions and the like if you are using components for entire pages/panels of your UI. Maybe something along these lines? ko.components.register('my-component', {
viewModel: {
createViewModel: function(params, componentInfo) {
$(componentInfo.element).hide();
return new MyViewModel(params);
}
},
afterBind: function(componentInfo) {
$(componentInfo.element).slideDown();
},
template: ...
}); |
+1 for this issue. And +1 for @dantrain's idea.
would probably mean that a parent view model is listening to 'afterRender' of this child component. But I think it is useful for a component to know the 'afterRender' of itself. So calling componentViewModel.afterRender() or something is a good solution I guess. |
How about something in the view-model itself that if present gets fired. Similar to Durandal.js compositionComplete lifecycle events so that if the function exists in your viewmodel, it gets called once the component has been bound to the DOM. Perhaps you can pass in the element to this function so appropriate code would look like such. viewModel: function(params) {
// Data: value is either null, 'like', or 'dislike'
this.chosenValue = params.value;
// Behaviors
this.like = function() { this.chosenValue('like'); }.bind(this);
this.dislike = function() { this.chosenValue('dislike'); }.bind(this);
this.compositionComplete = function(element) {
$(element).doSomeJqueryFunction();
};
} |
For anyone who need a workaround with current version v3.2.0, I found this link that helps on stackoverflow. |
+1 for some way to get notified when a component and its child components have been loaded. The afterRender mechanism would work but it is fired before sub components are loaded. I just found this question after posting on stack overflow. See that question for more information (if curious). #1533 Also seems to be related. The existing workaround don't work for child components unfortunately. |
+1 for this issue and for @richotaylor comments about child components |
I solved this problem using an early build of 3.3 and turning all my components synchronous. Other benefits: better perf and less flashing content. This does not prevent you from dynamically loading component templates, I just do it myself and pass a string to the component declaration. It's only a different way to organize the codebase (and it also makes me think that including |
@jods4 Could you share a code example of how you do it? I would really appreciate it! |
Well, in my project I'm using Typescript with requirejs as AMD loader, but in essence it works like this (Sorry if my js syntax for require is not 100% correct, TS makes this so much easier): Step 1: be sure to have a KO 3.3 build, otherwise this won't work. Step 2: define your component. define(['knockout', 'text!template'], function(ko, template) {
ko.components.register('my-component', {
viewModel: MyVM,
template: template,
synchronous: true
});
}); Step 3: make sure your component is loaded before any view tries to use it. define(['MyComponent'], function() {
/* Define whatever you want that needs MyComponent to be loaded */
}); |
@jods4 Thanks! I will look into |
@W3Max Note that as of today 3.3 is officially released! Look at @rniemeyer blog post for more info: http://www.knockmeout.net/2015/02/knockout-3-3-released.html |
@jods4 @W3Max I want to note that the first time load still might be aync even if you mark the component with
It has been a long way without afterBind... And I'll tell you how I need it when I see you again |
@lzl124631x This is pretty old stuff but what I did was that I bundled all the templates with my app (using require.js + !text plugin). I required them in the file that created the custom component and passed them as string to KO. -> guaranteed fast sync load. |
@jods4 Shaking hands. I'm using require.js, !text plugin and r.js for AMD, bundling and minifying. All the js and html files are bundled into one js file in my case. So, as you've said, this can ensure me it's a sync load? I'm not sure about that. Have you tested it? (Just out of curiosity, are you still using require.js? I'm using it but found that it's very difficult to set the paths configs when your project getting bigger and bigger. Things got messed up. I tend to make a shift to webpack which seems more popular now. Have you made the shift?) |
@lzl124631x I went as far as not letting KO load the template through require but passing it directly as a string. Although we are moving away from KO -- which is great but doesn't seem to evolve towards newer web standards -- to Aurelia for newer projects, we still rely on AMD/requirejs at the moment. It is stable and has worked great for us for several years. Webpack, browserify and co. look cool but it seems that there is still a lot of friction and moving parts, which we can't afford here at work. We are considering making the move, but haven't done so... yet. 😉 |
Hope this helps |
It allows you to load HTML after the Dom ready event |
I've been using RequireJS and Knockout on large production sites for a couple years now and I've found that the technique suggested by @jods4 is the way to go. Knockout should not have put I got tired though of constantly having to add a dependency in every view model to So I put it all into a RequireJS plugin. The relevant plugin snippet is below, but the complete Gist can be found at: https://gist.github.com/jacobslusser/2d9210606a8ab64f3a4df7747ee40404: // A simple RequireJS plugin that will load a view and view model based on naming convention.
// Once loaded the view is added to the DOM so it can be used with KO templates and the view
// and view model are registered as a KO component.
define('component', ['jquery', 'knockout'], function ($, ko) {
return {
load: function (name, parentRequire, onload, config) {
parentRequire([name, 'text!' + name + '.html'], function (viewModel, view) {
// Add the view to the DOM
var template = '<script type="text/template" id="' + name + '">' + view + '</script>';
$('body').append(template);
// Register as a KO component
ko.components.register(name, {
viewModel: viewModel,
template: { element: name },
synchronous: true
});
onload(viewModel);
}, function (err) {
onload.error(err);
});
}
};
}); NOTE: This plugin assumes that Put this in it's own file, or as I do, just in my main RequireJS config file and use it like this: // The component is required using the 'component!' plugin we created. This will load
// 'page1.html' and 'page1.js' and return 'page1.js' to us.
define(['knockout', 'component!page1'], function (ko, Page1) {
'use strict';
function App() {
var args = { name: 'Jacob' };
// Example of using the component binding with the automatically registered component
this.sampleComponent = ko.observable({
name: 'page1',
params: args
});
// Templates can also be used and are probably preferable because we have easy access
// to the view model if we want to programmatically call anything on the view model.
// i.e. sampleTemplate().data.yourFunction()
this.sampleTemplate = ko.observable({
name: 'page1',
data: new Page1(args)
});
}
ko.applyBindings(new App());
}); When the plugin loads the template it also adds it to the DOM and registers the KO component using the This has had a huge productivity boost in our large RequireJS/Knockout applications. The result is that we typically use the It's the best of both worlds; it's syntactically easier to follow; and has better performance because the view and view model are required in parallel rather than in serial as it is when you require A more complete example can be found in the afformentioned Gist: https://gist.github.com/jacobslusser/2d9210606a8ab64f3a4df7747ee40404. Still lovin' Knockout after all these years... |
+1 Isn't this possible yet? |
knockout/tko (ko 4 candidate) latest More specifically, the The API isn't set or documented yet, but the bones have been set up. |
Fixed with #2319. |
Hi Guys,
I need to reflow my page after some KO components have finished rendering in the DOM to support this Foundation plugin: http://foundation.zurb.com/docs/components/equalizer.html
I know we have afterRender for the template and foreach bindings but i can't find anything similar for the new components functionality.
How can i go about executing a callback when my components are loaded (either individually or after all have been loaded)? Is there something i've missed?
The text was updated successfully, but these errors were encountered: