How to vue watch a specific property in an array of objects - object

I'm using vue.js 2.5.2
I have an array of objects and I'd like to watch forms[*].selected and if it changes call a function.
This is my attempt, but obviously, it is not correct. I tried putting the array into a for loop to watch each object's property selected.
watch: {
for (var i = 0; i < forms.length; i++)
{
forms[i].selected: function(){
console.log("change made to selection");
}
}
},
This is the array of objects called forms[]
forms: [
{
day: '12',
month: '9',
year: '2035',
colors: 'lightblue',//default colour in case none is chosen
selected: true
},
{
day: '28',
month: '01',
year: '2017',
colors: 'lightgreen',//default colour in case none is chosen
selected: true
}
],
Any help would be greatly appreciated,
Thanks

You could use a deep watcher, but a more elegant solution would be to create computed property of the data you want to watch, and watch that instead:
new Vue({
el: '#app',
data: () => ({
forms: [{
day: '12',
month: '9',
year: '2035',
colors: 'lightblue',
selected: true
},
{
day: '28',
month: '01',
year: '2017',
colors: 'lightgreen',
selected: true
}
],
}),
computed: {
selected() {
return this.forms.map(form => form.selected)
}
},
watch: {
selected(newValue) {
console.log("change made to selection")
}
}
})
<html>
<head>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="(form, i) in forms" :key="i">
<input type="checkbox" v-model="form.selected"> {{form.colors}}
</li>
</ul>
</div>
</body>
</html>

Vue Watchers
Using deep tag is a simpler option but be careful with lists with many objects. It is not very optimized
watch: {
colours: {
// This will let Vue know to look inside the array
deep: true,
// We have to move our method to a handler field
handler()
console.log('The list of colours has changed!');
}
}
}

Related

Vuetify Combobox add new item as object instead of string

I am building a new/edit item form using Vuetify(v2.6.3). My form has multiple combobox's that pull their items from a backend API as objects. I would like to be able to add new items, which the combobox is able to do. However, when I add a new item it's added as a string. Is there a way to add that new item as an object instead?
I only need the new object to have a name key in order to send it to the API to be created
Desired New item from v-combobox:
{
name: 'New Item',
}
Simplified list of items returned from API and available as combobox choices:
[
{
id: 1,
name: 'Item 1',
createdAt: '2020-01-01',
updatedAt: '2020-01-01'
},
{
id: 2,
name: 'Item 2',
createdAt: '2020-01-01',
updatedAt: '2020-01-01'
},
{
id: 3,
name: 'Item 3',
createdAt: '2020-01-01',
updatedAt: '2020-01-01'
}
]
Here is a simplified version of my Form:
<template>
<v-card>
<v-card-title>
<span class="text-h5">Edit Item</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col>
<v-combobox
v-model="item"
:items="items"
item-text="name"
item-value="id"
label="ComboBox"
return-object
></v-combobox>
</v-col>
</v-row>
</v-container>
</v-card-text>
</v-card>
</template>
<script>
export default {
name: 'TestForm',
data() {
return {
item: null,
items: [
{
id: 1,
name: 'Item 1',
createdAt: '2020-01-01',
updatedAt: '2020-01-01',
},
{
id: 2,
name: 'Item 2',
createdAt: '2020-01-01',
updatedAt: '2020-01-01',
},
{
id: 3,
name: 'Item 3',
createdAt: '2020-01-01',
updatedAt: '2020-01-01',
},
],
}
},
}
</script>
In my method which sends the form to the API, I could check the value of each combobox and convert to an object if needed, but I am wondering if it's possible to handle it in the combobox component.
VComboBox doesn't seem to support that, but you could workaround it by using the component's change-event to add a new object with the entry if needed:
Add a change-event handler (e.g., named onChange) on the v-combobox.
In onChange(), lookup the entry in items by name (i.e., corresponding to the VComboBox's item-text prop).
If found, set item to the existing item.
Otherwise, create a new object with that entry, and set item to the newly created object. Note the new object's id (i.e., corresponding to VComboBox's item-value prop) must be unique for VComboBox to create and track it.
<v-combobox
v-model="item"
#change="onChange" 1️⃣
/>
let nextId = 1
export default {
⋮
methods: {
addItem(name) {
const newItem = {
id: nextId++,
name,
}
this.items.push(newItem)
return newItem
},
1️⃣
onChange(entry) {
if (typeof entry === 'string' && entry.trim()) {
2️⃣
const item = this.items.find(item => item.name === entry)
if (item) {
3️⃣
this.item = item
} else {
4️⃣
this.item = this.addItem(entry)
}
}
},
},
}
demo
I found the accepted answer useful but here is a similar solution that allows for a multiple selection combo box using chips. I used this to implement a way for a user to select multiple "Tags":
Ex. Stackblitz runnable example
<v-combobox
v-model="selectedItems"
:items="items"
chips
clearable
multiple
#change="OnChange"
item-text="TagName"
item-value="TagId"
label="Categories"
solo
prepend-icon="mdi-tag-multiple"
return-object>
<template v-slot:selection="{ attrs, item, select, selected }">
<v-chip
v-bind="attrs"
:input-value="selected"
color="primary"
close
#click="select"
#click:close="RemoveTag(item)"
>
<span>{{ item.TagName }}</span>
</v-chip>
</template>
</v-combobox>
Here is the code that handles the Add/Removal of these tags:
private OnChange(tags: any) {
let tagsArray = tags as [];
tagsArray.forEach(function(tag: any) {
// New tags are added as type "string" so we need to convert this to a Tag
// Iterate through the selected Tags and for each "string" found convert it to a Tag
if (typeof tag === 'string' && tag.trim()) {
const item = this.selectedItems.find(item => item === tag);
if (item) {
let newTag = new Tag(0, tag);
// Remove the string based tag
const index = this.selectedItems.indexOf(tag, 0);
if (index > -1) {
this.selectedItems.splice(index, 1);
}
// Add a new tag object instead
this.selectedItems.push(newTag);
return;
}
}
}.bind(this))
}
private RemoveTag(tagToRemove: Tag) {
var indexOfItemToRemove = this.selectedItems.findIndex(x => x.TagName == tagToRemove.TagName);
this.selectedItems.splice(indexOfItemToRemove, 1);
}
It does not appear that a ComboBox can directly return an object when adding a new item. However it looks like you can leverage the input event of the component to run a method that converts to an object.
Here is how I call the method on the component's input event:
<v-combobox
v-model="item"
:items="items"
item-text="name"
item-value="id"
label="ComboBox"
return-object
#input="makeObject"
></v-combobox>
#input="makeObject" listens for the component's input event and calls the makeObject method and passes the currently selected item
Then I was able to write a method which checks if the combobox value is a string. If so then it converts it to an object
<script>
export default {
name: "TestForm",
data() {
return {
item: null
// Remaining code omitted for readability
}
}
methods: {
makeObject(val) {
if (typeof val === "string") {
this.item = {
name: val,
};
}
},
},
};
</script>
Here is my full component:
<template>
<v-card>
<v-card-title>
<span class="text-h5">Edit Item</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col>
<v-combobox
v-model="item"
:items="items"
item-text="name"
item-value="id"
label="ComboBox"
return-object
#input="makeObject"
></v-combobox>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-text>
{{ item }}
</v-card-text>
</v-card>
</template>
<script>
export default {
name: "TestForm",
data() {
return {
item: null,
items: [
{
id: 1,
name: "Item 1",
createdAt: "2020-01-01",
updatedAt: "2020-01-01",
},
{
id: 2,
name: "Item 2",
createdAt: "2020-01-01",
updatedAt: "2020-01-01",
},
{
id: 3,
name: "Item 3",
createdAt: "2020-01-01",
updatedAt: "2020-01-01",
},
],
};
},
methods: {
makeObject(val) {
if (typeof val === "string") {
this.item = {
name: val,
};
}
},
},
};
</script>
sandbox link

Laravel - Datatables export excel from filtered data

Hello i would like to export data from my datatable based on user filtered data here for example :
I have done export excel for all row but now i'm trying to export data based on filtered, here is my filtered function() in index.blade php:
$(".filterButton").on('click', function(){
tableMediaOrder.column(8).search($('.input-advertiser-filter').val()).draw();
tableMediaOrder.column(7).search($('.input-agency-filter').val()).draw();
tableMediaOrder.column(9).search($('.input-brand-filter').val()).draw();
});
i have tried to use formated Datatables example from Datatable example : Format Output Data, but i don't know how to put the export button and make it as a custom <a href=""> for the Excel export in image above, maybe someone can provide an example how to make it? thank you!.
EDIT :
here what is my input in index.blade.php :
<div class="col">
<button id="filterButton" class="btn btn-primary filterButton"><i class="fa fa-filter"></i> Filter</button>
<div class="dropdown d-inline">
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-file-excel-o"></i> Export
</button>
<!-- dropdown-menu -->
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton" id="export-choose">
<a class="dropdown-item export-link" id="export-filtered">Excel</a>
<a class="dropdown-item export-link" href="{{ route('media-order.export') }}" id="exportRaw">Excel Raw</a>
</div>
<!-- end dropdown-menu -->
<button class="btn btn-primary float-right" data-toggle="modal" data-target="#addMediaOrderModal" data-backdrop="static" data-keyboard="false" class="btn btn-primary">
<i class="fa fa-plus-square mr-1"></i> Add Order
</button>
</div><!-- dropdown -->
</div>
So far i've been trying to put the <a href="" id="export-filtered"> to act as an Export button, add it as an onClick="exportFiltered" function and throw it into the javascript but it doesn't work, here is my javascript :
$(".exportFiltered").on('click', function(e) {
$('.hiddenbuttons button').eq(0).click();
});
but sadly it doesn't work, and it just make the Excel export become blank
UPDATE : Data Table
here is my datatable :
'use strict';
var addMediaOrderSubmitButton = Ladda('#addMediaOrderSubmitButton');
var editMediaOrderSubmitButton = Ladda('#editMediaOrderSubmitButton');
var tableMediaOrder = dt('#dt-media-order','media_order',{
// dom: '<"hiddenbuttons"B>rtip',
processing: true,
serverside: true,
iDisplayLength: 100,
bFilter: true,
searchable: true,
exportOptions: {
rows: 'visible'
},
ajax: {
url: "{{ route('media-order.index') }}?dt=1",
data: function (d){
d.filter_order = $('#input-order-filter').val();
d.filter_agency = $('#input-agency-filter').val();
d.filter_advertiser = $('#input-advertiser-filter').val();
d.filter_brand = $('#input-brand-filter').val();
// d.filter_start = $('#input-start-date').val();
// d.filter_end = $('#input-end-date').val();
//d.filterButton = $('#filterButton').val();
},
},
columns: [
{
data: 'action',
name: 'action',
orderable: false,
sortable: false,
className: 'text-center'},
{data: 'nomor', name: 'nomor'},
{data: 'nomor_reference', name: 'nomor_reference'},
{data: 'periode_start',
name: 'periode_start',
render: function(data){
var date = new Date(data);
var month = date.getMonth() + 1;
return (month.toString().length > 1 ? month : "0" + month) + "/" + date.getDate() + "/" + date.getFullYear();
}
},
{
searchable: true,
data: 'periode_end',
name: 'periode_end',
render: function(date){
var date = new Date(date);
var month = date.getMonth() + 1;
return (month.toString().length > 1 ? month : "0" + month) + "/" + date.getDate() + "/" + date.getFullYear();
}
},
{
searchable: true,
data: 'category_id',
name: 'category_id',
render: function(data, type, row) {
switch (data) {
case '1':
return 'New Order';
break;
case '2':
return 'Additional Order';
break;
case '3':
return 'Cancel Order';
break;
case '4':
return 'Paid';
break;
case '5':
return 'Bonus';
break;
default:
return 'Null';
break;
}
}
},
{
searchable: true,
data: 'type_id',
name: 'type_id',
render: function(data, type, row) {
switch (data) {
case '1':
return 'Reguler';
break;
case '2':
return 'Reguler PIB';
break;
case '3':
return 'CPRP';
break;
case '4':
return 'Package';
break;
case '5':
return 'Sponsor';
break;
case '6':
return 'Blocking';
break;
default:
return 'Null';
break;
}
}
},
{
searchable: true,
data: 'agency_name',
name: 'agency_name'
},
{
searchable: true,
data: 'advertiser_name',
name: 'advertiser_name'
},
{
searchable: true,
data: 'brand_name',
name: 'brand_name'
},
{
searchable: true,
data: 'version_code',
name: 'version_code'
},
{
data: 'gross_value',
name: 'gross_value' ,
render: $.fn.dataTable.render.number( ',', '.', 2, 'Rp','' )
},
{
data: 'nett_budget',
name: 'nett_budget',
render: $.fn.dataTable.render.number( ',', '.', 2, 'Rp','' )
},
{
data: 'nett_cashback',
name: 'nett_cashback',
render: $.fn.dataTable.render.number( ',', '.', 2, 'Rp','' )
},
{
data: 'nett_bundling',
name: 'nett_bundling',
render: $.fn.dataTable.render.number( ',', '.', 2, 'Rp','' )
},
{data: 'spot', name: 'spot' },
{
searchable: true,
data: 'accountexecutive_name',
name: 'accountexecutive_name'
},
{
searchable: true,
data: 'userto_name',
name: 'userto_name'
},
{
searchable: true,
data: 'group_id',
name: 'group_id'
},
{data: 'notes', name: 'notes' },
{
searchable: true,
data: 'attachment_name',
name: 'attachment_name'
}
],
buttons: [
{ // this exports only filtered data
extend: 'excelHtml5',
exportOptions: {
modifier: { search: 'applied' }
}
},
{ // this exports all data regardless of filtering
extend: 'excelHtml5',
exportOptions: {
modifier: { search: 'none' }
}
}
],
initComplete: function(setting, json){
$('.hiddenbuttons').css('display','none');
},
rowCallback: function( row, data, index) {
if (data.isdisabled == 1){
$(row).css('background-color', 'rgba(255, 0, 0, 0.2)');
}
}
});
UPDATE 2 :
it turns out i forgot to add the :
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.3.1/js/dataTables.buttons.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.3.1/js/buttons.html5.min.js"></script>
And also is there a way to customize the column since the "Action" column are also being exported like this :
But sadly the custom export <a href="" id="export-filtered"> is still not working, thanks again.
UPDATE 3 :
After searching and tinkering, i've finally found my solution which is using :
var buttons = new $.fn.dataTable.Buttons(tableMediaOrder, {
buttons: [
{
extend: 'excelHtml5',
// "dom": {
// "button": {
// "tag": "button",
// "className" : "exportFiltered",
// }
// },
exportOptions: {
// rows: '"visible'
columns: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
modifier: { search: 'applied' }
}
}
]
}).container().appendTo($('#exportFiltered'));
And finally able to use the :
as a external link to export the excel.
You can delegate a DataTables export button to another external (non-DataTables) element.
The following example uses two different Excel Export buttons - one for a full export of all data, regardless of any filtering which has been applied, and the other to export only the filtered-in data:
buttons: [
// see https://datatables.net/reference/type/selector-modifier
{ // this exports only filtered data
extend: 'excelHtml5',
exportOptions: {
modifier: { search: 'applied' }
}
},
{ // this exports all data regardless of filtering
extend: 'excelHtml5',
exportOptions: {
modifier: { search: 'none' }
}
}
]
Then, we hide these buttons using the following:
dom: '<"hiddenbuttons"B>rtip'
and:
initComplete: function(settings, json) {
$('.hiddenbuttons').css('display', 'none');
}
These two DataTables export buttons can now be invoked from elsewhere - for example, based on the change event of a select list.
Here is the select list:
<select name="export" id="export">
<option value="noexport">-- select --</option>
<option value="filtered">Excel Filtered Data</option>
<option value="alldata">Excel All Data</option>
</select>
And here is the related event listener:
$("#export").on('change', function(e) {
var mode = $("#export :selected").val();
if (mode === 'filtered') {
$('.hiddenbuttons button').eq(0).click();
} else if (mode === 'alldata') {
$('.hiddenbuttons button').eq(1).click();
}
});
For reference, here is the full approach, as a self-contained web page:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.22/css/jquery.dataTables.css">
<link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/buttons/1.6.5/css/buttons.dataTables.min.css"/>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/2.5.0/jszip.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.6.5/js/dataTables.buttons.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.6.5/js/buttons.html5.min.js"></script>
</head>
<body>
<div style="margin: 20px;">
<input type="text" id="name" placeholder="Enter name">
<input type="text" id="office" placeholder="Enter office">
<button id="filterButton" type="button">Filter</button>
<select name="export" id="export">
<option value="noexport">-- select --</option>
<option value="filtered">Excel Filtered Data</option>
<option value="alldata">Excel All Data</option>
</select>
<table id="example" class="display dataTable cell-border" style="width:100%">
</table>
</div>
<script>
var dataSet = [
{
"id": "1",
"name": "Tiger Nixon",
"position": "System Architect",
"salary": "$320,800",
"start_date": "2011/04/25",
"office": "Zurich",
"extn": "5421"
},
{
"id": "2",
"name": "Garrett Winters",
"position": "Accountant",
"salary": "$170,750",
"start_date": "2011/07/25",
"office": "Tokyo",
"extn": "8422"
},
{
"id": "3",
"name": "Ashton Cox",
"position": "Junior Technical Author",
"salary": "$86,000",
"start_date": "2009/01/12",
"office": "San Francisco",
"extn": "1562"
},
{
"id": "4",
"name": "Cedric Kelly",
"position": "Senior Javascript Developer",
"salary": "$433,060",
"start_date": "2012/03/29",
"office": "Edinburgh",
"extn": "6224"
},
{
"id": "5",
"name": "Airi Satou",
"position": "Accountant",
"salary": "$162,700",
"start_date": "2008/11/28",
"office": "Tokyo",
"extn": "5407"
},
{
"id": "6",
"name": "Donna Snider",
"position": "Customer Support",
"salary": "$112,000",
"start_date": "2011/01/25",
"office": "New York",
"extn": "4226"
}
];
$(document).ready(function() {
var table = $('#example').DataTable( {
dom: '<"hiddenbuttons"B>rtip',
lengthMenu: [ [5, -1], [5, "All"] ],
data: dataSet,
columns: [
{ title: "ID", data: "id" },
{ title: "Name", data: "name" },
{ title: "Office", data: "office" },
{ title: "Position", data: "position" },
{ title: "Start date", data: "start_date" },
{ title: "Extn.", data: "extn" },
{ title: "Salary", data: "salary" }
],
buttons: [
// see https://datatables.net/reference/type/selector-modifier
{ // this exports only filtered data
extend: 'excelHtml5',
exportOptions: {
modifier: { search: 'applied' }
}
},
{ // this exports all data regardless of filtering
extend: 'excelHtml5',
exportOptions: {
modifier: { search: 'none' }
}
}
],
initComplete: function(settings, json) {
$('.hiddenbuttons').css('display', 'none');
}
} );
$("#filterButton").on('click', function() {
table.column(1).search($('#name').val()).draw();
table.column(2).search($('#office').val()).draw();
});
$("#export").on('change', function(e) {
var mode = $("#export :selected").val();
if (mode === 'filtered') {
$('.hiddenbuttons button').eq(0).click();
} else if (mode === 'alldata') {
$('.hiddenbuttons button').eq(1).click();
}
});
} );
</script>
</body>
</html>
Update
If you want to use a <a> link to generate an Excel export, then maybe this will help:
Let's assume we have a link like the one from your question:
Excel
To handle a click event for this, you can use the following:
$("#export-filtered").on('click', function(e) {
e.preventDefault();
$('.hiddenbuttons button').eq(0).click();
});
Note that the link's ID is export-filtered - therefore you need to refer to that in your JavaScript, using the # symbol (which is for an ID) - and not the . symbol (which is for a class name):
$("#export-filtered")
Then you need to prevent the default click action from being applied, because you do not want the click to cause you to navigate to another page. I recommend doing this even if you have href="".
That works for me, using my DataTables code.
In your question, you do not show how you changed your DataTables code - so this may still not work for you. If that is the case, then there must be other differences (which are not shown in the question) between my example and your overall solution.

How can I test css properties? My test always passes

My app is slightly unusual in that the appearance is actually critically important. It's sort of a photo manipulation app, so I want to be able to write tests to check, for example, that an element actually has a particular background color. I'm able to find elements with react-testing-library but my jest .toHaveStyle() assertions seem to always pass.
The element under test (rest of component omitted)
<span
className="swatch"
style={{ background: `#${colorToString(color)}` }}
aria-label="original color"
/>
The test
describe('BeadMapRow', () => {
let result: RenderResult;
beforeEach(() => {
const bead = {
brand: 'Test',
code: 'T01',
name: 'Test Bead 1',
color: 0x005080ff
};
result = render(<BeadMapRow color={0x084a8bff} bead={bead} />);
});
it('works', () => {
const e = result.getByLabelText('original color');
console.log(e);
expect(e).toHaveStyle({ 'background-color': '0x084a8bff' });
});
});
Result of that console.log (trimmed)
console.log src/components/beadMapRow.test.tsx:21
HTMLSpanElement {
'__reactInternalInstance$a7if3tsd8xg':
FiberNode {
tag: 5,
key: null,
elementType: 'span',
type: 'span',
stateNode: [Circular],
return:
FiberNode {
/* ... */
},
child: null,
sibling:
FiberNode {
/*...*/
},
index: 0,
ref: null,
pendingProps:
{ className: 'swatch',
style: [Object],
'aria-label': 'original color' },
memoizedProps:
{ className: 'swatch',
style: [Object],
'aria-label': 'original color' },
/* ... */
'__reactEventHandlers$a7if3tsd8xg':
{ className: 'swatch',
style: { background: '#084a8bff' },
'aria-label': 'original color' },
[Symbol(SameObject caches)]:
[Object: null prototype] {
style:
CSSStyleDeclaration {
_values: {},
_importants: {},
_length: 0,
_onChange: [Function] },
childNodes: NodeList {} } }
If I change the expected color to something else, it still passes. The only way so far I've found to make it fail is to say .toHaveStyle('background-color') without a value.
.toHaveStyle() works as intended when you pass colour value types different from the one you are passing, as you can see in this Codesandbox. So it must be something with how you are formatting you colour values.
I think that issue is with the name of the property used to compare the color in the expect, in the component you used the propety background not backgroud-color so try
expect(e).toHaveStyle({ 'background': '#0x084a8bff' });
instead of
expect(e).toHaveStyle({ 'background-color': '0x084a8bff' });
Regards.

Vuetify treeview search behavior

Hello and Happy new Year guys,
Again I ask about v-treeview search. When I do my filter, the behavior do not satisfy me.
I updated my version of vuetify to 1.4.0. And I'm using vue 2.5.15
https://codepen.io/anon/pen/PXeMmy?&editors=101
HTML
<div id="app">
<v-container grid-list-md>
<v-layout wrap>
<v-flex xs6>
<!-- Search Field -->
<v-text-field label="search" v-model="search" box>
</v-text-field>
<!-- Treeview -->
<v-treeview :items="filteredTree"
v-model="selected"
active-class="grey lighten-4 indigo--text"
item-key="name"
selected-color="blue"
selectable
hoverable>
</v-treeview>
</v-flex>
<v-flex xs6>
<v-chip v-for="(s , i) in selected" :key="i">
{{s}}
</v-chip>
</v-flex>
</v-layout>
</v-container>
</div>
JS :
new Vue({
el: '#app',
data(){
return{
search: '',
tree: [
{
id: 1,
name: 'Applications',
children: [
{ id: 2, name: 'Calendar' },
{ id: 3, name: 'Chrome' },
{ id: 4, name: 'Webstorm' }
]
},
{
id: 5,
name: 'Languages',
children: [
{ id: 6, name: 'English' },
{ id: 7, name: 'French' },
{ id: 8, name: 'Spannish' }
]
}
],
selected: []
}
},
computed:{
filteredTree: {
get: function() {
let regexp = new RegExp(this.search, "i")
return this.filterTree(this.tree, regexp) || []
},
},
},
methods: {
filterTree: function(tree, filter) {
if (!Array.isArray(tree)) return null
return JSON.parse(JSON.stringify(tree)).filter(function matchName(o) {
let temp;
if (o.name.match(filter)) {
return true;
}
if (!Array.isArray(o.children)) {
return false;
}
temp = o.children.filter(matchName);
if (temp.length) {
o.children = temp;
return true;
}
});
}
}
})
In this exemple when I search "Calen", only "Application -> Calendar" is visible. Until now, it's what I want.
But when I select Calendar, "Application" is also selected; and when I clear the filter, all the children of "Application" are selected too. And I'd like to select "Calendar" and when I clear I don't want its siblings to be selected.
Thank you for reading

Why is this "not defined"

I am using Express and an API to get some data which I then pass into my view. I can loop through that data and print it from within my EJS template, so I know It's there is some capacity. However when I try to use that data in a chart.js chart (all in the same template file) it says it is "not defined"... Why is this happening?
App.js:
app.get('/search', function(req, res) {
var query = req.query.search;
endPoint = 'https://api.iextrading.com/1.0/stock/' + query + '/chart/1d';
request(endPoint, function(error, response, body) {
if(!error && response.statusCode == 200) {
stockData = JSON.parse(body);
console.log(stockData);
res.render('search.ejs', {stockData : stockData});
} else {
console.log(error);
}
});
});
EJS Template file
<% stockData.forEach(function(minute) { %>
<canvas id="myChart" width="400" height="400"></canvas>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: [minute['minute']],
datasets: [{
label: '# of Votes',
data: minute['open'],
backgroundColor: [
'rgba(255, 99, 132, 0.2)'
]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
</script>
<% }) %>
EDIT
If I change it to be like this it then says that "stockData" is undefined:
<% stockData.forEach(function(minute) { %>
<canvas id="myChart" width="400" height="400"></canvas>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: [stockData['open']],
datasets: [{
label: '# of Votes',
data: stockData['open'],
backgroundColor: [
'rgba(255, 99, 132, 0.2)'
]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
</script>
<% }) %>
Your stockData in chart.js is generate by javascript on browser. But stockData that really hold value that not undefine is generate by Nodejs on backend. If you wanna use like this. First, you need to render your ejs page, then send an ajax to server, get the response data. Then use that data you just receive to draw your chart. Somethings like this:
axios.get('/search')
.then(function (response) {
let data = response.data;
new Chart(document.getElementById('line-chart'), {
type: 'line',
data: {
labels: [],
datasets: [{
data: [your_response_data_from_nodejs.open],
label: 'Blabla',
borderColor: '#52D0C4',
fill: false
}
]
},
options: {
title: {
display: true,
text: 'Blala '
}
}
});
})
.catch(function (error) {
throw new error;
});

Resources