How to return the corresponding image by id in a helper with CollectionFS? - node.js

I had real problems writing the title of this question. I am using gridfs for my storage, so the files are stored in my MongoDb. Now I have the following situation:
var imageStore = new FS.Store.GridFS('images', {});
Images = new FS.Collection('images', {
stores: [imageStore]
});
Test = new Meteor.Collection('test');
The Images are stored in Images and the "posts" are stored in test. Now when I upload an image, as I can't reference the image in my Test collection, I save the ._id for that image.
FS.Utility.eachFile(event, function (file)
{
var image = Images.insert(file, function (err, fileObj)
{
});
Test.insert({name: 'Test!', userId: '123123', image: image._id});
});
Now in my template, I want to show the name and the image. But what do I return in my helper? My template for a single post looks like this:
<li>
<h2>{{name}}</h2>
<img src="{{file.url}}" alt="" class="thumbnail" />
</li>
And the Helper function:
Tests: function ()
{
return each-test-with-their-image;
}
I imagined something like this, but it looks stupid as hell:
Tests: function ()
{
var tests = Test.find().fetch();
for(var i = 0; i < tests.length; i++)
{
tests[i].file = Images.findOne({_id: tests[i].image});
}
return tests;
}
But I feel that there must be a better way.

Rather than iterate over an array of results you can just get the url in a helper as each document is displayed. This is cleaner and you don't have to recompute the whole array as docs are added/removed/changed.
html:
<li>
<h2>{{name}}</h2>
<img src="{{url}}" alt="" class="thumbnail" />
</li>
js:
Template.myTemplate.helpers({
url: function(){
var queryImage = Images.findOne({_id: this.image});
return queryImage && queryImage.url();
}
});

Related

Display single item with Vue.js

I have a list of items where the title is a link to display a detailed view of the item. Click the title and it correctly goes to url + Id. In the Vue tolls the detail page retrieves the item with matching ID but as and array not an object and the template does not display any properties - what am I missing?
<script>
import axios from "axios";
export default {
name: "Report",
data() {
return {
report: {}
};
},
mounted: function() {
this.getReport();
},
methods: {
getReport() {
let uri = "http://localhost:5000/api/reports/" + this.$route.params.id;
axios.get(uri).then(response => {
this.report = response.data;
});
}
}
};
</script>
The template is so
<template>
<v-content>
<h1>report detail page</h1>
<p>content will go here</p>-
<h3>{{ report.month }}</h3>
<pre>{{ report._id }}</pre>
</v-content>
</template>
any comments appreciated
url + Id
It sounds like your issue is that you are receiving an array not an object.
You can pull out objects encapsulated inside arrays easily.
For example, if we had the following data:
var bus1 = {passengers:10, shift:1}
var busArr = [bus1]
which we can assert: busArr === [{passengers:10, shift:1}]
We could then pull out bus1 by referencing the index 0:
var bus1New = busArr[0]
If you want to avoid the data transformation and just output the structure you can consider a v-for in your template.
<p v-for="val in report">
_id: {{val._id}}
<br>
month: {{val.month}}
</p>

Meteor - button click not updating value

I am trying to get a random document in the collection and display it on the page. It is successful every time I load the page, but I want a button to do the work as well.
main.html
<head>
<title>test</title>
</head>
<body>
<h1>Random Question</h1>
{{> question}}
</body>
<template name="question">
<button>Click Me</button>
{{#each object}}
{{question}}
{{a}}
{{b}}
{{c}}
{{d}}
{{answer}}
{{points}}
{{/each}}
</template>
main.js
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html';
Resolutions = new Mongo.Collection('quiz');
Template.question.created = function () {
var random = get_random();
this.question = new ReactiveDict();
this.question.set('object', random);
};
function get_random(){
var collection_size = Resolutions.find().count();
var random = Math.floor(Random.fraction() * collection_size);
// choose a random item by skipping N items
var item = Resolutions.findOne({},{
skip: random
});
var objArray = $.makeArray(item);
return objArray;
}
Template.question.helpers({
object: function () {
return get_random();
}
});
Template.question.events({
'click button': function (event, template) {
// increment the counter when button is clicked
var random = get_random();
template.question.set('object', random);
}
});
There is no error message when I load the page or click the button.
Any help is appreciated.
Btw, what is the object inside "this.question.set('object', random);". Maybe that's where my issue is.
You can considerably simplify your code and also solve your problem by not picking a random object in your helper - that will run many times, even when you don't expect it to. Also since you're only viewing a single object, use {{#with }} instead of {{#each }} - this will avoid the array conversion step.
html:
<template name="question">
<button>Click Me</button>
{{#with object}}
{{question}}
{{a}}
{{b}}
{{c}}
{{d}}
{{answer}}
{{points}}
{{/with}}
</template>
js:
import { Template } from 'meteor/templating';
import './main.html';
Resolutions = new Mongo.Collection('quiz');
Template.question.created = function () {
setRandom(); // initialize the random selection
};
function setRandom(){
var collection_size = Resolutions.find().count();
var random = Math.floor(Random.fraction() * collection_size);
Session.set('random',random);
}
Template.question.helpers({
object: function () {
return Resolutions.findOne({},{ skip: Session.get('random') });
}
});
Template.question.events({
'click button': function (event, template) {
setRandom();
}
});

How to access elements in the current template in meteor?

I have a template like this,
<template name = "foo">
<p id="loading" >LOADING...</p>
<p> {{theResult}} </p>
</template>
This is how I create foos,
// foos = [a, b, c, d]. With each button click I add a new item to the array
{{#each foos}}
{{> foo .}}
{{/each}}
And how a foo works,
Template.foo.created = function(){
var name = Template.currentData();
api_call(name, function(err, result){
Session.set(name, result);
});
}
Template.foo.helpers({
'theResult': function(){
var name = Template.currentData();
if(Session.get(name)) {
$("#loading").hide();
return Session.get(name);
} else {
return "";
}
}
})
So my expectation is to when the data came from the api_call, to hide "LOADING..." para, and to show the result in theResult.
The result is showing correctly. My problem is "LOADING..." is only get hidden on the top most foo. Not the other ones.
How can I fix this?
EDIT:
As suggested instead of,
$("#loading").hide();
I used,
Template.instance().$("#loading").hide();
This didn't work too :)
This is how I'd do it
Template... if theResult is undefined, the else path will be rendered.
<template name="foo">
{{#with theResult}}<p> {{this}} </p>
{{else}}<p id="loading" >LOADING...</p>
{{/with}}
</template>
Javascript... theResult is a simple Session.get call
Template.foo.helpers({
theResult: function(){
var name = Template.currentData();
return name && Session.get(name);
}
});
Thanks to Meteor templating engine, you can access a template scoped jQuery object that will only return elements within the corresponding template.
Template.foo.helpers({
'someText': function(){
var template = Template.instance();
template.$('p').changeSomeattr();
return Session.get('myPara');
}
});

Custom Handlebars Helper with partial as hash argument

I'm trying to create a custom handlebars helper, and I want to be able to pass it a "base-template" and a "partial"..
So what it should do is render the base template and then render whatever partials is passed as the second parameter.
I have the following right now:
module.exports.register = function(Handlebars, options) {
var assembleOpts = options || {};
Handlebars.registerHelper("sgComponent", function (template, partial, options) {
// Default options
var opts = {
cwd: '',
src: '',
glob: {}
};
options = _.defaults(options.hash, assembleOpts.sgComponent, opts);
var partialContent, partialContext;
// Join path to 'cwd' if defined in the helper's options
var cwd = path.join.bind(null, options.cwd, '');
var src = path.join.bind(null, options.src, '');
glob.find(src(partial), options.glob).map(function(path) {
partialContext = yfm.extract(path).context;
partialContent = yfm.extract(path).content;
});
return glob.find(cwd(template), options.glob).map(function(path) {
var context = yfm.extract(path).context;
var content = yfm.extract(path).content;
return {
path: path,
context: processContext(grunt, partialContext),
content: content
};
}).map(function (obj) {
var template = Handlebars.compile(obj.content);
return new Handlebars.SafeString(template({content: obj.context}));
});
});
var processContext = function(grunt, context) {
grunt.config.data = _.defaults(context || {}, _.cloneDeep(grunt.config.data));
return grunt.config.process(grunt.config.data);
};
};
And right now I'm using my helper like this:
{{{sgComponent 'path/to/basetemplate/basetemplate.hbs' 'path/to/partial/partial.hbs'}}}
I'm a little stuck right now. At the moment I can only figure out how to render either the base template or the partial.. Or render the base template but with the context from the partial (it's yaml font matter) What I would like to achieve is the basetemplate being rendered and the partials content being render inside of it, with whatever context defined in the partial.
Like so (base template):
<div class="sg-component">
<!-- Markup -->
<div class="sg-component__markup">
{{partial}}
</div>
<!-- Documentation -->
<div class="sg-component__documentation">
{{#markdown}}
~~~markup
{{partial}}
~~~
{{/markdown}}
</div>
</div>
Partial:
---
context: context stuff here
---
<h1 class="title--huge">This is a very large header</h1>
<h2 class="title--xlarge">This is a large header</h2>
<h3 class="title--large">This is a medium header</h3>
<h4 class="title--medium">This is a moderate header</h4>
<h5 class="title--small">This is a small header</h5>
<h6 class="title--xsmall">This is a tiny header</h6>
Thanks in advance!
Dan
So, I fixed it my self! Hurray..
I sat down it thought it through and came to the conclusion that I only needed to register the second hash argument as a partial.
So I added this after the Handlebars.compile(obj.content);
Handlebars.registerPartial("sgComponentContent", partial);
And then within my basetemplate I can now use {{> sgComponentContent}}
Awesome!

Create jqueryMobile page dynamically based on the url

I am creating an application to get some experience in jQuery Mobile and backbone. I have made a "restful" API with node.js that handles the data I need. It works fine with all my static pages I made in index.html. But when I need to create a page with data from a certain id I am a bit lost.
For example when I want to display all items(/items) I have a data-role=page with id items that list all items, but when I need to go to a detailed page for each item (/items/1) i want to create that details page whenever a user wants details on an item, in other words when a user visit the url spots#3 for example.
Is this possible?
my router: the model gives me all data i want
Spoter.Router = Backbone.Router.extend({
routes: {
"": "",
"spot#:id": "spotDetails"
},
//Details on a certain spot with id
spotDetails: function(id) {
var spotDetailsContentDiv = Spoter.spotDetailsContent;
spotDetailsContentDiv.empty();
var spot = new Spoter.spotModel({id: id});
spot.fetch({
successCallback: function(data) {
var spotDetailsView = new Spoter.spotDetailsView({
model: data
});
spotDetailsContentDiv.html(spotDetailsView.render().el);
}
});
}
});
View:
Spoter.spotDetailsView = Backbone.View.extend({
render:function () {
this.$el.html(this.template(this.model));
return this;
}
});
Template with underscore
<ul data-role="listview" data-theme="c" data-inset="true">
<li>
<a href="#">
<h1><%= this.model.name %></h1>
<p><%= this.model.description %></p>
</a>
</li>
</ul>

Resources