Get rid of unneeded DIVs in my if binding knockout - data-binding

I have an observable array with the following structure (where type can be only text or img):
ko.observableArray([{
type: 'text',
info: 'Hello'
},{
type: 'img',
info: 'http://cdn.zendesk.com/images/documentation/apps/logo-small.png'
}]);
Depending on the type I want to output either image or a bunch of text. So I am using the if binding. The result looks the way I expected, but the problem is in the underlying html:
<div data-bind="foreach: elements">
<div data-bind="if: type == 'text'"><div data-bind="text: info">Hello</div> </div>
<div data-bind="if: type == 'img'"></div> // Do not want it to be here
<div data-bind="if: type == 'text'"></div> // Do not want it to be here
<div data-bind="if: type == 'img'"><img data-bind="attr: { 'src': info}" src="http://cdn.zendesk.com/images/documentation/apps/logo-small.png">
</div>
</div>
It keeps inserting empty <divs> if the if statement returns false.
When I tried to achieve what I wanted with putting if and text binding in the same element I got the following error:
Multiple bindings (if and text) are trying to control descendant
bindings of the same
How can I get rid of unneeded DIVs in my output html with the if binding?
If this is impossible to achieve with if-binding, is there a way to do this somehow else? Because if I will have not only type = 'text' or 'img' but also 'video' and a dozen of other things I will have all them empty sitting there just as an artifact.

If you don't need the extra divs you can use the containerless control flow syntax of the if binding, which is based on comment tags:
<div data-bind="foreach: elements">
<!-- ko if: type == 'text' -->
<div data-bind="text: info"></div>
<!-- /ko -->
<!-- ko if: type == 'img' -->
<img data-bind="attr: { 'src': info}" />
<!-- /ko -->
</div>
Demo JSFiddle.
And the generated DOM will look like:
no extra divs only a few extra comments.
To get rid of these comments you can use templates.

You cannot get rid of the extra divs. I use the if binding do what you're doing on a regular basis.
The error you're getting simply indicates that you have competing bindings, which is expected with if and visible bindings. Your if and visible bindings should always be one div higher, so to speak.
Below is a screenshot of my DOM using Google's dev tools. The web application is actually running, and I use the if binding to reveal the view the user has chosen.
The extra divs are simply an artifact of the if binding.
If you think about it, if the if binding were to disappear altogether, what would be left in the DOM to reconstitute it when the condition is satisfied and that portion of the view should be shown?
UPDATE
Upon reconsidering the template approach, you could push the logic into the viewmodel (vm), bind the name of the template to an observable on the vm, and then dynamically set the template based on that logic. But, the templates themselves are going to hang around in the DOM. So I don't think there's a net gain here.

Related

How to reference the element id in VUE :class

I have several divs, which I'm not going to put in v-for, but still need to unique reference them. For #click id attribute works well.
<div v-bind:class="setAreaStyle('CAR')" #click="setFocus($event)" id="CAR">
But is there a way I can use element id, without click event, as reference in called function, so setAreaStyle would received 'CAR' as argument?
<div v-bind:class="setAreaStyle(id)" id="CAR">
Use $event.target.id
<div :class="setAreaStyle($event.target.id)"
#click="setFocus($event)"
id="CAR"
>
I guess the id you'll use will come from the v-for item, no ? You can then use it in both places:
<div
v-for="item in items"
:key="item.id"
:class="setAreaStyle(item.id)"
:id="item.id"
#click="setFocus($event)"
>
Side note: the name setAreaStyle is strange (seems it will do some side effects), you should only get something here, and it is the class name => getClass would make more sense.
Or :style="getStyle(item.id)" if you want to add inline CSS

WCAG 2.0 (Level AA) - Input element error - No input on code

i have a strange problem with WCAG 2.0 (Level AA).
The error below
3.3 Input Assistance: Help users avoid and correct mistakes.
Success Criteria 3.3.2 Labels or Instructions (A)
Check 187: input element has more than one associated label.
Repair: Remove or modify the label elements so there is only one associated label for each form control.
Error Line 1443, Column 1:
<body>
<p name="gl_path" id="gl_path" class="hidden"><span id="thme_path">zemez1029</span></p>
<di ...()
4.1 Compatible: Maximize compatibility with current and future user agents, including assistive technologies.
Success Criteria 4.1.1 Parsing (A)
Check 185: id attribute is not unique.
Repair: Modify the id attribute value so it is unique.
Error Line 165, Column 1:
<body>
<p name="gl_path" id="gl_path" class="hidden"><span id="thme_path">zemez1029</span></p>
<di ...(search)
This is the point where the error appear. There is no input or label and i dont understand whto to do to fix it
<body>
<p id="gl_path" class="hidden">{{ theme_path }}</p>
<div id="page">
<div id="page-preloader" class="visible">
<div class="preloader">
<div class="squares">
</div>
</div>
</div>
<div class="ie-warning">
<a href="//windows.microsoft.com/en-us/internet-explorer/download-ie">
<img src="catalog/view/theme/{{ theme_path }}/image/warning_bar_0000_us.jpg" height="75" width="1170" alt="You are using an outdated browser. For a faster, safer browsing experience, upgrade for free today."/>
</a>
</div>
<header>
<div class="top-line">
{{ header_nav }}
</div>
<div class="mid-line">
{{ header_top }}
</div>
{% if navigation %}
<div id="stuck" class="navigation"><!-- -->
<div class="container">
{{ navigation }}
</div>
</div>
{% endif %}
</header>
Check 187: input element has more than one associated label. Repair:
Remove or modify the label elements so there is only one associated
label for each form control. Error Line 1443, Column 1:
There are only two ways I can think of to receive this error.
You have two labels with the same for="itemID" attribute.
You are using the aria-labelledby attribute and have pointed that to an id that is duplicated on the page.
Your source code may not have these items but the generated HTML almost certainly will.
Check 185: id attribute is not unique. Repair: Modify the id attribute
value so it is unique. Error Line 165, Column 1:
This error is self explanatory, the same id is repeated twice within your generated HTML.
It could be that your accessibility checker is giving you incorrect line numbers and column numbers so don't rely on those for reference (it tends to happen if you have dynamically loaded content via JavaScript).
Given the first error I would guess that you have an element that has the aria-labelledby attribute that is pointing to the id that is duplicated causing the second error.
I would also guess that this is being added dynamically via JavaScript for you to not have spotted it sooner so use the inspect right-click option to view different items until you find the duplicated id. Once you have found it see if the id text is repeated 3 times in the DOM (twice for the duplicate ID and once for the aria-labelledby attribute.)
So I would expect you to see something along the lines of
<div id="a1"></div>
....
<p id="a1"></p>
....
<input aria-labelledby="a1"/>
EDIT
After thinking about this the most likely cause is either an email sign-up or search box added by your theme.
A search box is the most likely be the culprit as most search boxes do not have labels and your theme is adding a aria-labelledby to aid accessibility.
It is likely going wrong because of a plugin for a responsive menu (a mobile menu) as a lot of these plugins duplicate the menu structure and ids as they are not well written.
I can reproduce the same error (in the same tool you are using) by inserting two label for the same element:
<!DOCTYPE html>
<html lang="en">
<head><title>ok</title>
</head>
<body>
<label for="input_id">say</label>
<label for="input_id">say</label>
<input id="input_id" value="text" type="text" />
</body>
</html>
In this case, you have to remove the second <label>
You also have to check that you have not the error "Check 185: id attribute is not unique." which should mean that the same element appears twice and that will cause the same error (187).
This can happen if you have the same plugin in multiple part of your page.

polymer slotted content and data binding

Today, I'd like to create an element that generated a list of "cards". On these cards there could be very different things according to which page it gets included. For instance, sometimes these cards contain a picture, sometimes there could be contact information (name, age, adress, phone...), sometimes it could contain only a video, etc...
So what my idea was to design a polymer element that handle the CSS, the ajax call to the datasource, the dom-repeat, and a <slot> (formerly known as <content>) which would include in this element the html template used to create the card content (picture, video, or contact card)
this is what I did so far:
Parent element:
<tiles-list id="tilesView" datas="[[datas]]">
<img src="https//lorempixel.com/200/130/people" />
<p>[[item.name]]</p>
<p>[[item.age]]</p>
<p>[[item.adress]]</p>
<p>[[item.phone]]</p>
</tiles-list>
{{datas}}is replaced by the URL for the ajax call
and in the child element:
<iron-ajax
auto
url="[[datas]]"
handle-as="json"
last-response="{{ajax}}"
on-response="log"></iron-ajax>
<div id="grid">
<template is="dom-repeat" items="[[ajax.data]]">
<div class="card gridCell">
<slot></slot>
</div>
</template>
</div>
But yeah, it doesn't work. All I get is a list with the right amount of cards, but only the first one contains a picture, but no data. So I guess the slot doesn't work like I'm trying to make it work, and the data binding cannot work this way either.
Anybody has a solution?
I think what you want to achive is a perfect case for the Templatizer.
Change your code to:
<tiles-list id="tilesView" datas="[[datas]]">
<template tile>
<img src="https//lorempixel.com/200/130/people" />
<p>[[item.name]]</p>
<p>[[item.age]]</p>
<p>[[item.adress]]</p>
<p>[[item.phone]]</p>
</template>
</tiles-list>
And then when your ajax request resolves do something like this:
var template = Polymer.dom(this).querySelector('template[tile]');
this.templatize(template);
ajax.data.forEach(function(item){
var instance = this.stamp(item);
Polymer.dom(this.$.grid).appendChild(instance.root);
});
This will create several instances of you template, no dom-repeat needed.

Knockout css bindings within a foreach

I'm trying to do some templating for the site I'm working on the make it more reusable. To do this I'm using knockout to help with data-binding to transfer info from a json fine.
However, I'm having quite a bit of trouble passing a css property object to a span element within my template.
My html template kind of looks like this
<div data-bind="cssProperties: properties, css: { hidden : EvalDisplay() == false }">
<p>
<!-- ko foreach: options -->
<label class="btn">
<input type="checkbox" />
**<span class='optionText' data-bind="cssProperties: $parent.properties, html: Value"></span>**
</label>
<!-- /ko -->
</p>
</div>
The span with the asterisks next to it is what's giving me trouble. If I move that outside of the foreach loop then it works fine and the property is added, but if I keep it inside that loop where I need it to be it's just not being applies.
Any help would be great. I am very new at knockout so I don't know all the cool little tricks yet.
And before you ask, yes the css property has to be in the json and not in a css sheet. It needs to be where non-technical people can access it to change it.

How do I bind a dynamic set of jQuery Mobile buttons using Knockout.js?

I'm using jQuery Mobile (jQM) and Knockout.js (ko) to develop an application. In this application, I need to generate a variable number of buttons that are defined by a constantly updating web service.
So, in my markup, I have:
<div id="answerPage-buttons" data-bind="foreach: buttonsLabels">
<button data-role="button" data-inline="true" data-theme="b" data-bind="text: text, click: $root.submitAnswer" />
</div>
buttonLabels is a list of short strings returned from the web service. It's defined as:
self.buttonLabels = ko.observableArray();
This all works fine when the buttons are not "jQM styled". However, when I style them using:
$("#answerPage-buttons").trigger("create");
problems arise during the update.
The issue seems to be that jQM wraps the buttons in a div (with a sibling span) to make them all nice and mobile looking. However, when the ko applies the updates via the bindings, it only removes the tags, leaving the surrounding stuff, and adds new button tags - which are then also styled by the jQM trigger call.
So, I end up with an ever-growing list of buttons - with only the last set being operational (as the previous ones are gutted by the removal of their button element, but all the styling remains).
I've managed to address this, I think, by placing the following call immediately after the observable is updated:
$("#answerPage-buttons div.ui-btn").remove();
However, my feeling is that there's probably a better approach. Is there?
I found a solution.
If I surround the buttons with a div, it seems to work - e.g.
<div id="answerPage-buttons" data-bind="foreach: buttonsLabels">
<div>
<button data-role="button" data-inline="true" data-theme="b" data-bind="text: text, click: $root.submitAnswer" />
</div>
</div>
I'm guessing this is because the markup added by jQM remains "inside" the markup replicated by ko. Without the div, jQM wraps the button tag, which was the immediate child of the tag that contains the ko foreach binding.

Resources