Vue Js v-bind:class syntax not working ? - javascript

i am new in VueJs and i am having that little problem, first here is my sample HTML code :
<div class="search">
<input :class="{ longwidth : isActive }" v-show="showInput" type="text">
<img #click="hideImgShowInput" v-show="hideImg" src="Assets/images/search-icon.png">
</div>
i have followed the documentation exactly, and i am using PHPStorm as editor, but my function that changes the 'isActive' variable is not working i am having this error:
Attribute :class is not allowed here
Any help would be much appreciated.

That sounds like a PHPStorm warning. Ignore it, or try a Vue-aware editor like vs code or atom. Your code looks fine to me.

This error causes the component to not render
My case is similar to the following
<script type="text/x-template" id="game-row">
<tr>
<td>{{ title }}</td>
...
</tr>
</script>
This way when adding the class attribute to tr element, causes the mentioned message
<script type="text/x-template" id="game-row">
<tr :class="{ active: isActive }">
<td>{{ title }}</td>
...
</tr>
</script>
The way to fix it was to pass the class attribute in the custom component call, as follows
<script type="text/x-template" id="game">
<div>
<p v-if="isLoading">Loading...</p>
<template v-else>
<table>
<caption>Game {{ title }}</caption>
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr
is="game-day-row"
v-for="game of games"
:class="{ 'active': isActive }" <!-- Here I set the class -->
:key="game.id"
:game="game"
></tr>
</tbody>
</table>
</template>
</div>
</script>
You can read about it in the vue class and style guide
https://v2.vuejs.org/v2/guide/class-and-style.html#With-Components
In the html output the class active is added to the tr element

Related

Vue.js not rendering table data [duplicate]

I'm struggling to develop a simple component and use it inside a loop:
<template id="measurement">
<tr class="d-flex">
</tr>
</template>
Vue.component('measurement', {
template: '#measurement',
props: {
name: String,
data: Object,
val: String,
},
});
This is obviously not functional yet but already fails:
<table v-for="(m, idx) in sortedMeters">
<measurement v-bind:data="m"></measurement>
</table>
gives ReferenceError: Can't find variable: m inside view. For a strange reason the same thing works, i.e. without error, in a paragraph:
<p v-for="(m, idx) in sortedMeters">
<measurement v-bind:data="m"></measurement>
</p>
What causes the variable to be not found?
PS.: here's a fiddle: https://jsfiddle.net/andig2/u47gh3w1/. It shows a different error as soon as the table is included.
Update It is intended that the loop produces multiple tables. Rows per table will be created by multiple measurements
TLDR: Before Vue is passed the DOM template, the browser is hoisting <measurement v-bind:name="i" v-bind:data="m"> outside the <table> (outside v-for context), leading to the errors in Vue. This is a known caveat of DOM template parsing.
The HTML spec requires the <table> contain only specific child elements:
<caption>
<colgroup>
<thead>
<tbody>
<tr>
<tfoot>
<script> or <template> intermixed with above
Similarly, the content model of <tr> is:
<td>
<th>
<script> or <template> intermixed with above
The DOM parser of compliant browsers automatically hoists disallowed elements – such as <measurement> – outside the table. This happens before the scripting stage (before Vue even gets to see it).
For instance, this markup:
<table>
<tr v-for="(m,i) in obj">
<measurement v-bind:name="i" v-bind:data="m"></measurement>
</tr>
</table>
...becomes this after DOM parsing (before any scripting):
<measurement v-bind:name="i" v-bind:data="m"></measurement> <!-- hoisted outside v-for -->
<table>
<tr v-for="(m,i) in obj">
</tr>
</table>
Notice how i and m are then outside the context of the v-for loop, which results in Vue runtime errors about i and m not defined (unless by chance your component coincidentally declared them already). m was intended to be bound to <measurement>'s data prop, but since that failed, data is simply its initial value (also undefined), causing the rendering of {{data.value}} to fail with Error in render: "TypeError: Cannot read property 'value' of undefined".
To demonstrate hoisting without these runtime errors and without Vue, run the code snippet below:
<table style="border: solid green">
<tr>
<div>1. hoisted outside</div>
<td>3. inside table</td>
2. also hoisted outside
</tr>
</table>
...then inspect the result in your browser's DevTools, which should look like this:
<div>1. hoisted outside</div>
2. also hoisted outside
<table style="border: solid green">
<tr>
<td>3. inside table</td>
</tr>
</table>
Solution 1: Use <tr is="measurement">
If you prefer DOM templates, you could use the is attribute on a <tr> to specify measurement as the type (as suggested by the Vue docs and by another answer). This first requires the <measurement> template use <td> or <th> as a container element inside <tr> to be valid HTML:
<template id="measurement">
<tr>
<td>{{name}} -> {{data.value}}</td>
</tr>
</template>
<div id="app">
<table v-for="(m,i) in sortedMeters">
<tr is="measurement" v-bind:name="i" v-bind:data="m" v-bind:key="i"></tr>
</table>
</div>
Vue.component('measurement', {
template: '#measurement',
props: {
name: String,
data: Object
}
})
new Vue({
el: '#app',
data: {
sortedMeters: {
apple: {value: 100},
banana: {value: 200}
},
}
})
<script src="https://unpkg.com/vue#2.6.11"></script>
<template id="measurement">
<tr>
<td>{{name}} -> {{data.value}}</td>
</tr>
</template>
<div id="app">
<table v-for="(m,i) in sortedMeters">
<tr is="measurement" v-bind:name="i" v-bind:data="m" v-bind:key="i"></tr>
</table>
</div>
Solution 2: Wrap <table> in component
If you prefer DOM templates, you could use a wrapper component for <table>, which would be able to contain <measurement> without the hoisting caveat.
Vue.component('my-table', {
template: `<table><slot/></table>`
})
<div id="app">
<my-table v-for="(m, i) in sortedMeters">
<measurement v-bind:name="i" v-bind:data="m"></measurement>
</my-table>
</div>
Vue.component('measurement', {
template: '#measurement',
props: {
name: String,
data: Object
}
})
Vue.component('my-table', {
template: `<table><slot/></table>`
})
new Vue({
el: '#app',
data: {
sortedMeters: {
apple: {value: 100},
banana: {value: 200}
},
}
})
<script src="https://unpkg.com/vue#2.6.11"></script>
<template id="measurement">
<tr>
<td>{{name}} -> {{data.value}}</td>
</tr>
</template>
<div id="app">
<my-table v-for="(m, i) in sortedMeters">
<measurement v-bind:name="i" v-bind:data="m"></measurement>
</my-table>
</div>
Solution 3: Move <table> markup into template string
You could move the entire <table> into a component's template string, where the DOM template caveats could be avoided. Similarly, you could move the <table> into a single file component, but I assume you have a significant need for DOM templates instead.
Vue.component('my-table', {
template: `<div>
<table v-for="(m, idx) in sortedMeters">
<measurement v-bind:data="m"></measurement>
</table>
</div>`,
props: {
sortedMeters: Object
}
})
<div id="app">
<my-table v-bind:sorted-meters="sortedMeters"></my-table>
</div>
Vue.component('measurement', {
template: '#measurement',
props: {
name: String,
data: Object
}
})
Vue.component('my-table', {
template: `<div>
<table v-for="(m,i) in sortedMeters">
<measurement v-bind:name="i" v-bind:data="m" v-bind:key="i"></measurement>
</table>
</div>`,
props: {
sortedMeters: Object
}
})
new Vue({
el: '#app',
data: {
sortedMeters: {
apple: {value: 100},
banana: {value: 200}
},
}
})
<script src="https://unpkg.com/vue#2.6.11"></script>
<template id="measurement">
<tr>
<td>{{name}} -> {{data.value}}</td>
</tr>
</template>
<div id="app">
<my-table :sorted-meters="sortedMeters"></my-table>
</div>
If you replace
<table v-for="(m, idx) in sortedMeters">
<measurement v-bind:data="m"></measurement>
</table>
with
<template v-for="(m, idx) in sortedMeters">
<table>
<measurement v-bind:data="m"></measurement>
</table>
</template>
You'll end up with working code.
But you'll most likely want to use
<table>
<template v-for="(m, idx) in sortedMeters">
<measurement v-bind:data="m"></measurement>
</template>
</table>
or
<table>
<measurement v-for="(m, idx) in sortedMeters" v-bind:data="m"></measurement>
</table>
It's because you've missing <td> inside <tr>. Without it your component produces invalid html markup and extracts "slot" data outside <tr> causing error.
Your template should looks like:
<template id="measurement">
<tr>
<td>{{name}} -> {{data.value}}</td>
</tr>
</template>
You also need to move v-for to measurement:
<table border="1">
<tr is="measurement" v-for="(m,index) in obj" :key="index" v-bind:name="index" v-bind:data="m"></measurement>
</table>
You can use is attribute to set component name.
Working fiddle: https://jsfiddle.net/cyaj0ukh/

my select2 jquery only work for the first form

i want to use select2.min.js to auto-complete the choices (ForeignKey values) , but it only work for my first form , i used django formset for duplicate forms
this is my snippet
<tbody class="tbody tb1 " id="form_set">
{% for item in items.forms %}
<tr class="p-0 col-12">
<td class="">
<div class="col-12 p-0 mt-3 inp">
<input class="col-12 0qarz qarz" type="number" name="" placeholder="qarz">
</div>
</td>
<td class="">
<div class="col-12 p-0 mt-3 inp">
{{item.price | add_class:'col-12 '}}
</div>
</td>
<td class="">
<div class="col-12 p-0 mt-3 inp">
{{item.quantity | add_class:'col-12 '}}
</div>
</td>
<td class="">
<div class="col-12 p-0 mt-3 inp">
{{item.model | add_class:'col-12 0model model' | attr:'id:model'}}
</div>
</td>
</tr>
{% endfor %}
</tbody>
<script type="text/javascript">
$(function(){
$('.tb1 tr:last').formset({
prefix:'{{items.prefix}}',
addText:'add',
deleteText:'remove',
addCssClass:'btn btn-success',
});
})
</script>
<script type="text/javascript">
$(document).ready(function(){
$("#model").select2()
})
</script>
but the select2 only work for my first form then doesnt have any effect on other forms ! and how to set number of forms to add_class it will help to solve maybe?
thanks
First of all I would love to see a little bit more, for example how you actually define your formset. It is not also clear to me what are you trying to do here. Please paste more data.
I would suggest that you think about using django-select2 module that helps a lot with handling select2 stuff in django.
I am also not sure what you mean by "how to set number of forms", maybe you wish to include some incremental counter that can be done with {{ forloop }} inside for/endfor loop?
Please paste more stuff and answer will be better.
The selector you are using to initialize select2 #model is for element ids, which should be unique for each element in the DOM.
In most browsers the effect will be that only the first instance of an element id will be recognized, and the rest ignored as if they don't exist.
In this instance you want to use a class selector: .model. This will ensure select2 is initialized for all elements that have the class "model". So the code to initialize select2 would be:
<script type="text/javascript">
$(document).ready(function(){
$(".model").select2()
})
</script>
You have to reinitialize(like this way: $("#model").select2();) the select2 for other pages when they appear.
You should need separately initialize with different ids.
for example:
<script type="text/javascript">
$(document).ready(function(){
$("#id_1").select2();
$("#id_2").select2();
})
</script>
the way I found is sending the number of forms through context then apply for loop in the template.
views.py
get_context_data()
context.update({
"accessoryNum": len(StoreRequestAccessory.objects.filter(storeRequestId=self.object.pk)),
"oneDimensionalItemNum":len(StoreRequestOneDimensionalItem.objects.filter(storeRequestId=self.object.pk)),
"twoDimensionalItemNum":len(StoreRequestTwoDimensionalItem.objects.filter(storeRequestId=self.object.pk)),
})
template.html
{% block javascripts %}
<script>
{% comment %} get accessoryNum from context {% endcomment %}
var accessoryNum = {{accessoryNum}};
$(document).ready(function(){
for(let i = 0; i <=accessoryNum; i++){
$(`#id_storereq_accessory_form-${i}-accessoryId`).select2({
placeholder: "Select a Item",
allowClear: true
});
}
});
</script>
{% endblock javascripts %}

Dojo Template for loop not looping inside html table

I'm using a dojo widget to display some data through a dojo Template (which uses django templates). When using a for loop inside of an html element, the loop only executes once and is unable to access the currently looping variable. However, the same loop outside of a table is able to loop as expected.
I'm not sure why this {% for %} loop will not work inside a element but it works outside.
I've tried including "dojo/dom-construct", and have "dojox/dtl/tag/logic" included in my widget. My widget is currently defined as follows:
define([
"dojo/_base/declare",
"dijit/_WidgetBase",
"dijit/_OnDijitClickMixin",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dojo/text!views/siteInfo/siteBatteries.html",
"dijit/registry",
"dojo/dom",
"dojox/dtl/_DomTemplated",
"dojox/dtl/tag/logic",
"dojo/dom-construct",
],function(declare, _WidgetBase, _OnDijitClickMixin, _TemplatedMixin, _WidgetsInTemplateMixin, template, registry, dom, _DomTemplated){
return declare([_WidgetBase, _OnDijitClickMixin, _TemplatedMixin, _WidgetsInTemplateMixin, _DomTemplated], {
// WidgetLogic
});
});
Template:
<div class="container" id="SiteOverviewController">
{{ batteryList.length }}
<table>
<thead>
<tr>
<th>ObjectId</th>
</tr>
</thead>
<tbody>
{% for battery in batteryList %}
<tr>
<th>{{ battery.attributes.OBJECTID }}</th>
</tr>
{% endfor %}
</tbody>
</table>
END TABLE
START DIV
{% for battery in batteryList %}
<div>{{ battery.attributes.OBJECTID }}</div>
{% endfor %}
</div>
The output of the template above is as follows:
<div class="container" id="SiteBatteryController" widgetid="SiteBatteryController" style="">
4
<table style="">
<thead style="">
<tr style="">
<th style="">ObjectId</th>
</tr>
</thead>
<tbody style="">
<tr style="">
<th style=""></th>
</tr>
</tbody>
</table>
END TABLE
START DIV
<div style="">2225</div>
<div style="">2226</div>
<div style="">2227</div>
<div style="">2228</div>
</div>
From the output you can see that the table only has one row with empty output:
<tr style=""><th style=""></th></tr> and loops only once when it should be looping 4 times (as seen with the elements) and have data.
I think this is a bug in dojo. I created a github issue in dojox for this, see it here

Can't change binding values of custom element in code behind

I am trying to create a Custom Element that allows me to collapse itself from a simple click delegate, but it doesn't seem to work.
I have this code in my js file
import {inject, bindable, bindingMode} from 'aurelia-framework';
export class DataGridCustomElement {
#bindable({ defaultBindingMode: bindingMode.oneTime }) columns = [];
#bindable({ defaultBindingMode: bindingMode.oneTime }) items = [];
#bindable() collpased = true;
collapseClick() {
this.collapsed = !this.collpased;
}
}
And here is my HTML file
<template>
<require from='./data-grid.css'></require>
<div class="collapse-arrow" click.delegate="collapseClick()">
<span class="collapse-icon glyphicon ${collapsed ? 'glyphicon-plus' : 'glyphicon-minus'}" aria-hidden="true"></span>
<span>Order Lines</span>
</div>
<div class="collapse-block" css="${collapsed ? 'display:none;' : 'display:block;'}">
<table class="data-grid">
<thead>
<tr>
<td repeat.for="column of columns">
${column.title}
</td>
</tr>
</thead>
<tbody>
<tr repeat.for="item of items">
<td repeat.for="column of columns">
${item[column.propertyName]}
</td>
</tr>
</tbody>
</table>
</div>
</template>
The crazy thing is it just doesn't seem to at all. It shows collapsed as being false from the get go, even though I set it to true in the class.
I am calling it like so
<data-grid columns.bind="invoiceColumns" items.bind="lineData"></data-grid>
Any ideas? Am I missing something about Custom Elements?
Easy solution. You have a typo in this.collapsed = !this.collpased;.

Polymer content insertion points?

I am trying to build a grid element, but am having some issues with a content insertion points. Here's my element with content insertion points:
<template>
<content select="[data-guerilla-grid-service]"></content>
<table id="guerillaGrid">
<thead>
<tr>
<template is="dom-repeat" items="{{columns}}" as="column">
<th style$="{{getColumnStyle(column)}}" data-sort-field$="{{column.sortField}}" on-click="sortClick"><span>{{column.header}}</span></th>
</template>
</tr>
</thead>
<content select="[data-guerilla-grid-items]"></content>
</table>
And here's where I'm using it:
<template>
<guerilla-grid id="feedbackGrid">
<muted-feedback-service id="service" items="{{items}}" data-guerilla-grid-service on-load-finished="serviceLoadFinished" sort-field="Version"></muted-feedback-service>
<tbody data-guerilla-grid-items>
<template is="dom-repeat" items="{{items}}">
<tr>
<td>{{item.formattedTimestamp}}</td>
<td>{{item.hash}}</td>
<td>{{item.version}}</td>
<td>{{item.serialNumber}}</td>
</tr>
</template>
</tbody>
</guerilla-grid>
Am I doing this right?
Content selector does not appear to work with tag. Switched to a doing a table with divs using css display attribute table options.

Categories

Resources