Hi guys I am writing a meteor app and using the ddp.js v0.6.0 to make my frontend with static html. Now I need a way to get my session connection id in the front with ddp.js, is there anyway to do it? In meteor I can just do Meteor.connection._lastSessionId, is there any equivalent of this in ddp.js?
DDP.prototype._on_connected = function (data) {
var self = this;
var firstCon = self._reconnect_count === 0;
var eventName = firstCon ? "connected" : "reconnected";
self.readyState = 1;
self._reconnect_count = 0;
self._reconnect_incremental_timer = 0;
**
// Set the session ID here
self.sessionId = data.session;
**
var length = self._queue.length;
for (var i=0; i<length; i++) {
self._send(self._queue.shift());
}
self._emit(eventName, data);
// Set up keepalive ping-s
self._ping_interval_handle = setInterval(function () {
var id = uniqueId();
self._send({
msg: "ping",
id: id
});
}, self._ping_interval);
};
This is how it can be done.
https://forums.meteor.com/t/ddp-js-get-connection-id/29136/4
Related
I'm using npm ws package for websocket. I'm not being able send message into chatroom. any idea how to do it. Below is my code with sendMessage() and broadcast()
const sendMessage = (room_name, message, socket) => {
// rooms[room_name].message = JSON.stringify(message);
const obj = rooms[room_name];
for(i=0;i<obj.length;i++){
var temp = obj[i];
for(var innerObject in temp){
var wsClientID = temp[innerObject];
if(socket!==wsClientID){
wsClientID.send(JSON.stringify({
'message':message,
}));
}
}
}
// rooms[room_name].message = message;
socket.send(JSON.stringify(message));
// rooms[room_name].message = message;
}
socket.on("message", async (data) => {
broadcast(data)}
function broadcast(data) {
var count = 0;
for (const client of server.clients) {
if (client.readyState === socket.OPEN) {
count++;
client.send(data.toString())
}
}
}
I want to get the last deviceId.
Please try the following code on a smartphone.
https://www.ofima.ch/file1.html
Inside the function "getConnectedDevices" the variable deviceId ok.
But outside is returned a promise and not the variable deviceId.
How can I get the variable deviceId ?
Thanks
Miche
Explanation:
You need to wrap your alert in an async function and use await. Also to take the value from the promise you needed .then(). Hope the below helps.
Original code:
async function getConnectedDevices() {
var index;
const devices = await navigator.mediaDevices.enumerateDevices();
for (var i=0; i<devices.length; i++) {
if (devices[i].kind == "videoinput") {
index = i;
}
}
var deviceId = devices[index].deviceId;
alert('deviceId is ok: ' + deviceId);
return (deviceId);
}
const deviceId = getConnectedDevices();
alert('deviceId is not defined, why ?: ' + deviceId);
New code:
async function getConnectedDevices() {
let index;
const devices = await navigator.mediaDevices.enumerateDevices();
for (let i=0; i < devices.length; i++) {
console.debug(devices[i]);
if (devices[i].kind == "videoinput") {
index = i;
}
}
console.log('deviceId is ok: ', devices[index]);
return devices[index].deviceId;
}
(async() => {
const deviceId = await getConnectedDevices().then();
alert(`deviceId: ${deviceId}`);
})();
And a quick hack for storing the deviceId in the window
console.log('globalDeviceId should be undefined', window.globalDeviceObj);
async function getConnectedDevices() {
let index;
const devices = await navigator.mediaDevices.enumerateDevices();
for (let i = 0; i < devices.length; i++) {
console.debug(devices[i]);
if (devices[i].kind == "videoinput") {
index = i;
}
}
console.log('deviceId is ok', devices[index]);
return devices[index];
}
function getDeviceId() {
(async() => {
window.globalDeviceObj = await getConnectedDevices().then();
console.log(`globalDeviceId set: ${JSON.stringify(window.globalDeviceObj)}`);
})();
}
function tick() {
if(typeof window.globalDeviceObj === 'undefined'){
requestAnimationFrame(tick);
}else {
alert(`globalDeviceId get: ${JSON.stringify(window.globalDeviceObj)}, with deviceId: ${(window.globalDeviceObj.deviceId)}`)
}
}
function init() {
tick();
getDeviceId();
}
init();
My problem is that some autoruns are fired many times, and i'd like to have a way to quickly check the source.
I wanted to know if something like that was possible :
let reactive_var_1 = new ReactiveVar();
let reactive_var_2 = new ReactiveVar();
let reactive_var_3 = new ReactiveVar();
let reactive_var_4 = new ReactiveVar();
Template.test.onCreated(function () {
console.log('Template.test.onCreated...');
this.autorun(function () {
console.log('Template.test.onCreated.autorun...');
console.log('autorun source is : ');
console.log(source);
let do_something_1 = reactive_var_1.get() + 1;
let do_something_2 = reactive_var_3.get() + 2;
let do_something_3 = reactive_var_3.get() + 3;
let do_something_4 = reactive_var_4.get() + 4;
});
});
Template.test.events({
'click .something': function () {
console.log('Someone clicked !!');
reactive_var_3.set(12);
}
});
Output console should look like this :
Template.test.onCreated...
Template.test.onCreated.autorun...
autorun source is :
undefined
Someone clicked !!
Template.test.onCreated.autorun...
autorun source is :
'reactive_var_3'
This is the source function i'm looking for, which could output my 'reactive_var_3'
Thanks for your help :)
You could introduce another cache variable that keeps track of the state of the other variables:
const _cache = {};
const vars = {
reactive_var_1 : new ReactiveVar();
reactive_var_2 : new ReactiveVar();
reactive_var_3 : new ReactiveVar();
reactive_var_4 : new ReactiveVar();
};
In each autorun you could check, which of the ractive variables does not match with the cache anymore. The following function provides such a feature:
function diff() {
const diffs = [];
Object.keys(vars).forEach( key => {
const reactiveVar = vars[key];
const value = reactiveVar.get();
// if value is different from state
// it has caused an autorun
if (_cache[key] !== value) {
diffs.push(key);
}
// update cache
_cache[key] = value;
});
return diffs;
}
In your autorun you can just call the diff methods and get the keys (names) of the variables that caused the tracker to run again:
Template.test.onCreated(function () {
console.log('Template.test.onCreated...');
this.autorun(function () {
console.log('Template.test.onCreated.autorun...');
console.log('autorun source is : ');
console.log(diff());
let do_something_1 = reactive_var_1.get() + 1;
let do_something_2 = reactive_var_3.get() + 2;
let do_something_3 = reactive_var_3.get() + 3;
let do_something_4 = reactive_var_4.get() + 4;
});
});
Note that the cache is immediately updated in the diff function. If this limits your use case, you may extract it into a separate function like updateCache or something.
Also note, that _cache exists outside the template, which can be a problem when using several instances of the same template. You may then switch to another approach and keep the _cache and vars as local variables within the to the template instance:
Template.test.onCreated(function () {
console.log('Template.test.onCreated...');
const _cache = //...
const vars = //...
function diff(){ /* ... */ }
this.autorun(function () {
console.log('Template.test.onCreated.autorun...');
console.log('autorun source is : ');
console.log(diff());
let do_something_1 = reactive_var_1.get() + 1;
let do_something_2 = reactive_var_3.get() + 2;
let do_something_3 = reactive_var_3.get() + 3;
let do_something_4 = reactive_var_4.get() + 4;
});
});
PROBLEM: I want to parse the elements in a page from another website, glue resulting elements in an object and insert it in a Mongo collection. Before insertion i want to check if my Mongo yet has an identical object. If it does it shall exit the running functions, otherwise i want the script to start parsing the next target.
Example:
I have a function that connects to a webpage and returns its body content
It is parsed
When <a></a> elements are met, another callback is called in which all parsed elements are merged in one object and inserted in a collection
My code :
var Cheerio = Meteor.npmRequire('cheerio');
var lastUrl;
var exit = false;
Meteor.methods({
parsing:function(){
this.unblock();
request("https://example.com/", Meteor.bindEnvironment(function(error, response, body) {
if (!error && response.statusCode == 200) {
$ = Cheerio.load(body);
var k = 1;
$("div.content").each(function() {
var name = $...//parsing
var age = $....//parsing
var url = $...//parsing <a></a> elements
var r = request("https://example.com/"+url, Meteor.bindEnvironment(function(error, response, body) {
lastUrl = response.request.uri.href;// get the last routing link
var metadata = {
name: name,
age: age
url: lastUrl
};
var postExist;
postExist = Posts.findOne(metadata); // return undefined if doesnt exist, AND every time postExist = undefined ??
if (!postExist){
Posts.insert(metadata);// if post doesnt exist (every time go here ??)
}
else {
exit = true; // if exist
}
}));
if (exit === true) return false;
});
}
}));
}
});
Problem 1 : The problem is my function works every time, but it doesn't stop even if the object exists in my collection
Problem 2 : postExist is always undefined
EDIT : The execution must stop and wait until the second request's response.
var url = $...//parsing <a></a> elements
//STOP HERE AND WAIT !!
var r = request("https://example.com/"+url, Meteor.bindEnvironment(function(error, response, body) {
Looks like you want the second request to be synchronous and not asynchronous.
To achieve this, use a future
var Cheerio = Meteor.npmRequire('cheerio');
var Future = Meteor.npmRequire('fibers/future');
var lastUrl;
var exit = false;
Meteor.methods({
parsing:function(){
this.unblock();
request("https://example.com/", Meteor.bindEnvironment(function(error, response, body) {
if (!error && response.statusCode == 200) {
$ = Cheerio.load(body);
var k = 1;
$("div.content").each(function() {
var name = $...//parsing
var age = $....//parsing
var url = $...//parsing <a></a> elements
var fut = new Future();
var r = request("https://example.com/"+url, Meteor.bindEnvironment(function(error, response, body) {
lastUrl = response.request.uri.href;// get the last routing link
var metadata = {
name: name,
age: age
url: lastUrl
};
var postExist;
postExist = Posts.findOne(metadata); // return undefined if doesnt exist
if (!postExist) {
Posts.insert(metadata);// if post doesnt exist (every time go here ??)
fut.return(true);
} else {
fut.return(false);
}
}));
var status = fut.wait();
return status;
});
}
}));
}
});
You can use futures whenever you can't utilize callback functions (e.g. you want the user to wait on the result of a callback before presenting info).
Hopefully that helps,
Elliott
This is the opposite :
postExist = Posts.findOne(metadata); // return undefined if doesnt exist > you're right
if (!postExist){ //=if NOT undefined = if it EXISTS !
Posts.insert(metadata);
}else {
exit = true; // if undefined > if it DOES NOT EXIST !
}
You need to inverse the condition or the code inside
I'm a bit new to Meteor and something I'm having trouble with is reactive data -- particularly in instances where I need to change the data shown based on a mouse or keyboard event. Doing this kind of stuff the normal js way seems to give me trouble in meteor since everything I change gets re-rendered and reset constantly.
So, I thought I'd see if this would be a case in which I could use Meteor's Deps object, however I can't quite grasp it. Here's the code I'm using:
(function(){
var tenants = [];
var selectedTenant = 0;
var tenantsDep = new Deps.Dependency;
Template.tenantsBlock.tenantsList = function()
{
tenants = [];
var property = $properties.findOne({userId: Meteor.userId(), propertyId: Session.get('property')});
var tenancies = _Utils.resolveTenancies(property, true, null, true);
for(var i = 0; i < tenancies.length; i++)
{
if(tenancies[i].tenancyId == Session.get('tenancy'))
{
tenants = tenants.concat(tenancies[i].otherTenants, tenancies[i].primaryTenant);
}
}
tenants[selectedTenant].selected = 'Selected';
tenantsDep.changed();
return tenants;
};
Template.tenantsBlock.onlyOneTenant = function()
{
tenantsDep.depend();
return tenants.length > 1 ? '' : 'OneChild';
};
Template.tenantsBlock.phoneNumber = function()
{
tenantsDep.depend();
for(var i = 0; i < tenants[selectedTenant].details.length; i++)
if(_Utils.getDynamicContactIconClass(tenants[selectedTenant].details[i].key) == 'Phone')
return tenants[selectedTenant].details[i].value;
return null;
};
Template.tenantsBlock.emailAddress = function()
{
tenantsDep.depend();
for(var i = 0; i < tenants[selectedTenant].details.length; i++)
if(_Utils.getDynamicContactIconClass(tenants[selectedTenant].details[i].key) == 'Email')
return tenants[selectedTenant].details[i].value;
return null;
};
Template.tenantsBlock.addedDate = function()
{
tenantsDep.depend();
return _Utils.timeToDateString(tenants[selectedTenant].created);
};
Template.tenantsBlock.events({
'click .Name': function(e, template)
{
tenantsDep.depend();
var _this = e.currentTarget;
var tenantName = _this.innerHTML;
$(_this).addClass('Selected');
$(_this).siblings().removeClass('Selected');
for(var i = 0; i < tenants.length; i++)
{
if(tenants[i].name == tenantName)
tenants[i].selected = "Selected";
else
tenants[i].selected = '';
}
}
})
})();
^This seemed to be what they were getting at in the meteor documentation (http://docs.meteor.com/#deps_dependency) for dependency.changed() and dependency.depend(), but all this does is give me an infinite loop.
So can I modify the way I declare deps to get this to make data reactive? Is there a better way to do this all together?
UPDATE:
Although I was skeptical to do so, I've been inclined to try to use Session.set/Session.get in a localized way. So, the next time I have to do this, I'll just do
Session.set('tenantsBlock' {tenants: [], selectedTenant: 0});
and then just access this variable from within helpers and event maps related to Template.tenantsBlock. That way they all have real time access to the data and they all get re-run when the data changes. Here's what I converted this script into (sorry these are both so large):
(function()
{
Template.tenantsBlock.created = Template.tenantsBlock.destroyed =function()
{
_Utils.setSession('tenantsBlock', {
tenants: [],
selectedTenant: 0
})
};
Template.tenantsBlock.tenantsList = function()
{
var localContext = Session.get('tenantsBlock');
localContext.tenants = [];
var property = $properties.findOne({userId: Meteor.userId(), propertyId: Session.get('property')});
var tenancies = _Utils.resolveTenancies(property, true, null, true);
for(var i = 0; i < tenancies.length; i++)
{
if(tenancies[i].tenancyId == Session.get('tenancy'))
{
localContext.tenants = localContext.tenants.concat(tenancies[i].otherTenants, tenancies[i].primaryTenant);
break;
}
}
localContext.tenants[localContext.selectedTenant].selected = 'Selected';
Session.set('tenantsBlock', localContext);
return localContext.tenants;
};
Template.tenantsBlock.onlyOneTenant = function()
{
var localContext = Session.get('tenantsBlock');
return localContext.tenants.length > 1 ? '' : 'OneChild';
};
Template.tenantsBlock.phoneNumber = function()
{
var localContext = Session.get('tenantsBlock');
for(var i = 0; i < localContext.tenants[localContext.selectedTenant].details.length; i++)
if(_Utils.getDynamicContactIconClass(localContext.tenants[localContext.selectedTenant].details[i].key) == 'Phone')
return localContext.tenants[localContext.selectedTenant].details[i].value;
return null;
};
Template.tenantsBlock.emailAddress = function()
{
var localContext = Session.get('tenantsBlock');
var selectedTenantDetails = localContext.tenants[localContext.selectedTenant].details;
for(var i = 0; i < selectedTenantDetails.length; i++)
if(_Utils.getDynamicContactIconClass(selectedTenantDetails[i].key) == 'Mail')
return selectedTenantDetails[i].value;
return null;
};
Template.tenantsBlock.addedDate = function()
{
var localContext = Session.get('tenantsBlock');
return _Utils.timeToDateString(localContext.tenants[localContext.selectedTenant].created);
};
Template.tenantsBlock.events({
'click .Name': function(e, template)
{
var localContext = Session.get('tenantsBlock');
var _this = e.currentTarget;
var tenantName = _this.innerHTML;
for(var i = 0; i < localContext.tenants.length; i++)
{
if(localContext.tenants[i].name == tenantName)
{
localContext.tenants[i].selected = 'Selected';
localContext.selectedTenant = i;
}
else
{
localContext.tenants[i].selected = '';
}
}
Session.set('tenantsBlock', localContext);
}
})
})();
You'll have to overcome the old-school way of doing it :) Meteor is a lot simpler than you think. A good rule of thumb is that if you're using jQuery to manipulate any DOM elements, you're probably doing it wrong. Additionally, if you're accessing any data without using the collection API, you'd better have good reason to do so.
In your case, you don't need to code up any manual dependencies at all. Manual dependencies are rarely needed in most Meteor applications.
The first thing you need to do is put all your tenants inside a Meteor.Collection, which will make them easier to work with.
Tenants = new Meteor.Collection("tenants");
Your tenantsBlock template should look something like this (modulo some different html elements):
<template name="tenantsBlock">
<ol>
{{#each tenants}}
<li class="name {{selected}}">
<span>Primary Tenant: {{primaryTenant}}</span>
<span>Other Tenants: {{otherTenants}}</span>
<span>Phone Number: {{phoneNumber}}</span>
<span>Email Address: {{emailAddress}}</span>
<span>Added Date: {{addedDate}}</span>
</li>
{{/each}}
</ol>
</template>
Each document in Tenants should look something like the following:
{
primaryTenant: "Joe Blow",
otherTenants: "Mickey Mouse, Minnie Mouse",
phoneNumber: "555-234-5623",
emailAddress: "joe.blow#foo.com",
addedDate: "2005-10-30T10:45Z"
}
Then, all the code you would need is just for the selection/deselection, and you can delete everything else:
Template.tenantsBlock.tenants = function() {
return Tenants.find();
};
Template.tenantsBlock.selected = function() {
return Session.equals("selectedTenant", this._id);
};
Template.tenantsBlock.events({
'click .name': function(e) {
Session.set("selectedTenant", this._id);
}
});
Once again, I reiterate that you should never be doing DOM manipulations with Javascript when using Meteor. You just update your data and your templates will reactively update if everything is done correctly. Declare how you want your data to look, then change the data and watch the magic.
Meteor has really evolved since I posted this back in 2013. I thought
I should post a modern, superior method.
For a while now you've been able to create a ReactiveVar and now you can append those directly to templates. A ReactiveVar, similar to Session, is a reactive data store. ReactiveVar, however, holds only a single value (of any type).
You can add ReactiveVar to the client side of your project by running this in your terminal from your app's root directory:
$meteor add reactive-var
This javascript shows how you can pass the variable between the template's onCreated, onRendered, onDestroyed, events and helpers.
Template.myTemplate.onCreated = function() {
// Appends a reactive variable to the template instance
this.reactiveData = new ReactiveVar('Default Value');
};
Template.myTemplate.events({
'click .someButton': (e, template) => {
// Changes the value of the reactive variable for only this template instance
template.reactiveData.set('New Value');
},
});
Template.myTemplate.helpers({
theData: () => {
// Automatically updates view when reactive variable changes
return Template.instance().reactiveData.get();
},
});
This is superior for a few reasons:
It scopes the variable only to a single template instance. Particularly useful in cases where you might have a dozen instances of a template on a page, all requiring independent states.
It goes away when the template goes away. Using ReactiveVar or Session variables you will have to clear the variable when the template is destroyed (if it is even destroyed predictably).
It's just cleaner code.
Bonus Points: See ReactiveDict for cases in which you have many instances of a template on a page at once, but need to manage a handful of reactive variables and have those variables persist during the session.