How to properly code foreach and style binding - javascript

I have prepared a small jsfiddle here: http://jsfiddle.net/zb8jwre6/
Basically, I have observable array of sliders, and each slider should have it's own observable array of segments, which contain some properties for CSS-binding in HTML.
My first problem is that I'am not sure which foreach bind should i use:
This one doesn't work for some reason:
<div data-bind "foreach: $root.sliders">
<p data-bind="text: day"></p>
</div>
This one works, but I am not sure in which cases should I use this one:
<!-- ko foreach: sliders-->
<p data-bind="text: day"></p>
<!-- /ko -->
My second problem is that I don't know how to apply wanted CSS stylings from segment observable array.
I have tried this:
<div class='slider-segment' data-bind= "style: {left: segment_left, width:
segment_width, backgroundColor: segment_color}"></div>
But this does not work. I think I need to make those properties also as observables, but I am not sure how to do this properly in ViewModel
I would like to know what foreach binding should I use. When can I use "comment" foreach bindng and when do I use normal one, and I would like to know how to rework my code, so I can bind CSS properties from segments observable array.
Thank you!

I've changed
self.segments = ko.observableArray([segments]);
with
self.segments = ko.observableArray(segments);
See:
http://jsfiddle.net/x4a8pkmu/
I would like to know what foreach binding should I use. When can I use
"comment" foreach bindng and when do I use normal one, and I would
like to know how to rework my code, so I can bind CSS properties from
segments observable array
The "comment" syntax is useful if you do not want a container element. For example:
<ul>
<!-- ko foreach: myList -->
<li data-bind="text: myProp"></li>
<!-- /ko -->
</ul>
produces the same effects as:
<ul data-bind="foreach: myList">
<li data-bind="text: myProp"></li>
</ul>

The point of making a variable an observable is if you are going to change these values based on user interaction/server response, and then updating the UI. If the values are never going to change then using an observable for the style properties isn't helpful.
There is a very small difference between the two foreach loops - 'Comment' foreach does not have a parent div tag around the repeating child tags, while the other one does. So the outputs would look like:
Comment foreach:
<p>MON</p>
<p>TUE</p>
<p>WED</p>
Div foreach:
<div>
<p>MON</p>
<p>TUE</p>
<p>WED</p>
</div>
The comment foreach is useful for cases like these:
<ul>
<li class="header">Header item</li>
<!-- ko foreach: myItems -->
<li>Item <span data-bind="text: $data"></span></li>
<!-- /ko -->
</ul>

Related

ko.js: Remove entry from observableArray from within

Let's assume an observableArray with some entries. I wan't to have a complex html element for each of them. I used components to reuse templates:
<!-- ko foreach: searchResults -->
<entry class="..." params="entry: $data"></entry>
<!-- /ko -->
Rendering these items works perfectly. The problem: There's a "x"-button in each of these items which should allow the user to delete the items. Is there a way to manipulate the array from withing the array? Like accessing the parent?
I could solve it with proper ko-scoping:
<entry class="..." params="entry: $data, removeItem: $parent.removeItem"></entry>

Acessing parent observableArray from child observableArray

I have an observableArray of posts. Each post should have its observableArray of comments, which i'm doing this way:
self.posts.subscribe(posts) {
ko.utils.arrayForEach(posts, function(post) {
post.comments = ko.observableArray()
})
}
So I have two foreachs:
<!-- ko foreach: posts -->
<div class="post">
...
<!-- ko forech: comments -->
<div class="comment">
<span class="delete_comment" data-bind="click: $root.deleteComment"></span>
</div>
<!-- /ko -->
<!-- /ko -->
In my viewmodel, the deleteComment function:
self.deleteComment = function(comment) {
//ajax..
// now i should remove this comment from the comments array
}
The problem here is that I just can't find a way to remove the comment from the comments array. I can't access the comments array from the viewmodel since it's dynamically created. I tried to bind the parent in the data-bind:
<span class="delete_comment" data-bind="click: $root.deleteComment.bind($parent)"></span>
But there's no difference, the first argument in deleteComment is still the comment object. How can I access the outer observableArray from inside deleteComment?
But there's no difference, the first argument in deleteComment is still the comment object.
Your bind changed what this is in the deleteComment function, not what the first argument is. If you don't need this for something else in deleteComment, you're good to go — use this.comments.
Or, of course, make the posts responsible for deleting their comments, rather than the root model.

Knockout 3.x: markup inside element referencing a named template is wiped out

In Knockout 2.x, one could write:
<div data-bind="template: { name: 'my-template', foreach: elements }">
<div class="placeholder"><- these are the array elements.</div>
</div>
This way, a placeholder element could be appended to the rendered elements.
But, as I see, Knockout 3.0 changed this behavior: "placeholder" markup is just wiped out of the container element.
Is this intentional? Any workarounds?
One solution that I can immediately think of is adding "afterRender" to the template definition, but this has (according to my experiments with KO 3.0) a weird glitch: it doesn't fire for empty "elements" array.
Thank you in advance for your answers.
In comparing similar usages of templates to that of using the foreach option, I could not get the placeholder markup to render except for the specific case identified. As such, I'd assume that might not have been an intended usage.
Since the foreach option to the template binding doesn't provide anything that can't be replicated, one option would be to move the elements into the data option of a regular template and then "manually" provide the foreach markup either within a "container" element or containerless.
Containerless option
<script type="text/html" id="containerless-regular-template">
<!-- ko foreach: $data -->
<div data-bind="text:$data"></div>
<!-- /ko -->
<div class="placeholder"><- these are the array elements.</div>
</script>
If you want to get a little more fancy due to more dynamic requirements for the "placeholder", you could nest another template call at the end of the foreach markup within a containerless if binding. The outermost container would be limited but the contents would be dynamic.
<script type="text/html" id="my-nested-template">
<!-- ko foreach: $data.foreach -->
<div data-bind="text:$data"></div>
<!-- /ko -->
<!-- ko if: $data.template -->
<div data-bind="template: { name: template.name, data: template.data }"></div>
<!--/ko -->
</script>
<div data-bind="template: { name: 'my-nested-template', data: {foreach: [], template: {name: 'another-template-name', data: {}}} }">
Fiddle

Exclude html element at ko.applyBindings

Here is a simplified version of the problem:
<div id="model-one-container" data-bind="css: {foo: someModelOneProperty}">
<div id="model-two-container" data-bind="text: someModelTwoProperty"></div>
<div data-bind="text: anotherModelOneProperty"></div>
</div>
.
ko.applyBindings(viewModelOne, document.getElementById("model-one-container"));
ko.applyBindings(viewModelTwo, document.getElementById("model-two-container"));
If I do that, knockout will complain that there isn't a "someModelTwoProperty" in viewModelOne, so I need to exclude the #model-two-container div from the first applyBindings.
Is there any way to do this without altering the view-models?
Here's the answer.
Since knockout 2.0 there is a controlsDescendantBindings flag which you can use to create a custom binding that then stops KO from binding on an element or any of its children.

Displaying conditional html with Knockout

I have an knockout observable array of activities which contains audits and comments. I've got the data from the server and sorted the array of activities based on the timestamp of the objects.
I'd like to be able to conditionally display html based on the type, so audits and comments will look different.
<!-- ko foreach: activities -->
<div class="audit" data-bind="visible: {activity is typeof Audit}">
#*Do some audit html*#
</div>
<div class="comment" data-bind="visible: {activity is typeof Comment}">
#*Do some comment html*#
</div>
<!-- /ko -->
I've got the following html but I don't know how do the condition, I just wrote something in above as a placeholder so you get the idea of what I'm trying to achieve.
I'm probably approaching this all wrong, any help much appreciated!
Nayjest's solution should work if you change the visible binding to an if binding - that way it won't try render the parts with the title dependency.
A better solution, however, is probably to have two templates and execute them based on the type. You could have a method on the VM that takes $data and returns, for example, 'auditTemplate' or 'commentTemplate' depending on the result of something like $data instanceof Audit. You would then have two templates embedded as script tags with those ids:
<script id="auditTemplate" type="text/html">
<div class="audit">
<!-- Do some audit stuff -->
</div>
</script>
<script id="commentTemplate" type="text/html">
<div class="comment">
<!-- Do some comment stuff -->
</div>
</script>
And then in your VM, you'd have something like:
this.getTemplate = function(data) {
return (data instanceof Audit) ? 'auditTemplate' : 'commentTemplate'
}
In your page's html you'd do something like:
<!-- ko foreach: activities -->
<div databind="template: {name:$parent.getTemplate($data), data: $data}"></div>
<!-- /ko -->
If you have class Audit that is visible in global scope and property 'activities' of view model, try something like this:
<div data-bind="foreach: activities">
<div data-bind="visible: $data instanceof Audit">
<h1 data-bind="text: $data.title"></h1>
<!-- Some other data here -->
</div>
</div>

Categories

Resources