Print data with NodeJS hbs package access [object Object] - node.js

Trying to print data which is stored in lists on a NodeJS server on the front end using the hbs view package. Using Express and hbs.
This is what the code looks like on the server:
app.get("/", function(request, response){
response.render("index", {list: list});
});
And on the front end:
{{{body}}}
{{#each list}}
<p>{{ x }}</p>
{{/each}}
Data is stored like:
[{ x:
{ type: 'uri',
value: 'http://example.org/hospital/Basildon_University_Hospital' },
name: { type: 'literal', value: 'Basildon_University_Hospital' },
city: { type: 'literal', value: 'Basildon' },
county: { type: 'literal', value: 'Essex' },
email: { type: 'literal', value: 'pals#btuh.nhs.uk' },
phone: { type: 'literal', value: '01268_524900' },
lat: { type: 'literal', value: '51.557685852050781' },
long: { type: 'literal', value: '0.45057165622711182' } }]
When trying to output the property x, all I get is [object Object] printed, same goes for any other property in the data. Tried using list.x but then I get nothing printed at all.
Also have other data stored like:
[ { Concept:
{ type: 'uri',
value: 'http://www.openlinksw.com/schemas/virtrdf#QuadMapFormat' } } ]
Not even printing [object Object]
What can I do to get the values printed and access the objects and lists with hbs?

Don't forget to add the below on server side
var Handlebars = require('hbs');
Handlebars.registerHelper('json', function(context) {
return JSON.stringify(context);
});
And then
app.get("/", function(request, response){
response.render("index", {list: list});
});
then Use this
{{{body}}}
{{#each list}}
<p>{{ json x }}</p>
{{/each}}
Working example
jsfiddle

Related

Content in tab does not show in Shopware 6

I have added a custom tab on my module’s details page, however, the tab is rendered but the content for the tab isn’t showing. There isn’t any error in the console log as well.
index.js
import './page/wt-shopfinder-list';
import './page/wt-shopfinder-detail';
import './view/wt-shopfinder-detail-base';
import './view/wt-shopfinder-detail-review';
const { Module } = Shopware;
Module.register('example-shopfinder', {
type: 'plugin',
name: 'ExampleShopFinder',
title: 'example-shopfinder.general.mainMenuItemGeneral',
description: 'example-shopfinder.general.descriptionTextModule',
version: '1.0.0',
targetVersion: '1.0.0',
color: '#9AA8B5',
icon: 'default-shopping-paper-bag',
entity:'wt_shop_finder',
routes: {
index: {
components: {
default: "wt-shopfinder-list"
},
path: 'index',
},
detail: {
component: 'wt-shopfinder-detail',
path: 'detail/:id',
redirect: {
name: 'example.shopfinder.detail.base',
},
children: {
base: {
component: 'wt-shopfinder-detail-base',
path: 'base',
meta: {
parentPath: 'example.shopfinder.index'
},
},
review: {
component: 'wt-shopfinder-detail-review',
path: 'review',
meta: {
parentPath: 'example.shopfinder.index'
},
},
},
meta: {
appSystem: {
view: 'detail',
},
},
props: {
default(route) {
return {
shopFinderId: route.params.id,
};
},
},
}
},
navigation: [{
id: 'wt-shopfinder',
label: 'example-shopfinder.menu.mainMenuItemGeneral',
color: '#ff3d58',
icon: 'default-shopping-paper-bag-product',
path: 'example.shopfinder.index',
parent: "sw-marketing",
position: 100,
}],
});
wt-shopfinder-detail/wt-shopfinder-detail-html.twig
<sw-tabs
class="wt_shopfinder-detail-page__tabs"
position-identifier="wt-shopfinder-detail"
>
{% block wt_shopfinder_detail_content_tabs_general %}
<sw-tabs-item
:route="generalRoute"
:title="$tc('sw-customer.detail.tabGeneral')"
:disabled="false"
>
{{ $tc('sw-promotion-v2.detail.tabs.tabGeneral') }}
</sw-tabs-item>
{% endblock %}
{% block wt_shopfinder_detail_content_tabs_general2 %}
<sw-tabs-item
:route="reviewRoute"
:title="$tc('sw-customer.detail.tabGeneral')"
:disabled="false"
>
Review
</sw-tabs-item>
{% endblock %}
</sw-tabs>
wt-shopfinder-detail/index.js
//Sharing only the url part for tab navigation
generalRoute() {
console.log("ID = "+this.shopFinderId);
return {
name: 'webbytroops.shopfinder.detail.base',
params: { id: this.shopFinderId },
};
},
wt-shopfinder-detail-base/index.js
import template from './wt-shopfinder-detail-base.html.twig';
const { Component } = Shopware;
const { Criteria } = Shopware.Data;
Component.register('wt-shopfinder-detail-base', {
template,
inject: ['repositoryFactory'],
metaInfo() {
return {
title: "Custom"
};
}
});
wt-shopfinder-detail-base/wt-shopfinder-detail-base.html.twig
<sw-card title="Custom">
Hello world!
</sw-card>
The correct pattern for the route would be {moduleName}.{routeName}.{childName} and in the module name dashes are replaced by dots. So the correct route in your case should be example.shopfinder.detail.base.
Also, unless you omitted it, you're missing the router-view tag after the sw-tabs component.
<sw-container>
<sw-tabs>
...
</sw-tabs>
<router-view />
</sw-container>

Vue Vee-validate select (dropdown list)

So I am using this input template I got from a tutorial #logaretm created, which works great for just about anything I need. I would like to know if there is a way to modify this template to validate as a Select (drop-down) and how would I populate the option values?
I looked at vee-validation documentation but haven't been able to figure this out on my own. Any help would with this is greatly appreciated.
<template>
<div
class="TextInput"
:class="{ 'has-error': !!errorMessage, success: meta.valid }"
>
<label :for="name">{{ label }}</label>
<input
:name="name"
:id="name"
:type="type"
:value="inputValue"
:placeholder="placeholder"
#input="handleChange"
#blur="handleBlur"
/>
<p class="help-message" v-show="errorMessage || meta.valid">
{{ errorMessage || successMessage }}
</p>
</div>
</template>
<script>
import { toRef } from "vue";
import { useField } from "vee-validate";
export default {
props: {
type: {
type: String,
default: "text",
},
value: {
type: String,
default: "",
},
name: {
type: String,
required: true,
},
label: {
type: String,
required: true,
},
successMessage: {
type: String,
default: "",
},
placeholder: {
type: String,
default: "",
},
},
setup(props) {
// use `toRef` to create reactive references to `name` prop which is passed to `useField`
// this is important because vee-validte needs to know if the field name changes
// https://vee-validate.logaretm.com/v4/guide/composition-api/caveats
const name = toRef(props, "name");
// we don't provide any rules here because we are using form-level validation
// https://vee-validate.logaretm.com/v4/guide/validation#form-level-validation
const {
value: inputValue,
errorMessage,
handleBlur,
handleChange,
meta,
} = useField(name, undefined, {
initialValue: props.value,
});
return {
handleChange,
handleBlur,
errorMessage,
inputValue,
meta,
};
},
};
</script>

(this.internalValue || []).findIndex is not a function when enabling multiple selection on v-select

I am using Vue.js with Vuetify.
Following is my minimal reproducible example:
<template>
<v-app>
<v-select v-model="site" :items="sites" item-value="_id" item-text="name"></v-select>
<v-btn #click="showSelections">Show Selections</v-btn>
</v-app>
</template>
<script>
export default {
name: 'App',
data: () => ({
site: [],
sites: [
{
name: 'Vancouver',
_id: '5d9c276784e00100699281e2',
},
{
name: 'LA',
_id: '5d9c276784e00100699281e5',
},
{
name: 'Montreal',
_id: '5d9c276784e00100699281e3',
},
],
}),
methods: {
showSelections: function() {
console.log(this.site);
}
}
};
</script>
This example works perfect until you want to enable multiple selection on the v-select component.
<v-select v-model="site" :items="sites" multiple item-value="_id" item-text="name"></v-select>
As soon as you click the combobox, you'd get this:
vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in v-on handler: "TypeError: (this.internalValue || []).findIndex is not a function"
found in
---> <VSelectList>
<VThemeProvider>
<VMenu>
<VSelect>
<VMain>
<VApp>
<App> at src/App.vue
<Root>
TypeError: (this.internalValue || []).findIndex is not a function
at VueComponent.findExistingIndex (VSelect.ts?1576:338)
at VueComponent.selectItem (VSelect.ts?1576:816)
at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)
at VueComponent.invoker (vue.runtime.esm.js?2b0e:2179)
at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)
at VueComponent.Vue.$emit (vue.runtime.esm.js?2b0e:3888)
at click (VSelectList.ts?7bd1:169)
at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)
at VueComponent.invoker (vue.runtime.esm.js?2b0e:2179)
at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)
This seems to be an issue caused by Vue CLI 4.5.11 transpiling Vuetify. If you remove vuetify from transpileDependencies, your example works properly:
// vue.config.js
module.exports = {
// transpileDependencies: [
// 'vuetify'
// ]
}
Interestingly, this isn't a problem at all (no config changes needed) with Vue CLI 5.0.0-alpha.4, so consider upgrading.
I had the same problem. I leave you here as I have it in case it works for you:
<!-- VueJS Template -->
<v-select :items="arrayItems" v-model="arrayItemsSelected" label="Items" item-text="text" outlined multiple chips attach dark></v-select>
// VueJS Data
export default {
data: () => ({
arrayItemsSelected: [],
arrayItems: [
{ value: "Item1", text: "Item1" },
{ value: "Item2", text: "Item2" },
{ value: "Item3", text: "Item3" },
{ value: "Item4", text: "Item4" },
{ value: "Item5", text: "Item5" },
],
}),
}
I had the same issue when I was toggling the multiple property of the v-select. See the reproduction link: https://codepen.io/kkojot/pen/MWOpYqZ
To avoid this error you have to clear the property bound to v-model and change it empty object {} or empty array [] accordingly.
computed: {
isMultiple() {
//comment the if statment below to see the 'TypeError: (this.internalValue || []).findIndex is not a function'
if (this.multiple) this.site = [];
else this.site = {};
return this.multiple;
},
},

Mongoose query problem - $push pushing element twice

I want to push single string into array from document in my mongodb collection.
I'm using nodejs with mongoose v5.3.1
I'm putting data into my html form and then, submitting it to server.
There is my HTML:
<form action="/addchat" method="post">
<input type="text" class="form-control" name="chatid" id="addchatid">
<input type="text" class="form-control" name="chatname">
<select class="form-control form-control-sm" name="chatbot" id="chatbot">
<option value="684206793:AAH5uDpus4Ngw1Z60pj6iOedBGCM8Vq0">botname1</option></select><br>
<button type="submit" class="btn btn-primary">Add</button>
</form>
There is my app code:
app.post('/addchat', async (req, res) => {
var czat = {
name: req.body.chatname,
chatid: req.body.chatid
}
await botsmodel.updateOne({
token: req.body.chatbot
}, {
$push: {
chats: czat
}
},
async (err, result) => {
if (err) {
console.log(`Error updating chats ${err}`)
} else {
console.log(`Chats updated for ${req.body.chatbot}`);
}
});
await res.redirect('/')
});
There is my collection schema:
var schemaOptions = {
timestamps: true,
toJSON: {
virtuals: true
},
toObject: {
virtuals: true
}
};
var botyschema = new mongoose.Schema({
_id: String,
name: String,
token: String,
chats: Array
}, schemaOptions);
After executing, my console looks like it have pushed "czat" object once:
Chats updated for 684206793:AAH5uDpus4Ngw1Z60pj6iOedBGCM8Vq0
But in my collection two objects have appended into my array, it looks that:
"chats": [
{
"name": "chat_main",
"chatid": "100516120633"
},
{
"name": "chat_main",
"chatid": "100516120633"
}
],
I'm missing something in my schema or query?
I had similar problem, and I solved it by changing $push to $addToSet.
$addToSet documentation
Hope it'll be helpful.
I am not sure if addToSet is the best solution because the query being executed twice.
If you used a callback and a promise simultaneously, it would make the query executes twice.
So choosing one of them would make it works fine.
https://mongoosejs.com/docs/schematypes.html
Try changing your schema to this
var schemaOptions = {
timestamps: true,
toJSON: {
virtuals: true
},
toObject: {
virtuals: true
}
};
var botyschema = new mongoose.Schema({
_id: String,
name: String,
token: String,
chats: [{
"name": String,
"chatid": Number
}]
}, schemaOptions);
Or you could try just
chats: [{}]
But I guess that opens it up to recieve any input.
I have problem same you, so I tried remove "await", and I successfully

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