I have a hbs view which is receiving and displaying some data that I am pulling from MongoDB.
It is displaying all my data correctly except for the binary data which I am using inside of an img element. If I copy the binary data from the MongoDB document and put it in the img element it displays in the browser. For this reason I feel that the variable I am referencing is incorrect?
I have also tried to use 'img.$binary' and that does not show any data at all.
This would seem to me the way to do it though with 'img' being an object?
But when I log 'img' to the console it seems to be the right one. When I log 'img.$binary' it is undefined. I am definitely getting some data in the img tag as it shows this when I run node js (with 'img' in the appropriate place in hbs view):
HTML:
My route:
router.get('/', async (req, res) => {
const products = await Product.find()
res.render('shop', {
title: 'Shop',
products: products
})
})
Mongoose Model:
const ProductSchema = new mongoose.Schema(
{
title: { type: String, required: true, unique: true },
desc: { type: String, required: true },
img: { type: Buffer },
categories: { type: Array },
size: { type: String },
colour: { type: String },
price: { type: Number, required: true },
},
{ timestamps: true }
)
MongoDB Collection:
{
"_id" : ObjectId("62dd1127884e20dcfbb09a6c"),
"title" : "'Northface tshirt'",
"desc" : "'test'",
"img" : { "$binary" : "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAIAAAAH...(shortened for viewability), "$type" : "00" },
"categories" : [],
"size" : "'XL'",
"colour" : "'White'",
"price" : 80,
"createdAt" : ISODate("2022-07-24T09:30:15.974Z"),
"updatedAt" : ISODate("2022-07-24T09:30:15.974Z"),
"__v" : 0
}
HBS view:
<body>
<div class="grid-wrapper">
{{!-- Object --}}
{{#each products}}
<div class="grid-number">
<img src="data:image/jpg;base64,{{img}}">
{{!-- String --}}
<h2>{{title}}</h2>
{{!-- Array --}}
{{#each categories}}
{{!-- Strings --}}
<p>Apparel Type: {{apparelType}}</p>
<p>Gender: {{gender}}</p>
{{/each}}
<p>Size: {{size}}</p>
<p>Price: {{price}}</p>
<p>Colour: {{colour}}</p>
</div>
{{/each}}
</div>
Any help would be greatly appreciated, I have searched for a couple hours now and seen some similar issues but nothing has helped so far. Hoping someone with more experience with data will know what is going wrong here. Thanks in advance.
Not exactly an answer for inserting the binary data straight into the img tag but I have now found a way to display the DB document images dynamically.
Firstly, I added a route to view the img for each document in the collection by it's id:
//VIEW PRODUCT IMG
router.get('/:id/img', async (req, res) => {
try {
const product = await Product.findById(req.params.id)
if (!product || !product.img) {
throw new Error()
}
res.set('Content-Type', 'image/jpg')
res.send(product.img)
} catch (e) {
res.status(404).send()
}
})
Then, modified my img src attribute to point towards the route + inserted the id's of the documents dynamically in the hbs template:
<div class="grid-wrapper">
{{!-- Object --}}
{{#each products}}
<div class="grid-number">
<img src="products/{{id}}</img">
{{!-- String --}}
<h2>{{title}}</h2>
{{!-- Array --}}
{{#each categories}}
{{!-- Strings --}}
<p>Apparel Type: {{apparelType}}</p>
<p>Gender: {{gender}}</p>
{{/each}}
<p>Size: {{size}}</p>
<p>Price: {{price}}</p>
<p>Colour: {{colour}}</p>
</div>
{{/each}}
</div>
I can now display the images as intended:
Related
I'm trying create a follow button on list items in Vue. My strategy is to grab the value of a particular list item property and store it in the data object. Then use this value in a method to add it to an array in my database.
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock">+follow</button>
</div>
I'm not sure how to get the value of result.symbol "into" the button element to set the value symbol in the data object below.
<script>
export default {
data() {
return {
results: [ // this is populated by an api call
{
currency: "USD"
exchangeShortName: "NYSE"
name: "International Game Technology PLC"
stockExchange: "NYSE"
symbol: "IGT"
},
{...},
...
],
symbol: "",
};
},
followStock() {
// add this.symbol to database array
},
},
};
</script>
I'm guessing there might be an easier strategy I'm overlooking as I'm still new to Vue, so any other solution that essentially allows me to fire off the value of result.symbol from any rendered result to my database would be awesome.
You can just pass the result as a parameter to your method.
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result)">+follow</button>
</div>
And in your method:
methods: {
followStock(result) {
// do something with result
console.log({result});
let symbol = result.symbol;
},
}
P.S I didn't see you put your followStock() inside a methods object, but I did so in the example. https://v2.vuejs.org/v2/api/#methods
Write directly as a function call.
The vue compiler will turn followStock(result.symbol) into function(event) {followStock(result.symbol)}.
new Vue({
el: '#app',
data() {
return {
results: [
{
name: "International Game Technology PLC",
symbol: "IGT"
},
{
name: "A name",
symbol: "A symbol"
}
]
};
},
methods: {
followStock(symbol) {
console.log(symbol)
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result.symbol)">+follow</button>
</div>
</div>
As Nazaire mentioned you can access the results anywhere inside the child elements when using v-for.
(it works like a normal for-loop)
It's not only limited to the corresponding element (the element in which you do v-for)
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result.symbol)">+follow</button>
</div>
followStock(symbol){
// you can now add symbol to db
}
I'm trying to get results from an api based on the user search box. When the user enters a value 'en' or 'de'. They should get the result from that search. I need to bind the user input into my query string. This works when I manually code the country into the template, but not when I bind the value into the string after the user inputs a value for the second time. The 'get' request that uses the user input value 'query' works fine. But not when I bind this a second time
I want to be fit to access
results[i].query.name
But '.query' is not working when I query the data unless I enter the value manually '.en'
I have a json file that looks like the following
[
{
"en": {
"name": "testone",
"id": 5363289,
"location": "messages_en.properties1"
},
"de": {
"name": "testonede",
"id": 5363289,
"location": "messages_en.properties2"
}
},
{
"en": {
"name": "test2",
"id": 5363289,
"location": "messages_en.properties3"
},
"de": {
"name": "test2de",
"id": 5363289,
"location": "messages_en.properties4"
}
}
]
Below is my index.html vue.js template
<div id=#app>
<input type="text" v-model="query" placeholder="Choose Language" />
<div class="medium-6 columns">
<a #click="getResult(query)" class="button expanded">Retrieve</a>
</div>
<template v-for="(result, i) in results">
<div class="card" style="width: 20rem; display:inline-block;">
<div class="card-block"></div>
<p> {{results[i].query}} </p>
<!-- works when I manually code in the 'en' query but when ran with 'query' it returns an error 'Cannot read property 'name' of undefined"' second time it returns that the value is -->
<!-- <p> {{results[i].en.name}} </p> -->
<!-- <p> {{results[i].query.name}} </p> -->
</div>
</template>
</div>
Vue.js
el: '#app',
data () {
return {
search: '',
query: 'en',
results: '',
title: '',
items: '',
section: ''
}
},
methods: {
getResult(query) {
axios.get('http://localhost:3000/api/country?country=' + query + '&blank=true').then(response => {
this.results = response.data;
console.log(this.results);
});
},
You need to use bracket notation to access a property using a param, so:
results[i][query].name
The second issue is that results[i][query] will be undefined until the async call has completed, so you will need to check that the property is not undefined or use a boolean flag. So, to check that it is not undefined you could do something like:
<p v-if="!!results[i][query]">{{results[i][query].name}}</p>
<p v-else>Loading...</p>
Here's a simplified JSFiddle for that: https://jsfiddle.net/4w3dxm22/
Or you could just use a dataLoaded flag:
new Vue({
el: '#app',
methods:{
getResult(query) {
this.dataLoaded = false; // set dataLoaded to false
axios.get('http://localhost:3000/api/country?country=' + query + '&blank=true').then(response => {
this.results = response.data;
this.dataLoaded = true; // Data has loaded so set dataLoaded to true
});
},
data: {
dataLoaded: false
}
})
Then you can do:
<span v-if="dataLoaded">{{results[i][query].name}}</span>
<span v-else>Loading Data...</span>
Here's the simplified JSFiddle for that: https://jsfiddle.net/99ydx82u/
I am learning Meteor JS and followed a tutorial to build a menu builder shopping list. I am trying to add some features to it. I added one feature successfully, but now I am trying to create an organization feature where users can join an organization and see all shopping lists related to that organization. The first step is to allow for adding an organization by users.
The form appears, and I was able to insert in to the database from the console, but when I use autoform the objects are not being inserted into the database.
I recently upgraded from Meteor 1.3 to 1.4. I don't believe that is an issue since all of the other forms on the app are inserting properly still.
I have a feeling it has something to do with subscribe/publish, but I am not sure what I am doing wrong.
HTML- neworganization.html
<template name='NewOrganization'>
<div class='new-organization-container'>
<i class='fa fa-close'></i>
{{#autoForm collection='Organizations' id='insertOrganizationForm' type='insert'}}
<div class='form-group'>
{{> afQuickField name='organization'}}
</div>
<div class='form-group'>
{{> afQuickField name='members'}}
</div>
<button type="submit" class="btn btn-primary">Add</button>
{{/autoForm}}
</div>
</template>
organizations.html
<template name='Organizations'>
<h3>Your Organizations</h3>
{{#if $.Session.get 'newOrganization'}}
{{> NewOrganization }}
{{else}}
<button class='btn btn-organization btn-primary'>Add an Organization</button>
<button class='btn btn-join'>Join an Organization</button>
<button class='btn btn-deny'>Leave an Organization</button>
{{/if}}
<section class='organization-list'>
{{#if Template.subscriptionsReady}}
{{#each organizationList}}
{{> OrganizationItem}}
{{/each}}
{{else}}
<p>Loading...</p>
{{/if}}
JS- organizations.js
Template.Organizations.onCreated(function() {
this.autorun(() => {
this.subscribe('organizations');
});
});
Template.Organizations.helpers({
organizations() {
return Organizations.find({});
}
});
Template.Organizations.events({
'click .btn-organization': () => {
Session.set('newOrganization', true);
}
});
Template.NewOrganization.helpers({
organizationList: () => {
var organizationItems = Organizations.find({});
return organizationItems;
}
});
newOrganization.js
if (Meteor.isClient) {
Meteor.subscribe('organizations');
}
Template.NewOrganization.events ({
'click .fa-close': function () {
Session.set('newOrganization', false);
}
});
collections/organizations.js
import SimpleSchema from 'simpl-schema';
SimpleSchema.extendOptions(['autoform']);
Organizations = new Mongo.Collection('organizations');
Organizations.allow({
insert: function(userId){
return !!userId;
},
update: function(userId, doc){
return !!userId;
}
});
OrganizationSchema = new SimpleSchema ({
organization: {
label: "Organization Name",
type: String
},
id: {
label: "ID",
type: String,
autoform: {
type: "hidden"
}
},
members: {
type: Array
},
"members.$": Object,
"members.$.name": String,
"members.$.role": String,
inOrganization: {
type: Boolean,
defaultValue: true,
autoform: {
type: 'hidden'
}
},
createdAt: {
type: Date,
label: "CreatedAt",
autoform: {
type: "hidden"
},
autoValue: function() {
return new Date();
}
}
});
Meteor.methods({
deleteOrganizations: function(id) {
Organizations.remove(id);
}
});
Organizations.attachSchema(OrganizationSchema);
The problem is in the way the Schema was designed. I had inserted an id into the schema. My reasoning was that I wanted to have a way to add and remove members from an organization. What I did not take into account was that Mongo autogenerates an id for database object and by designing my schema in this way, I was creating a conflict. I removed the id from my schema and removed the problem.
Here is the new collections/organizations.js file:
import SimpleSchema from 'simpl-schema';
SimpleSchema.extendOptions(['autoform']);
Organizations = new Mongo.Collection('organizations');
Organizations.allow({
insert: function(userId){
return !!userId;
},
update: function(userId, doc){
return !!userId;
}
});
OrganizationSchema = new SimpleSchema ({
organization: {
label: "Organization Name",
type: String
},
members: {
type: Array
},
"members.$": Object,
"members.$.name": String,
"members.$.role": String,
inOrganization: {
type: Boolean,
defaultValue: true,
autoform: {
type: 'hidden'
}
},
createdAt: {
type: Date,
label: "CreatedAt",
autoform: {
type: "hidden"
},
autoValue: function() {
return new Date();
}
}
});
Meteor.methods({
deleteOrganizations: function(id) {
Organizations.remove(id);
}
});
SimpleSchema.debug = true;
Organizations.attachSchema(OrganizationSchema);
So I'm linking in some values to an .hbs-file and most of the values is returned. But for some reason, one of the values is not returning anything. The .hbs:
<nav>
<h2>Working Files</h2>
{{#if snippets }}
<ul>
{{#each snippets}}
<li class={{ this.active }}>{{ this.fileType }}</span>
</li>
{{/each}}
</ul>
{{/if}}
</nav>
And I'm sending them in like this:
router.route("/home/:id")
.get(restrict, function(req, res) {
User.findOne({ user: req.session.Auth.username }, function(error, data) {
Snippet.find({ postedBy: data._id}).exec()
.then(function(data) {
Snippet.find({ _id: req.params.id}).exec()
.then((snippetID) => {
// This is what I send it -------------------------------------------------
let context = {
snippets: data.map(function(snippet) { // Gets the snippet info for nav
return {
name: snippet.title,
fileType: snippet.fileName,
id: "/home/" + snippet._id
};
}),
text: snippetID[0].snippet[0].text, // Gets the snippet-text, and writes it
sessionId: snippetID[0]._id, // For the CRUD to get the id
active: "active"
};
res.render("../views/home", context);
// ------------------------------------
}).catch((err) => {console.log(err)})
}). catch(function(err) {console.log(err)});
});
});
It's the "this.active" that holds no value at all. I've been scratching my head over and over about this, and I can't understand why that value won't follow. All the other values do follow. I've even tried to set the "active"-key to the same value as "id" or "text", but no luck.
Do anyone know what the problem is?
I would like to retrieve elements from a collection only if an element in an array is equal to the current month that I have stored in a session. For example, if my session variable is equal to "juillet" I would like to have only the items which have "juillet" in the "moisrec" method :
Legumes = new Mongo.Collection("legumes");
if (Meteor.isServer) {
Meteor.startup(function() {
if (Legumes.find().count() === 0) {
var legumes = [{
nom: "poireau",
moisrec: ["juillet", "août"],
}, {
nom: "navet",
moisrec: ["octobre", "novembre"],
}, {
nom: "choux-fleur",
moisrec: ["juillet", "août"]
}];
// Insert sample data into db.
legumes.forEach(function(item) {
Legumes.insert(item);
});
}
});
}
I have a helpers that looks like this :
Template.Legumes.helpers({
legumes : function() {
return Legumes.find({});
}
});
I use blaze after for templating :
{{#each legumes}}
<div class="col-sm-3">
<div class="thumbnail">
<img src="legumes/{{nom}}.jpg" alt="{{nom}}" width="400" height="300">
<p><strong>{{nom}}</strong></p>
<p>Période récolte : {{#each mois in moisrec}}<a>{{mois}} </a>{{/each}}</p>
<button class="btn" href="/legumes:{{_id}}">Fiche complète</button>
</div>
</div>
{{/each}}
Thanks
Yoann
Legumes.find({ moisrec: "juillet" });
That should get you what you need. Since it's a simple array of strings there isn't any other fancy calls you'll need to make.