How to render a vue component with TR as the root element? - javascript

I don't have much experience with Vue and facing an issue with redering a component with root as TR element.
I read this in the docs https://v2.vuejs.org/v2/guide/components.html#DOM-Template-Parsing-Caveats and tried adding the component using is property, but that didn't help either.
Check the code below.
Vue.component('car',{
props: ['number']
})
new Vue({
el: "#app"
})
<div id="app">
<table>
<tr is='car' inline-template number='123'>
<tr>
<td>car no</td>
<td>{{number}}</td>
</tr>
</tr>
<tr is='car' inline-template number='456'>
<tr>
<td>car no</td>
<td>{{number}}</td>
</tr>
</tr>
</table>
</div>
This errors out:
Error compiling template:
Inline-template components must have exactly one child element.
How can I fix this? Appreciate the help.
Fiddle https://jsfiddle.net/8d65gvua/
Update ----
More findings
If i wrap my tr in a template it works
<tr is='car' inline-template number='456'>
<template>
<tr>
<td>car no</td>
<td>{{number}}</td>
</tr>
<template>
</tr>
I have no clue why. Shouldn't the issue with tr be fixed with using the is property? Why do we need to do this?

I've never used inline-template before, my guess is that you are using it to pass the number to your "inner template"? You can achieve the same thing by using slots. (Reference)
I did a test but using slots instead, which I think it's a better way to cater for passing "inner templates". Also given the below warning in the documentation with regards to inline-template I would opt for slots.
However, inline-template makes the scope of your templates harder to
reason about. As a best practice, prefer defining templates inside the
component using the template option or in a <template> element in a
.vue file.
Example
Vue.component('my-row', {
template: `
<tr>
<slot></slot>
</tr>
`
})
The above is a component to define a single table row. The <slot> element is basically a placeholder for any content you want to pass into this template.
Usage
<div id="app">
<table>
<tr
is="my-row"
v-for="todo in todos"
>
<td>{{ todo.text }}</td>
</tr>
</table>
</div>
What will happen here is that <td>{{ todo.text}}</td> will be placed instead of the <slot> element in the my-row component. You can have multiple <td> elements, whatever content you put in the my-row will appear instead of <slot>.
JS Fiddle: https://jsfiddle.net/x793nub6
PS: Slots!
I don't want to make the answer too long, but keep in mind that with slots you can have a super customisable components, of course this all depends on your needs. But I would suggest having a look at the documentation and trying out a few expirements.
Update - If you still want to use inline-template
I went through the JS Fiddle you shared once more and realised what's the issue you are having. Basically vue is saying that one child needs to be passed. I guess there's some mixup since there's a <tr> nested in another <tr>. To fix it wrap the inner <tr> with a <template> element.
From:
<tr is='car' inline-template number='123'>
<tr>
<td>car no</td>
<td>{{number}}</td>
</tr>
</tr>
To:
<tr is='car' inline-template number='456'
<template>
<tr>
<td>car no</td>
<td>{{number}}</td>
</tr>
</template>
</tr>

Related

Drilldown functionality with AngularJS using ng-repeat property and jQuery

I want to do the exact same thing as this Fiddle (The example is mine) but using angular. In the normal HTML I have a parent tr with its own child tr so when I run the example I can see
that the child records that are related to its parent tr
Then I start doing this in Angular but now Im confused because I'm using ng-repeat and a JSON structure to populate the data so now I only have this piece of code, I put this as the parent but I don't know how to deal with the "child":
<tbody>
<tr ng-repeat="d in category" class="parent">
<td class="expand"></td>
<td ng-bind="d.cat"></td>
<td ng-bind="d.LW$"></td>
<td ng-bind="d.LW"></td>
<td ng-bind="d.L4W"></td>
<td ng-bind="d.L13W"></td>
<td ng-bind="d.L52W"></td>
</tr>
</tbody>
Here is my code with the angular version: https://jsfiddle.net/228wkfej/

need advice on dust temmplate

I am completely new to dust (linkedin), just working on my first little template. After writing it the obvious (but long) way I thought of a way to optimize using an inline partial.
The long version looks like this:
{#parcours}<tr class="pcsel_pc" id="{id}">
<td class="pcsel_exp_btn"><a href="#" class="list{?exp}Hide{:else}Exp{/exp}Btn">
<span class="glyphicon glyphicon-{?exp}minus{:else}plus{/exp}"></span></a></td>
<td class="pcsel_col">{name}</td><td class="pcsel_col pcsel_num">{count}</td>
</tr>
{?exp}
{#variants}
<tr class="pcsel_var{?sel} pcsel_sel{/sel}" id="{id}" >
<td class="pcsel_col"> </td><td class="pcsel_var pcsel_col">{name}</td>
<td class="pcsel_col pcsel_num">{count}</td>
</tr>
{/variants}
{:else}
{#variants}
<tr class="pcsel_var pcsel_hide" id="{id}" >
<td class="pcsel_col"> </td><td class="pcsel_var pcsel_col">{name}</td>
<td class="pcsel_col pcsel_num">{count}</td>
</tr>
{/variants}
{/exp}
{/parcours}
Explanation:
I have a context parcours that contains an inner context variants. If the variable exp does not exist in the outer context, I want to use a class pcsel_hide in the inner context.
This solution works but the code for the inner context is contained twice which is kind of stupid. So I thought of a way to use an inline partial which is conditionally set in the outer context and used in the inner context:
{#parcours}<tr class="pcsel_pc" id="{id}">
<td class="pcsel_exp_btn"><a href="#" class="list{?exp}Hide{:else}Exp{/exp}Btn">
<span class="glyphicon glyphicon-{?exp}minus{:else}plus{/exp}"></span></a></td>
<td class="pcsel_col">{name}</td><td class="pcsel_col pcsel_num">{count}</td>
</tr>
{?exp}{<hide/}{:else}{<hide} pcsel_hide{/hide}{/exp}
{#variants}
<tr class="pcsel_var{+hide/}{?sel} pcsel_sel{/sel}" id="{id}" >
<td class="pcsel_col"> </td><td class="pcsel_var pcsel_col">{name}</td>
<td class="pcsel_col pcsel_num">{count}</td>
</tr>
{/variants}
{/parcours}
This version is nice and short, but it doesn't seem to do the job. I see the class pcsel_hide all the time even if the outer context contains exp and thus uses the correct classes.
Any ideas ?
This is because inline partials are statically evaluated before template rendering begins. The last version of an inline partial defined with the same name wins.
Inline partials cannot be conditionally evaluated like this. What you probably want instead is to use a logic helper like {#eq}.

Angular append directive template to table

I have situation when i need to repeat multiple tbody in one table, what im trying to do is to make every tbody directive and i want its template to append to table, but when im put the directive inside the table tag its put his content outside the table.
the cart draw directive:
return {
restrict : 'AE',
templateUrl: 'client/cart/views/cart-draw.html',
scope : {},
replace: true,
controller : controller
}
the tpl:
<tbody ng-repeat="draw in CartService.items.draws track by $index">
<tr>
<td>
//some content
</td>
</tr>
</tbody>
the html:
<table class="table">
<cart-draw></cart-draw>
</table>
here is the plunker, if you inspect element you will see the tbody is out of the table:
http://plnkr.co/edit/9wEGFE5K0w0ayp6qo8Lx?p=preview
That is happening because the <table> tag doesn't recognize your custom <cart-draw> element as a valid child.
I would modify like so: http://plnkr.co/edit/u88N76h5dvLAvR3C1kRs?p=preview
index.html
<table><tbody cart-draw></tbody></table>
cart-draw.html
<tbody ng-repeat="body in bodies">
<tr>
<td>
{{body}}
</td>
</tr>
</tbody>
app.js
$scope.bodies = ["hello1", "hello2", "hello3"];
This is a long pending issue in Angular's Github repo.
https://github.com/angular/angular.js/issues/1459
I also stumbled upon to this problem once (with SVG). It happens because before rendering the directive, the template is cross verified with HTML DTD and alone doesn't make sense (without tag) and so it doesn't work. Same applies to <tr> and <li>
There are many solutions which uses ng-transclude and link functions to wrap it in respective parent tag and then use it.
This is actually a known & strange issue when it comes to directives & <table>'s.
I believe it actually comes in as invalid HTML at first, causing it somehow appear outside of your <table> tag.
Try making cart-draw an attribute of a <tbody>:
<table>
<tbody cart-draw></tbody>
</table>
plunker Example
This will make it work as intended.

Finding an ng-repeat index?

I can do this in Angular.js:
<tr ng-repeat="cust in customers">
<td>
{{ cust.name }}
</td>
</tr>
Which would iterate through all the customers by putting each in a <tr> of its own.
But what if I wanted two customers in one <tr>? How would I accomplish that?
I normally do that by messing around with indexes and modulus values, but I'm not sure how to do that here.
It turns out this can be done without any custom filters or changing the format of your data, though it does require some extra markup. I thought this woudn't be possible at first as you can't use a span or anything similar within the table for your ng-repeat. This answer however, points out that you can use a tbody.
So it's just a case of using the ng-if directive (which renders html if the expression is true), the $index variable (provided by ng-repeat) and the $even variable (which is also provided by ng-repeat and is true when $index is even
I've created a demo in this Plunker
<div ng-controller="MainCtrl">
<table>
<tbody ng-repeat="cust in customers">
<tr ng-if="$even">
<td>{{customers[$index].text}}</td>
<td>{{customers[$index+1].text}}</td>
</tr>
</tbody>
</table>
</div>
This would of course only work if you have two columns, what if you have more? Well you can also put a full expression into ng-if rather than just a variable. So you can use modulus values like this:
<tbody ng-repeat="cust in customers">
<tr ng-if="($index % 3) == 0">
<td>{{customers[$index].text}}</td>
<td>{{customers[$index+1].text}}</td>
<td>{{customers[$index+2].text}}</td>
</tr>
</tbody>

cannot render a simple table with dust.js

I'm having trouble rendering a simple table using dust.js
This template:
<table>
{#hours}
<tr>
<td>{dayText}</td>
<td>{hoursText}</td>
</tr>
{/hours}
</table>
Outputs:
<table>
<tr>
<td></td>
<td></td>
</tr>
</table>
Whereas changing the template to an unordered list works just fine:
<ul>
{#hours}
<li>{dayText} {hoursText}</li>
{/hours}
</ul>
Here is the model:
{
hours: [
{dayText: "Mo-Fri ", hoursText: "11:00-22:00"},
{dayText:"Sat", hoursText: "12:00-22:00"},
{dayText:"Sun", hoursText: "12:00-21:00"}
]
}
The templates are compiled in browser using dust-full-1.1.0.js
I'm using the LinkedIn fork.
Have I found a bug or have I missed something?
I have found the cause of my problem. My template sources are loaded from the within the page:
$("#dust-templates").children().each(function() {
var compiled = dust.compile($(this).html(), $(this).attr('id'));
console.log(compiled);
dust.loadSource(compiled);
})
The problem is that the browser does not deliver the template html verbatim:
{#hours}
{/hours}
<table>
<tbody><tr>
<td>{dayText}</td><td>{hoursText}</td>
</tr></tbody>
</table>
So it is not strange that this does not work as expected. If I store the template as a javascript variable it works.

Categories

Resources