Mongo: Get data of two collections - second depending on first one - javascript

I try to display an article and below of that article I want to display some literature data which belongs to the article.
In the collection literature there are many documents, but I want to filter those which has article_id: article._id
I believe I think to complicated, but this is what I'm trying to do:
publication.js
Meteor.publish('references', function(){
return Articles.find({});
});
Router.js
Router.route('/cars', {
name: 'main',
data: function() {
return {
article: Articles.find({})
}
}
});
template_1.html
<template name="main">
<div>
{{article._id}}
<header><h1>{{article.title}}</h1></header>
{{article.content}}
{{>literature reference=article._id}}
</div>
</template>
template_2.html
<template name="literature">
Same id: {{this.reference}}
Now get all item of literature-collection
<ul>
{{#each items}}
<li>{{this.title}}</li>
{{/each}}
</ul>
</template>
helper.js
Template.literature.helpers({
items: function() {
return Literature.find({article_id: article._id}); /* this should be the id the first template */
}
});
Guess that could be easier. So my second think was to put the literature-query also into the router:
Router.route('/cars', {
name: 'main',
data: function() {
return {
article: Articles.find({}),
items: Literature.find({article_id: article._id}) /* How do I get the variable which is needed? */
}
}
});

You should use meteor add reywood:publish-composite
so your publish function would like this:
Meteor.publishComposite('articles', function(){
return {
find: function(){
return Articles.find();
},
children: [{
find: function(article){
return Literature.find({article_id: article._id})
}
}]
}
});

Related

Vuejs not updating list but updating obect

I am learning Vuejs and I am stuck. Why can I see the messages get added to the object (in Chrome Vue debugger) yet it is not added to the div that contains the list?
My Vue Component:
<template>
<div id="round-status-message" class="round-status-message">
<div class="row">
<div class="col-xs-12" v-for="sysmessage in sysmessages" v-html="sysmessage.message"></div>
</div>
</div>
</template>
<script>
export default {
props: ['sysmessages'],
methods: {
scrollToTop () {
this.$el.scrollTop = 0
}
}
};
</script>
My Vue instance:
$(document).ready(function()
{
Vue.component('chat-system', require('../components/chat-system.vue'));
var chatSystem = new Vue({
el: '#system-chat',
data: function () {
return {
sysmessages: []
};
},
created() {
this.fetchMessages();
Echo.private(sys_channel)
.listen('SystemMessageSent', (e) => {
this.sysmessages.unshift({
sysmessage: e.message.message,
});
this.processMessage(e);
});
},
methods: {
fetchMessages() {
axios.get(sys_get_route)
.then(response => {
this.sysmessages = response.data;
});
},
processMessage(message) {
this.$nextTick(() => {
this.$refs.sysmessages.scrollToTop();
});
// updateGame();
}
}
});
});
My template call in HTML:
<div id="system-chat">
<chat-system ref="sysmessages" v-on:systemmessagesent="processMessage" :sysmessages="sysmessages" :player="{{ Auth::user() }}"></chat-system>
</div>
There are no compile or run time errors and I can see records added to the props in the vue chrome tool. I can also see empty HTML elements added to the div.
What have I missed?
UPDATE: My record structures:
response.data is an array of objects, each like this:
{"data":[
{"id":100,
"progress_id":5,
"message":"start message",
"action":"welcome"
},
{"id"....
e.message.message contains the text message entry, so just a string.
I am trying to access the message variable in each object during the fetchMessages method.
You're adding objects with sysmessage as the property.
this.sysmessages.unshift({
sysmessage: e.message.message,
});
But you are trying to view
v-for="sysmessage in sysmessages" v-html="sysmessage.message"
Based on your update, the code should be:
this.sysmessages.unshift({
message: e.message.message,
});
And you can leave the template as
v-html="sysmessage.message"

Meteor: Router with URL of a userName?

I have problems with creating routes with user's usernames. So idea is something like this: Click on path and go to that users profile. His link should be something like : http://www.something.com/usersUsername
I was reading and trying everything I found on internet about this but lot of stuff changed so I couldn't manage this.
Only thing I found usefull is that when page loads client ,,watch" paths first and then subscribes to a collection so I got ,,null" for path. Any help? My idea is to create something to waitOn for subscribe...
Packages: iron:router , accounts-ui , accounts-password
Here is code:
Start page, template:
<template name="početna">
<h1>Dobrodošli!</h1>
<h3>Registrujte se:</h3>
{{> register}}
<h3>Prijavite se:</h3>
{{> login}}
{{#if currentUser}}
<h2>Logovan si!</h2>
{{> logout}}
Profil
{{/if}}
Router JS file:
Router.configure({
layoutTemplate: 'okvir'
});
// * * * * * * //
Router.route('/', {
name: 'početna',
template: 'početna',
});
Router.route('/:username', {
waitOn: function(){
return Meteor.subscribe('userData'), Meteor.user().username
},
name: 'profil',
template: 'profil',
});
Simple HTML template file only to se if it works:
<template name="profil">
<h1>RADI</h1>
</template>
Thanks!
Here you go:
Router.route('/:username',{
name: 'profil',
waitOn: function () {
return Meteor.subscribe('userData', this.params.username)
},
action: function () {
this.render('profil', {
data: function () {
return Meteor.users.findOne({username: this.params.username});
}
});
}
});
EDIT:
With this.params.username will let anybody visit that profile, user or not. If you want to prevent that, you can use onBeforeAction()
onBeforeAction: function() {
if(Meteor.user() && this.params.username == Meteor.user().username){
this.next();
} else {
Router.go('/not-authorized') //or any other route
}
},
Luna, thanks for help! Luna's answer helped but I also needed:
1.) Helper to set value of username=username
Template["početna"].helpers({ username: function() { return Meteor.user().username } })
2.) Publish
Meteor.publish("userData", (username) => {
return Meteor.users.find({
username: username
})
});

Can autorun's reactive computation be dependent on part of a collection?

I'd like to create a template in Meteor that has a Tracker.autorun which exclusively runs when part of a document changes --- but not when other parts of the document change.
So here is sample code using a minimongo collection and template.autorun
parent.html
{{#each items}}
{{> child}}
{{/each}}
child.html
<div>{{title}}</div>
<p>{{description}}</p>
Minimongo Collection
LocalProject.findOne() output:
"items": [
{
"title": "hi",
"description": "test"
},
{
"title": "hi 2",
"description": "test 2"
},
],
"otherstuff:{//etc}
child.js
Template.child.onRendered(function(){
this.autorun(function() {
var data = Template.currentData();
doSomething(data.title,data.description)
});
});
addnewitem.js
LocalProject.update(currentEditingProjectID,{ $push: { 'items': newItem }},function(error,result){
if(error){console.log(error)}
});
The problem is, whenever I run addnewitem.js, all of my Template.child autoruns execute even though their reactive data source (Template.currentData()) has not changed unless it was the specific item I updated. Similarly if I want to update an existing item, not just add a new one to the array, all of the autoruns for each item get executed.
So is there a way, using this model, to create a dependency for autorun that is reactively granular to specific portions of a document?
I don't think the way to go is by using an autorun. I would either set up individual reactive dependencies on each item, or use observe/observeChange.
First idea
parent.html:
{{#each items}}
{{> child}}
{{/each}}
parent.js:
Template.parent.helpers({
// Returns only item ids
items: function() {
return Items.find({}, { fields: { _id: 1 } });
}
});
child.html:
{{#each items}}
{{#with item=getItem}}
<div>{{item.title}}</div>
<p>{{item.description}}</p>
{{/with}}
{{/each}}
child.js:
Template.child.helpers({
getItem: function() {
// Get the item and set up a reactive dependency on this particular item
var item = Items.find(this._id);
// After item has been displayed, do something with the dom
Tracker.afterFlush(function () {
doSomething(item.title, item.description);
});
return item;
}
});
Second idea
parent.html:
{{#each items}}
{{> child}}
{{/each}}
parent.js:
function do(item) {
Tracker.afterFlush(function () {
doSomething(item.title, item.description);
});
}
Template.parent.onCreated({
this.items = Items.find({});
this.handle = this.items.observe({
added: function(item) { do(item); },
changed: function(oldItem, newItem) { do(newItem); },
});
});
Template.parent.onDestroyed({
this.handle.stop();
});
Template.parent.helpers({
items: function() {
return Template.instance().items;
}
});
child.html:
{{#each items}}
<div>{{title}}</div>
<p>{{description}}</p>
{{/each}}
There's a tool just for this - 3stack:embox-value provides reactive isolation, and value caching.
Using your example, you could isolate changes to title/description like so:
first up, add the packages
meteor add 3stack:embox-value
meteor add ejson
Then, update your code:
Template.child.onRendered(function(){
// creates a reactive data source, that only triggers "changes"
// when the output of the function changes.
var isolatedData = this.emboxValue(function(){
var data = Template.currentData();
return {title: data.title, description: data.description}
}, {equals: EJSON.equals})
this.autorun(function() {
// This autorun will only execute when title or description changes.
var data = isolatedData()
doSomething(data.title,data.description)
});
});

How can you filter a data-list to render into multiple outlets in emberjs

How can you filter a data-list to render into multiple outlets in emberjs.
What I have now in not really working, but may help you understand what I want to achieve.
I can solve this by making multiple file-list.hbs template-files ( where I change file in the each to fileList1 or fileList2, ...), but that doesn't seem right.
What I want to achieve
I have a documents page where I want to list all of the document in the file list (see fixtures file). But instead of printing out one files-list, I want to split the lists so I have multiple lists according to the filter.
Please look at the code to understand it better ^^
Can anyone help? :)
File.FIXTURES
App.File.FIXTURES = [
{
id: 1,
showHomepage: false,
filter: 'filter1',
url: '/file1.pdf',
description: 'file1'
},
{
id: 2,
showHomepage: false,
filter: 'filter2',
url: '/file2.pdf',
description: 'file2'
},
{
id: 3,
showHomepage: true,
filter: 'filter2',
url: '/file3.pdf',
description: 'file3'
},
{
id: 4,
showHomepage: true,
filter: 'filter3',
url: '/file4.pdf',
description: 'file4'
}
];
Route
App.InfoDocumentenRoute = Ember.Route.extend({
model: function() {
var store = this.store;
return Ember.RSVP.hash({
fileList1: store.find('file' , { filter: "filter1" }),
fileList2: store.find('file' , { filter: "filter2" }),
fileList3: store.find('file' , { filter: "filter3" })
});
},
renderTemplate: function() {
this.render('file-list', { // the template to render
into:'info.documenten', // the route to render into
outlet: 'file-list-filter1', // the name of the outlet in the route's template
controller: 'file' // the controller to use for the template
});
this.render('file-list', { // the template to render
into:'info.documenten', // the route to render into
outlet: 'file-list-filter2', // the name of the outlet in the route's template
controller: 'file' // the controller to use for the template
});
this.render('file-list', { // the template to render
into:'info.documenten', // the route to render into
outlet: 'file-list-filter3', // the name of the outlet in the route's template
controller: 'file' // the controller to use for the template
});
}
});
info/documents.hbs
{{ outlet file-list-filter1 }}
{{ outlet file-list-filter2 }}
{{ outlet file-list-filter3 }}
file-list.hbs
<ul class="download-list">
{{#each file in file}}
<li class="download-list__item">
<a {{bind-attr href=file.url}} target="_blank" class="download-list__link">
<i class="icon-download download-list__link__icon"></i>
{{file.description}}
</a>
</li>
{{else}}
<li>
Geen documenten beschikbaar.
</li>
{{/each}}
I think the best way to go about this would be to declare your file-list.hbs as a partial and include it within your other templates where needed as: {{partial "file-list"}}. In your showHomepage where you only want to use it a single time, merely include the {{partial "file-list"}} within your showHomepage.hbs.
Then, for your InfoDocumentRoute, put the following to declare your model as an array of filelists:
App.InfoDocumentenRoute = Ember.Route.extend({
model: function() {
var store = this.store;
return [
store.find('file' , { filter: "filter1" }),
store.find('file' , { filter: "filter2" }),
store.find('file' , { filter: "filter3" })
];
}
});
And your InfoDocument.hbs as:
{{#each file in model}}
{{partial "file-list"}}
{{/each}}
Which will then render the file-list template for each item in the model array.
More info about partials
So from what i gather about your question you want to filter your model on your filter property on the model. I am sure there are a few ways to accomplish this but here is another possible solution that could spark another solution.
So in the route I returned the models. Then in the controller I created properties that are filtering the array of models from the route. Then in the template I loop over the array that filter property gives me in the controller and output in the template.
Heres JSBin. http://emberjs.jsbin.com/vunugida/5/edit
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('File');
}
});
App.IndexController = Ember.ArrayController.extend({
filter1: function() {
return this.filter(function(item) {
return item.get('filter') === "filter1";
});
}.property(),
filter2: function() {
return this.filter(function(item) {
return item.get('filter') === "filter2";
});
}.property(),
filter3: function() {
return this.filter(function(item){
return item.get('filter') === "filter3";
});
}.property()
});
TEMPLATE:
<script type="text/x-handlebars" data-template-name="index">
<h1>Index Template</h1>
<ul>
{{#each}}
<li>{{url}}</li>
{{/each}}
</ul>
<p>Filter 1</p>
{{#each filter1}}
<li>{{url}}</li>
{{/each}}
<p>Filter 2</p>
{{#each filter2}}
<li>{{url}}</li>
{{/each}}
<p>Filter 3</p>
{{#each filter3}}
<li>{{url}}</li>
{{/each}}
</script>

Ember.js show hasMany data in template with one or more results

After endless trying I hope someone find the clue in what I am trying. I know there are many questions about this specific topic on stackoverflow. However I think I do not ask the same question. As I do not find the answer to my specific challenge.
Here is my Router:
App.Router.map(function () {
this.resource('article', {path: '/article/:id'});
this.resource('article.new', {path: "/article/new"});
});
Routes
App.ArticleRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('article', params.id);
}
});
App.ArticleNewRoute = Ember.Route.extend({
renderTemplate: function () {
this.render('article', {
controller: 'article.new'
});
},
model: function () {
return this.store.createRecord('article');
}
});
The model
App.Category = DS.Model.extend({
name: DS.attr('string'),
image: DS.attr('string'),
categoryRelation: DS.belongsTo('category')
});
App.Article = DS.Model.extend({
name: DS.attr('string'),
category: DS.hasMany('category')
)};
The returned JSON from server:
{
"articles":[
{
"id":1,
"name":"Car 1",
"category":[1,2],
{
"id":2,
"name":"Car 2",
"category":2,
],
"categorys":[ // note the added 's' when returning multiple items as per EmberJS convention
{
"id":1,
"name":"Oldtimers"
},
{
"id":2,
"name":"Classic"
}
],
}
And now the question, because I try in my template the following:
<script type="text/x-handlebars" data-template-name="article">
<div>
{{#each category in model}}
{{category.name}}<br>
{{name}}<br>
{{/each}}
</div>
</script>
I have tried multiple variations in the template, this is my last code which seems correct. Note: as for article with id 2, the template must also render if there is just one article.
Edit: I translated some code for you guys. If there are misspellings, they are probably not in the original code.
Your article template will receive just one article so this {{#each category in model}} don't work, you need to use {{#each category in model.category}}:
<div>
Article {{name}}<br/>
{{#each category in model.category}}
Category {{category.name}}<br/>
{{/each}}
</div>
This is a fiddle with this in action http://jsfiddle.net/marciojunior/fj26R/

Categories