I'm using Knockout.js for a number of things in a project.
Among other things it's used to build XML config for a third-party tool on the fly, which requires a very specific, strict markup.
This means, that all resulting markup needs to be without any data-bind attributes.
Currently I can achieve this by additionally binding
<Element data-bind="attr: {'data-bind': false}" />
or alternatively doing a separate, additional loop over the whole resulting markup, removing all data-bind attributes.
Neither of the solutions are too straightforward, the first one meaning very verbose templating, the second requires an additional pass over the whole result.
Does Knockout offer a better alternative to remove all data-bind attributes after bindings have been applied?
Maybe preprocessNode can help:
You can hook into Knockout’s logic for traversing the DOM by providing a node preprocessor. This is a function that Knockout will call once for each DOM node that it walks over, both when the UI is first bound, and later when any new DOM subtrees are injected (e.g., via a foreach binding).
The following will remove the data-bind attribute:
ko.bindingProvider.instance.preprocessNode = function(node) {
if (node.removeAttribute) {
setTimeout(function() {
node.removeAttribute('data-bind');
}, 0);
}
};
See http://jsfiddle.net/jfjbpbtq/
setTimeout is needed because knockout will read the data-bind attribute after calling preprocessNode.
Related
I know two ways to bind HTML to client-side JavaScript code and stay a kind of object-oriented:
Use a lot of IDs (or special CSS class names, or some other distinct HTML attributes) in HTML and do a "harvest" in JS initialization method (or request each DOM object each time, right before use);
Do not write HTML at all. Construct an element at runtime, in initializer, and remember a reference to DOM object (or jQuery object) in a variable.
Are there some other ways that allow to use design-time phase (writhing HTML) which is much more convenient than doing all the work at runtime, and at the same time do not use a lot of identifiers of any kind having to maintain their uniqueness?
AngularJS is the framework you want to use for 2-way data binding.
I used AngularJS for multiple projects now, combined with nodeJS, and I never looked back at jQuery, you keep your code clean with the MVC pattern and manipulating the DOM is made easy and clear.
Example for 2 way data binding:
HTML
<p>{{elementText}}</p>
<input type="text" ng-model="item.value" />
JavaScript/Controller
$scope.item = {
value: ''
};
$scope.elementText = "The text you want to display";
console.log($scope.item.value); //Directly get your values from the scope.
Want to assign values to <select> boxes or fill <table>'s using JSON data? No problem, AngularJS got you covered.
Interesting AngularJS features:
ng-model
ng-repeat
Animations
Custom directives
I hope this will help you!
I will be using knockoutjs in a single page application and I am concerned whether the observables remain in memory even if any DOM elements that the bindings were applied to, are removed.
What I think is that I will need to handle this in my application, by calling
ko.cleanNode(DOMElement)
on each DOM element that used observables, before removing them from the document.
I just need someone to confirm that this is the case
Thanks
CleanNode doesn't remove the observables, it just unbinds them from the UI elements. You'd also need to remove any references to your view models by setting them to null in order for them to be garbage collected, something like:
var myVM = new myViewModel();
ko.applyBindings(myVM, DOMElement);
//All your other stuff
ko.cleanNode(DOMElement);
myVM = null;
I've set up a project where I've extended a Backbone.Marionette.Layout that contains two different regions. This layout can be used as a component throughout the application. In particular, the regions are set up like the following.
regions : {
masterRegion : { selector: '[data-region=master]' },
slaveRegion: { selector: '[data-region=slave]' }
},
In particular, I'm using a data-region selector to inject the view I'm interested in.
When such a layout is used in a tree structure views are duplicated since getEl function adresses the wrong region to inject the view. Obviously it's my fault and within Marionette (v1.1.0) doc the following is written.
override the getEl function if we have a parentEl this must be
overridden to ensure the selector is found on the first use of the
region. if we try to assign the region's el to parentEl.find(selector)
in the object literal to build the region, the element will not be
guaranteed to be in the DOM already, and will cause problems
where getEl is defined as
getEl: function(selector){
return Marionette.$(selector);
}
So, my question is the following. What does this mean? How can I override this method? Where is the correct to perform such an override?
Hope it's clear.
Here's my understanding of this:
the points below apply to the case where the layout is contained within another element ("if we have a parentEl")
the first time you use a region, Marionette needs to select the proper DOM element to populate, according to the selector string ("ensure the selector is found on the first use of the region")
you can't simply look for the selector in the parentEl ("if we try to assign the region's el to parentEl.find(selector) in the object literal"), because the DOM element we want isn't necessarily in the DOM yet ("the element will not be guaranteed to be in the DOM already")
In other words, the first time you use a region (e.g. with a call to the show method), Marionette needs to build a region instance and associate it with the correct DOM element (specified by the selectorattribute).
However, before Marionette can look for the DOM element within the containing parent element, it must ensure that all required DOM elements (most importantly the one we're looking for) have loaded.
Does that make more sense to you?
Edit based on flexaddicted's comment.
Could you suggest me a the correct way to achieve this? Is there any
manner to override the method below?
I don't think you need to override this method. The comment indicates why the DOM element is fetched that way instead of by direct assignment when the region is built, but it should still work properly with a tree structure (since parents can still be determined properly).
I think the problem might be with your region selector: as it is "generic", it can potentially match multiple elements (as opposed to selecting with an id attribute that should match only 1 element), and could be matching a DOM element you're not expecting such as a child view. (This of course depends on when Marionette looks at the DOM to fetch the selector.)
Also, I'd consider using a composite view for your tree structure needs if possible. See http://davidsulc.com/blog/2013/02/03/tutorial-nested-views-using-backbone-marionettes-compositeview/ and http://lostechies.com/derickbailey/2012/04/05/composite-views-tree-structures-tables-and-more/
I've been using KendoUI and have been using they're command functions. However to call JS I must call named jS functions. No huge deal. When I use the "This" key word it brings back the entire grid and I mus find a value of a child from a sibling of the same parent elements and i wound up doing this ugly thing. The question I have is how can I turn this "thing" into something jqueryable readable and comprehensible
function AddRole(e) {
var $ParentNode = e.target.parentNode.parentNode.children[1].children[0].getAttribute("value", 0);
}
Sorry, but you have other problems.
If you rely on such a structure e.target.parentNode.parentNode.children[1].children[0], your Markup and JS do not scale at all.
Use the oppurtunity to create scalable and consistent code. Or at least, set some id, class or html5 data attribute on the children[0] element in order to identify it properly.
Say I have a (rather ridiculous) book model who attributes look something like:
page:{
paragraph:{
wordcount: {
the: 8,
at: 10
}
}
}
can I bind to change of very nested values like so?
book.on("change:page:paragraph:wordcount:the", ...);
Backbone only triggers change events for top-level attribute names. Per the Catalog of Events:
"change" (model, options) — when a model's attributes have changed.
"change:[attribute]" (model, value, options) — when a specific attribute has been updated.
There's no mention of any nesting there. In fact, if you do change a nested property of the attribute, I'm pretty sure it won't even trigger an event (since the attribute itself didn't change). Additionally, if you look at the annotated source (although the factors affecting this behavior are a bit split up) you can follow it down to where change:attribute events are triggered.
However, it does look like there's a plugin to help accomplish what you're after.