KnockoutJS bind event after template render - javascript

I've been searching for a while, and I'm pretty confident this is a new question, and not a repeat like the title suggests. :)
Basically, I'm trying to find out if there is a subscribe-able event that KnockoutJS creates after a template render when using something like jQuery templates.
I'd use the built-in "afterRender" but I found out that it doesn't fire if the observable array is cleared. I built this demo to illustrate that problem: http://jsfiddle.net/farina/YWfV8/1/.
Also, I'm aware that I could write a custom handler...but that seems really unnecessary for what I need.
I just want one event that fires after the template finishes rendering.

My colleague actually solved this last night using something we were playing with before I went home.
So the whole "problem" with the events “afterRender”, “afterAdd”, and “beforeRemove” is that they act differently in conjunction with a "foreach" binding. KnockoutJS is nice enough to tell you this on their page, but for whatever reason it didn't actually sink in for me until I saw it in practice.
What really works is to scrap the whole "foreach" binding and use Knockout's native "data" bind like this:
data-bind="template: { name: 'item-template', data: items, afterRender: caller }"
Then "afterRender" works exactly as the name suggests.
I was under the impression that you couldn't iterate the collection and render new UI without foreach, but these examples illustrate that it does work.
http://jsfiddle.net/farina/kuFx2/1/ (Using object array style ViewModel)
http://jsfiddle.net/farina/QtZm2/1/ (Using function style ViewModel)
I made an example for both ViewModel styles because I sometimes need one or the other.
Thanks for the help Dan!!

Is beforeRemove is what you are looking for? I am not sure what behaviour you want to achieve. Please checkout this sample: http://jsfiddle.net/romanych/YWfV8/8/
Is it what you want or not?

Related

creating and destroying shieldUI widgets

I am coming from a different development background with a very little jQuery and/or shieldUI knowledge. Can someone please very shortly explain how to remove/destroy shieldUI components or widgets? I don't see any special widget method therefore I assume this is done with jQuery. By destroying I mean removing everything down to (including) markup.
Also, what happenes with widget when calling hide()? I see that markup is somehow stripped down (removed), but some wrappers remain. Is it safe to call another widget setup with the same "id" ? will it overwrite? will it cause object orhpans?
As you see I am missing some very basic "how it works". I am a quick learner so please, just few basic pointers will do. Thank you :)
EDIT: found destroy widget method under "swidget()"...what is swidget?
swidget() gives you a reference to the component instance, through which you can access methods and properties, such as height, width, refresh(), destroy(), etc.
Further, with respect to the question at hand, regarding the destroy method and approach - the ideas is, that whenever you need to refresh some data on the component, you can recreate it, rather than calling a method such as rebind.
This is demonstrated in the following demo:
http://demos.shieldui.com/web/rangebar-chart/related-charts

Should all the view logic go in templates?

For example, I need to disable every input when the view's model isn't new (has an id).
I can do :
if(!view.model.isNew()) {
view.$('input, select, textarea').prop('disabled', true);
}
or I can go do an "if" on every input I have in my template:
<input type="text" {{# if model.id }}disabled{{/ if }}/>
If we follow the MVC (or MVP) pattern, I guess the second approach would be best, since the view logic is in the view. However, if I go with this approach and decide to change the condition that disables the inputs, I need to change it for every input in EVERY template. If I leave in the JS code, there is only one place to change.
This is just one example, but I am having similar dilemmas with alot of things. Hopefully you got an answer for that.
Cheers!
Based on your question, I'd say that you're probably running into this general problem for two related reasons -
You're using backbone exclusively, rather than a framework like Marionette or Chaplin.
Your views are "too big" and trying to incorporate too much material into a single view.
I almost never include logic into templates and that's because I almost never need to. That's because I write a lot of very small views, and piece them together to make large views. Marionette's view classes make this very easy to do, by subclassing the backbone view into different view types, adding additional utilities and drastically cutting down on boilerplate. Marionette is also very good at handling nested views, which really allow you to drill down and create the exact view tool you need for a specific use case.
It's not at all uncommon to be able to define a useful view using Marionette in 1-2 lines of code, which gives you a lot of flexibility to make very small, efficient views. The disadvantage to making lots of small views is that it's a lot of code to write, and could become difficult to maintain, but Marionette makes that disadvantage relatively insignificant. Marionette only took me a couple of days to get the hang of, and I highly recommend integrating it into your Backbone apps for exactly this problem.
When your views are big, and try to do too much, you wind up with the problem you're describing - you have to modify them too much to fit your specific needs and your code gets ugly and hard to read. When you have a lot of small views, they're very responsive and need little if any customization.
The example from your question is, I think, a border line case. My gut instinct would be to create two entirely separate views and run them under the following pseudo code:
editableView = { //definition }}
disabledView = { //definition }}
if (newModel)
editableView.render()
else
disabledView.render()
This is my gut instinct because my bet is that there are other differences between the views than whether the inputs are editable. Even if there aren't now, you may find in a few months that your needs have changed and that you'd like to incorporate some changes. The approach I suggest allows you to put those changes right into the appropriate View and not have to worry about logicking them out in a single view and deciding whether that logic belongs in the template or the view.
If you were absolutely certain that the only difference between the two views was going to be whether the inputs were editable, and that your needs are not going to change in the future, then maybe you would want to think about rendering them with the same view. If that is the case, I'd recommend that you put the logic in the javascript, rather than in the template, for the reasons you identified. But as I said, your example really is a borderline case, and in most instances I think you'll find that shrinking your view scope will really help you to see where your "template logic" belongs.
NOTE: I've talked a lot about Marionette in this answer, but I also mentioned Chaplin as another option above. I don't have much experience with Chaplin, but you may want to consider it at it as a Marionette alternative.
I prefer to do implement view logic in templates:
It is easier to change and read
Any dependency to id, element and etc can be implemented in template
List item
complex logic can be implemented in templateHelper of marionette or serializeData of Backbone without any dependencies to content of template or view
you can also implement complex logic using Handlebar Helper
Disadvantages of view logic in code are:
in case of changes to selectors (id, classes and so), all views have to be changed or reviewed
logic has to be applied again if view is re-rendered
Perhaps what you might be looking for presenter (aka decorator).
Instead of sending the template the model directly, consider sending it though a presenter, this way you can construct the attributes for the input field. Something like this:
present = function(model) {
return {
inputAttributes: model.isNew() ? 'disabled' : '',
id: model.id,
name: 'Foobar'
}
}
template(present(model));
then in your template:
<input type="text" {{inputAttributes}}>

jQuery document ready with Knockout.js

I just got thrown into the Umbraco ASP.NET CMS for my latest project, I'm not sure if this is how it across the board but for my setup Knockout.js is doing all the templating.
I'm not too keen on knockout.js but so far it's been pretty straight forward except for when I start adding in some jQuery stuff, the problem I'm having is jQuery is firing before knockout has finished populating the page with all the elements.
The only solution that's worked for me thus far is all my jQuery stuff is wrapped in the setTimeout() function, which obviously is no good.
What's the most efficient way to make jQuery and Knockout work together so jQuery doesn't before knockout is done?
I recently had the same issue with the jSignature plugin and my Knockout view. I needed the KO view to have fully rendered before I invoked jSignature, otherwise it didn't size itself correctly.
I fixed it with a template binding and an afterRender callback function to invoke the jQuery work.
Here's the KO docs:
http://knockoutjs.com/documentation/template-binding.html
Here's a quick jsfiddle showing how you can use it:
http://jsfiddle.net/PCbFZ/
The trick is that you can use the afterRender callback of the template binding without actually using a template itself. Instead, you wrap all your existing HTML in a div that will invoke the afterRender callback:
<div data-bind="template: {afterRender: initApp}">
<!-- your existing HTML here -->
</div>
initApp is the function that does the jQuery work.
I think that should generally do what you need, though if your HTML is particularly complex, or you have many views you need to render inside the one page, you might need to do a bit more work. Let me know how you get on - maybe I can try to help a bit more if this doesn't quite fix your issue as easily as it did mine!
Update - following the comment from JACL below - here's an extended version of the fiddle showing this technique also working with ko-if. Each time you show/hide the 'widget' using the checkbox, a different random colour is applied to indicate the afterRender function doing its work.
http://jsfiddle.net/PCbFZ/15/
You might use:
$(window).load(function(){ /* code */ }); instead of $(document).ready();
Perhaps window.load instead of document.ready will do the trick

attaching behavior to elements created by knockoutjs

Take, for example, the situation using knockoutjs:
<div id="container" data-bind="foreach:containers">
<li class="complex-element">...</li>
</div>
Now I want to attach some complex behavior to complex-element. How do I do that? I can't just attach events directly to complex-element, as they're created and removed dynamically at runtime to match the view model. So, as I see it:
I can riddle my html with data-bind="click:..."s and data-bind="mouseenter:..."s but I would rather avoid this if I could. Maybe I'm too rooted in my old MVC ways, but adding open(), select(), or dragStart() functions and isOpen, isSelected or isDragging observable flags to my view model just makes a mess and my intuition tells me that as the app gets bigger that view model is going to become unmanageable. I'd rather keep my data and my presentation separate if possible.
Or I can use jquery delegation to attach events to something that stays fixed. Something like:
$("#container").on('click', '.complex-element button.open', function(e) {
var elem = $(e.target).parents('.complex-element');
...
});
But this wouldn't work as the app gets more complex because what happens if the container is itself wrapped in an element only shown on login (<div id="wrapper" data-bind="if:isLoggedIn">...</div>). I might as well just bind all events to the body and that's a recipe for disaster.
I found a very cool article on knockmeout.net (http://www.knockmeout.net/2011/07/another-look-at-custom-bindings-for.html) that advocates using custom knockout bindings to drive complex behavior, and this seems to be an awesome solution for widget-like behaviors like autocompletes or date-pickers, but what about just simple old fashioned controllers... would this work?
I guess, after all that, my question is pretty simple: Has anyone used Knockoutjs on a really large web application? And how did you go about it?
You could use a different jquery bind:
$(document).on('click', '#container>.complex-element button.open', function(e) {
var elem = $(e.target).parents('.complex-element');
...
});
This would work if #container does not exist.
There is some work going on in the community to make the knockout bindings unobtrusive and easy to write. For now, knockout offers you dataFor() which can be seen here: Using unobtrusive event handlers or you can use a little library like this: Introducing the Knockout.Unobtrusive Plugin
If you need to execute some arbitrary js on some rendered elements from the foreach you could use the afterAdd callback
<div id="container" data-bind="foreach: { data: containers,
afterAdd: somefunction }">
<li class="complex-element">...</li>
</div>
I have used KO on a really large web app and the way I went about managing complexity was
Split out viewmodels into separate using namespaces within a single master namespace.
Write custom bindings for complex reusable behavior.
Created entity model classes that encapsulated most of the important business logic, akin to backbone models.
Ensured that the viewModels were entirely view specific and view related (helped by point 3)
Kept all jquery selectors to a bare minimum, I feel dirty writing any global delegates but sometimes they are needed.
Hope this helps.

OOPs concepts into Javascript widget

A simple javascript widget design question.
I have started working in javascript fairly recently. And as part of my work, I intend to create a lot of reusable code; so for me javascript widgets seems like the best way to go.
However I am faced with a design dilemma, and I am not able to find the right answer.
I have a simple javascript widget, which alters the string in a html component. So my widget is somewhat like this:
(function() {
var convertString = function() {
$(".classForHtmlComponentsIWantToHandle").each(function(index, element) {
//js_fu stuff done with string fetched from "element"
});
};
var _somePrivateHelperMethod() {
//some work that would have been impossible without this helper method
};
GloballyDefinedNamespace.StringUtils = {convert : convertString};
}());
this allows me to call later
GloballyDefinedNamespace.StringUtils.convert();
Now if you would notice in my widget-ish function above, I extract all the HTML components from the DOM, that I want to alter string for. Interesting bit is that HTML will have span and divs with same css class and also textbox components with css class.
Both have a different way of extracting their value and setting new value back.
Based on my experience, if this is a widget to alter string then all it should care about is incoming object that "hold" string in a uniform manner and then based on a object expectations, my widget should be able to operate blindly.
"if it sounds like a duck, walks like a duck, then it's a duck". Kind of thing.
So effectively I would like to be able to NOT worry about distinguishing "element" object being a textbox or span in my widget. Instead wrap it into a generic wrapper.
However people have advised me that in javascript widgets the usual convention is to take care of component specific stuff within widgets. I am not convinced, as I firmly believe in programming to interfaces and not specifics. So I am at conflict.
In my example above, I don't think so the highlighted dilemma does justice to this problem, but on a larger scale this pops up as a question for me often.
I would like to hear opinion from guys, as to how to build that component independence within my widget for an HTML DOM? Is a solution to create javascript wrapper objects with same interface and then css-select them separately and then make method call as following?
(function() {
var locateAllComponents = function() {
$(".generic-class").each(function(index, element) {
//wrap element into suitable wrapper that has common interface for getter
//setter.
GloballyDefinedNamespace.StringUtils.convert(wrappedElement);
});
};
}())
Any insights and answers would be highly appreciated.
You are trying to force concepts on javascript that are foreign to it.
Programming to an interface works for for OOP, but for other language types it is a bad idea.
You can look at how jquery uses .text() (http://api.jquery.com/text/) and .val() (http://api.jquery.com/val/) and you will see that what you want to abstract they put into two different functions, for a reason.
Widgets can be useful, but at a cost of bloat and performance, so you need to look at what you are doing and ensure that you don't exact too much of a price.
So, start small, perhaps with a widget that can work on form elements, and see what you can do reasonably within the object, but you will find that at some point it becomes very helpful if either the API user or the API developer creates functions to pass in to your widget, to get the additional functionality.
But, all of this will impact performance, so as you work, do some unit tests so you can look at changes in performance to help guide you on what changes are worth the price.
You may want to start with looking at the JQuery UI widgets though, and see what they did, rather than just re-inventing the wheel for no reason.

Categories