I'm learning how to use meteor and I'm having trouble syncing the clients collection with what the server has. I'm trying to make a counter that increments by one every time you click it by calling a method on the server. When I go to my application it always displays 1 but when I do .find() on my collection through the Mongo shell it has what the number should actually be. I have autopublish on so shouldn't this work automatically? Here is my code for both client and server:
/client/main.js
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html';
Counter= new Mongo.Collection('counter');
Template.counter.helpers({
counter() {
return Counter.find({});}
});
Template.counter.events({
'click #a':function() {
Meteor.call('add')
},
});
/client/main.html
<head>
<title>HypeCoins</title>
</head>
<body>
<h1>HypeCoins</h1>
{{> counter}}
</body>
<template name="counter">
<button id='a'>Click Me</button>
<p>You've pressed the button {{counter.count}} times.</p>
</template>
/server/main.js
import { Meteor } from 'meteor/meteor';
Counter= new Mongo.Collection('counter');
Meteor.startup(() => {
});
Meteor.methods({
'add':function(){
Counter.update({},{$inc:{count:1}});
}
});
You will get your solution by using a modifier on your update. This will require you to create an ID so there is something to update. You can do so by:
client/main.js
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html';
Counter = new Mongo.Collection('counter');
Template.counter.helpers({
counter() {
return Counter.findOne();
}
});
Template.counter.events({
'click #a':function() {
Meteor.call('add');
},
});
server/main.js
import { Meteor } from 'meteor/meteor';
Counter = new Mongo.Collection('counter');
Meteor.methods({
'add': function() {
currentCount = Counter.findOne();
if (!currentCount) {
Counter.insert({ count: 1});
}
Counter.update({_id: currentCount._id }, { $inc: { count: 1 } });
},
});
Refer to Meteor documentation:
https://docs.meteor.com/api/collections.html#Mongo-Collection-update
You have to define your collection schema. Take a look at that package it should be useful for you: https://github.com/aldeed/meteor-simple-schema
Related
I am trying to understand Meteor.publish() and Meteor.subscribe(), with help from this video.
I have a collection defined like this:
export const Chansons = new Mongo.Collection('chansons');
Which I publish on my server:
Meteor.publish("chansons", function(){
return Chansons.find();
});
But then when I try to subscribe to it on my client, I have an error:
While building for web.browser:
imports/ui/body.js:17:14: Unexpected token, expected ";" (17:14)
What I do not get is that I wrote the code exactly like in the video, and it worked at first!
constructor() {
super();
this.state = {
subscription: {
chansons: Meteor.subscribe("chansons")
}
}
}
I then changed the formatting of my code somewhere else and now this error appeared and I cannot seem to fix it.
The error seems to come from the constructor(), since it goes away when I delete this block of code.
I know this question is really stupid, but I have no idea how to fix this.
edit: here is the whole body.js:
//Importation des méthodes
import { Template } from 'meteor/templating';
import { Meteor } from 'meteor/meteor';
import { Chansons } from '../api/chansons.js'
//Importation de body
import './body.html';
//Importation des templates
import './templates/header.html';
import './templates/youTube.html';
import './templates/search.html';
import './templates/parametres.html';
import './templates/affichagePlaylist.html';
//Subscription à la collection de chansons
constructor() {
super();
this.state = {
subscription: {
chansons: Meteor.subscribe("chansons")
}
}
}
//Lancement de YouTube
if (Meteor.isClient) {
onYouTubeIframeAPIReady = function() {
player = new YT.Player("player", {
//height: "400",
//width: "600",
videoId: "fkk1vg0nAfc",
events: {
onReady: function (event) {
event.target.playVideo();
}
}
});
};
YT.load();
};
Template.body.helpers({
chansons(){
return Chansons.find({})
}
});
Template.search.events({
'click #Ajouter' : function(){
const vidURL = document.getElementById("URL").value;
Chansons.insert({
URL : vidURL
});
const URLs = Chansons.find({},{ fields: { URL: 1, _id: 0 } }).map((chanson) => chanson.URL);
console.log(URLs);
}
});
Your statement with constructor() {} outside of a class declaration is invalid syntax.
You can declare a function using the short syntax version only within the scope of an object (or a class) in ES6. They are referred to as "methods" (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions)
With Meteor's Blaze frontend, you probably want to subscribe when the template instance is created, i.e.:
Template.body.onCreated(function () {
this.subscribe("chansons"); // Similar to Meteor.subscribe, but scoped to the template instance.
});
I want to contribute to a project - it's written in Vue, and I am a beginner in Vue.
I have two components - Setup and MainApp
Both will need to update some state based on different messages from the websocket. Some websocket messages will affect the former, some the latter.
Vue doesn't know services, so I thought I'd just create a custom component, with empty <template>. instantiate the websocket there and then issue an this.emit() every time a new message occurs in the listener.
Both other components would listen to the emits and would be able to react.
Unfortunately, I can't get the websocket component to work.
main.js:
import Ws from './WsService.vue';
//other imports
const routes = [
//routes
]
const router = new VueRouter({
routes // short for `routes: routes`
})
const app = new Vue({
router
}).$mount('#app')
//I thought this to be the way to instantiate my webSocket service:
const WsService = new Vue({
el: '#WsService',
components: { Ws }
});
index.html
<body>
<div id="app">
<div id="WsService"></div>
<router-link to="/setup">Setup</router-link>
<router-link to="/main-app">Main App</router-link>
<router-view></router-view>
</div>
<script src="/dist/demo-app.js"></script>
</body>
the websocket "service":
<template>
</template>
<script>
const PORT_LOCAL = 9988;
var ws = new WebSocket("ws://localhost:" + PORT_LOCAL);
ws.onopen = function() {
ws.send('{"jsonrpc":"2.0","id":"reg","method":"reg","params":null}');
};
ws.onerror = function(e) {
console.log("error in WebSocket connection!");
console.log(e);
};
export default {
data() {
return {
}
},
created() {
var self = this;
ws.onmessage = function(m) {
var msg = JSON.parse(m.data);
switch(msg.id) {
// result for address request
case "reg":
self.$emit("reg_received", msg.result);
break;
case "send":
self.$emit("send_received", msg.result);
break;
case "subscribe":
self.$emit("subscribe_received", msg.result);
break;
default:
console.log(msg);
break;
}
}
},
methods: {
},
send(id, method, params) {
ws.send('{"jsonrpc":"2.0","id":"' + id + '","method":"' + method + '","params":null}');
}
}
}
</script>
Send for example from main app (this seems to work):
import WsSvc from './WsService.vue';
export default {
data() {
//
},
subscribe() {
let jsonrpc = "the jsonrpc string";
WsSvc.send(jsonrpc);
}
}
Listening to emit:
export default {
data() {
//
},
created() {
this.$on("reg_received", function(result){
//do smth with the result
});
}
}
Wit this configuration, the created hook actually never gets called - and thus I'll never hit the onmessage listener. The reason to have a custom component I thought was that I would have access to the emit function.
It feels I am making it more complicated than it should be but I haven't managed yet to get it right. The solution doesn't need to follow this approach.
There's no need for a socket specific component in this case. What I have done in the past on a couple projects is implement an API or store object that handles the socket messages and then import that API or store into the components that need it. Also in a similar answer, I show how to integrate a WebSocket with Vuex.
Here is an example that combines the concept of using Vue as an event emitter with a web socket that can be imported into any component. The component can subscribe and listen to the messages it wants to listen to. Wrapping the socket in this way abstracts the raw socket interface away and allows users to work with $on/$off subscriptions in a more typically Vue fashion.
Socket.js
import Vue from "vue"
const socket = new WebSocket("wss://echo.websocket.org")
const emitter = new Vue({
methods:{
send(message){
if (1 === socket.readyState)
socket.send(message)
}
}
})
socket.onmessage = function(msg){
emitter.$emit("message", msg.data)
}
socket.onerror = function(err){
emitter.$emit("error", err)
}
export default emitter
Here is an example of that code being used in a component.
App.vue
<template>
<ul>
<li v-for="message in messages">
{{message}}
</li>
</ul>
</template>
<script>
import Socket from "./socket"
export default {
name: 'app',
data(){
return {
messages: []
}
},
methods:{
handleMessage(msg){
this.messages.push(msg)
}
},
created(){
Socket.$on("message", this.handleMessage)
},
beforeDestroy(){
Socket.$off("message", this.handleMessage)
}
}
</script>
And here is a working example.
Hey this should work for you better and easy
This my example with .vue file
yourVueFile.Vue
<template>
// key in your template here
</template>
<script>
export default {
//use the created() option to execute after vue instance is created
created() {
let ws = new WebSocket("yourUrl");
ws.onopen = e => {
ws.send(
JSON.stringify({ your json code })
);
ws.onmessage = e => {
let data = JSON.parse(e.data);
// the this.$data get your data() options in your vue instance
this.$data.dom = data;
};
};
},
data() {
return {
dom: core
};
},
methods: {
}
};
</script>
I am very new to Meteor and trying to develop an online synchronous game experiment. Generally, once participants AGREE to the consent form, I want to create a user and add that user into my Players collection.
My consent_page.js looks like this:
import './consent_page.html';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { Template } from 'meteor/templating';
import { Meteor } from 'meteor/meteor'
import '../../../api/players/methods.js'
Template.consent_page.events({
'submit .consent-form'(event) {
event.preventDefault();
Meteor.call('players.addPlayer');
FlowRouter.go('/instructions')
}
});
and my players.addPlayer method looks like this
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { Players } from './players.js';
import { Random } from 'meteor/random'
Meteor.methods({
'players.addPlayer'() {
console.log('I am in the method');
username = Random.id();
user = create_user(username);
alert(username);
alert(user);
Players.insert({
_id: this.userId,
enterTime: new Date(),
status: 'instructions',
passedQuiz: false,
quizAttempts: 0,
needRematch: false,
condition: 'control'
});
}
});
/*
* Helper functions for the methods
*/
//create user in the server
create_user = function (username) {
return Accounts.createUser({
username: username,
});
};
The collection Players.js has the definition of the collection.
import { Mongo } from 'meteor/mongo'
export const Players = new Mongo.Collection('players');
However, this doesn't work. I do get redirected to the instructions page, but the user doesn't get created .. I get the following error:
Error invoking Method 'players.addPlayer': Method 'players.addPlayer' not found [404]
Although, I get the I am in the method message printed in the console. The alert with the return of create_user is undefined. Also, I want to create the users without a password (how can I do that?).
Accounts.createUser() method is a part of accounts-base package. You need to import that at first. I'm sharing a snippet from my working project. Hope this helps:
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
const bcrypt = require('bcrypt');
Meteor.methods({
'user.create':function(data){
return Accounts.createUser({
name: data.name,
username: data.userId,
password: data.password,
});
}
});
I keep on getting these error messages in my browser console:
Exception in template helper: ReferenceError: "CollectionNames" is not defined
The "CollectionNames" being all the collections I have in my app. I have researched but cant find a suitable solution.
My environment: I am running meteor 1.2
The task.js file is where I define each collection. Below is the code in task.js
/myMeteorApp
--/imports/api/tasks.js
import { Mongo } from "meteor/mongo";
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
Images = new FS.Collection("images", {
stores: [new FS.Store.FileSystem("images", {path: "~/uploads"})] });
buyList = new Mongo.Collection("BuyList");
WhoAreWe = new Mongo.Collection("whoDb");
merchantReviews = new Mongo.Collection("merchantReviews");
Messages = new Meteor.Collection("messages", {transform: function (doc)
{ doc.buyListObj = buyList.find({sessionIDz: {$in: [doc.buyList]}}); return doc; }});
The server is where I publish the collections. Below is the code:
/myMeteorApp
--/server/main.js
import buyList from '../imports/api/tasks.js';
import Messages from '../imports/api/tasks.js';
import Images from '../imports/api/tasks.js';
import WhoAreWe from '../imports/api/tasks.js';
import merchantReviews from '../imports/api/tasks.js';
Meteor.startup(() => {
// code to run on server at startup
Meteor.publish('buyList', function(){
return buyList.find();
});
Meteor.publish('Messages', function(){
return Messages.find();
});
Meteor.publish('Images', function(){
return Messages.find();
});
Meteor.publish('WhoAreWe', function(){
return WhoAreWe.find();
});
Meteor.publish('merchantReviews', function(){
return merchantReviews.find();
});
});
And the client is where I subscribe for the collections. Find below the code:
/myMeteorApp
--/client/main.js
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html';
Meteor.subscribe('Messages');
Meteor.subscribe('WhoAreWe');
Meteor.subscribe('Images');
Meteor.subscribe('buyList');
Where am I going wrong. I've been at this for many days now... Kindly help!
The collections must be defined on both the client and the server! Just import your collection names on the client side as well as the server:
import { buyList, Messages, Images, WhoAreWe, merchantReviews } from '../imports/api/tasks.js';
You still have to subscribe to the various publications of course.
It is a naming problem, when you publish the collection, you should refer to the collection name (messages), not the meteor variable (Messages)
Meteor.publish('messages', function(){...
I'm struggling to understand Meteor 1.3's include logic.
For an app I'm trying to put together, I have in /client/main.js:
import '../imports/startup/accounts-config.js';
import '../imports/ui/body.js';
import '../imports/ui/home.js';
import '../imports/ui/map.js';
import '../imports/ui/admin.js';
...
In /imports/ui/body.js, I have (I use flow router and mainLayout is the main layout, in which all other templates are rendered):
...
Template.mainLayout.onCreated(function mainLayoutOnCreated() {
Meteor.subscribe('tasks');
Meteor.subscribe('categories');
});
...
Then I have the following function:
Template.admin.helpers({
categories() {
return Categories.find({}, { sort: { createdAt: -1 } });
},
});
If I put this function in /imports/ui/body.js, I'm able to call 'categories' in the template with name 'admin'. However, when I put this function in /imports/ui/admin.js, I get a useless exception in the javascript console:
Exception in template helper: categories#http://localho...
How is it that moving the file in which this helper is declared, while still being included in the same 'main' file, results in an exception being thrown?
You should import the desired template and the templating package at the top of your admin.js file.
import {
Template
} from 'meteor/templating';
import '.deep/admin.js';
Template.admin.helpers({
categories() {
return Categories.find({}, { sort: { createdAt: -1 } });
},
});