Uncaught TypeError: $ is not a function, when trying to integrate external plugins into my Meteor app - meteor

Trying to integrate a plugin that is not available as a package into my Meteor app.
Here's the code:
import $ from "meteor/jquery";
import jQuery from "meteor/jquery"
(function($, window, document){
'use strict';
var doc = $(document);
window.notifyAlert = function(){
var $this = $(this),
onload = $this.data('onload');
if(onload !== undefined) {
setTimeout(function(){
notifyNow($this);
}, 800);
}
$this.on('click', function (e) {
e.preventDefault();
notifyNow($this);
});
}
function notifyNow($element) {
var message = $element.data('message'),
options = $element.data('options');
if(!message)
$.error('Notify: No message specified');
$.notify(message, options || {});
}
}(jQuery, window, document));
Here's the error:
Uncaught TypeError: $ is not a function
I wrapped the function to be immediately invoked in the context of the jQuery object, since I thought that will surely help solve the problem and prevent interference with the global namespace, but nope, same problem.
If you take take the immediate invocation out, obviously it won't work either. So I'm completely lost at what to do here.
I only have this error when working with Meteor and nowhere else.

Both $ and jQuery are accessible as properties of the package import, so import them using bracket notation:
import {$, jQuery} from 'meteor/jquery';
Note that depending on your file load order, you may also be able to access it from the window object inside your imported file:
const {$} = window;

Related

RequireJs Google maps google is not define

I have a simple nodejs project that should load asynchronously the google maps api javascript, i followed this answer https://stackoverflow.com/a/15796543
and my app.js is like this:
var express = require("express"),
app = express(),
bodyParser = require("body-parser"),
methodOverride = require("method-override");
https = require("https");
requirejs = require('requirejs');
requirejs.config({
waitSeconds : 500,
isBuild: true,
paths : {
'async': 'node_modules/requirejs-plugins/src/async',
}
});
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(methodOverride());
var router = express.Router();
router.get('/', function(req, res) {
res.send("Hello World!");
});
requirejs(["async!http://maps.google.com/maps/api/js?key=mykey&sensor=false"], function() {
console.log(google);
});
app.listen(3000, function() {
console.log("asd");
});
package.json:
{
"name": "rest-google-maps-api",
"version": "2.0.0",
"dependencies": {
"express": "^4.7.1",
"method-override": "^2.1.2",
"body-parser": "^1.5.1",
"requirejs": "2.3.3",
"requirejs-plugins": "1.0.2"
}
}
i've got always the same error:
ReferenceError: google is not defined
The main issue here is that you are trying to run in Node code that is really meant to be used in a browser.
The async plugin
This plugin needs to be able to add script elements to document and needs window. I see you set isBuild: true in your RequireJS configuration. It does silence the error that async immediately raises if you do not use this flag, but this is not a solution because:
isBuild is really meant to be set internally by RequireJS's optimizer (or any optimizer that is compatible with RequireJS), not manually like you are doing.
isBuild means to indicate to plugins that they are running as part of an optimization run. However, your code is using the plugin at run time rather than as part of an optimization. So setting isBuild: true is a lie and will result in undesirable behavior. The async plugin is written in such a way that it effectively does nothing if isBuild is true. Other plugins may crash.
Google's Map API
It also expects a browser environment. The very first line I see when I download its code is this:
window.google = window.google || {};
Later in the code there are references to window.document and window.postMessage.
I don't know if it is possible to run the code you've been trying to load from Google in Node. I suspect you'd most likely need something like jsdom to provide a browser-like environment to the API.
assuming you did everything else correctly, which I am not testing here. The reason you are getting the error is because you call console.log(google) and there is no google variable. You need to pass google in as a reference in your call back function. This will either get rid of the error, or change the error if you have set up requirejs incorrectly.
requirejs(["async!http://maps.google.com/maps/api/js?key=mykey&sensor=false"],
function( **google** ) {
console.log(google);
});
see the requirejs docs http://requirejs.org/docs/node.html#1

Meteor : "console" variable undefined inside require call

I'm facing a strange problem on Meteor and I can't resolve it :
I'm developping a WebRTC app using Meteor, PeerJS and AdapterJS (which give an WebRTC plugin for unsupported browser like Safari or IE). These two libs are downloaded using NPM : meteor npm install peerjs/adapterjs
So in my view's controller I have :
view.js
//import Peer from 'peerjs'; => same error with "import"
//import AdapterJS from 'adapterjs';
Template.view.onRendered(function(){
AdapterJS = require("adapterjs");
Peer = require("peerjs");
//var peerkey="..."
var peer = new Peer({
key: peerkey, // get a free key at http://peerjs.com/peerserver
debug: 3,
config: {'iceServers': [
{ url: 'stun:stun.l.google.com:19302' },
{ url: 'stun:stun1.l.google.com:19302' },
]}
});
But when I run my controller, I get an exception because "console" is undefined inside peerjs/util.js function when calling the peerjs constructor :
Uncaught TypeError: Cannot read property 'log' of undefined
Strangly, when I only require "peerjs", there is no exeption...
I tried to change the order of require functions but it won't work.
Other variable like "alert", "window.console" work and are defined inside the module but "console" not.. :/
Any suggestion can help ^^
Thanks in advance.
EDIT : If I add a breakpoint on the first line of node_module/peerjs/lib/util.js, I see that the "console" variable is "undefined" inside util.js but .... it is defined inside the caller function (fileEvaluate) !
EDIT2 : I tried something else to check if the code inside adapterjs redefine or change something : I put 'require("adapterjs")' inside a timeout function with a long delay (10 seconds) and .... console is still undefined inside peer module ! But when I comment require("adapterjs"), no error, console is defined ! I think that Meteor do something special before running the controller script depending on require functions...
EDIT3 : Here is a git repo to test the project : gitlab.com
If you display the dev console, you will see exceptions.
I found a solution, although I don't fully understand it myself. It's something to do with the way that Meteor imports modules, and Peerjs runs afoul of that.
Basically I copied node_modules/peerjs/dist/peer.js into the client directory, so that Meteor will load it "as is".
A small change to main.js as follows:
import './main.html';
// I placed peer.js from the node_modules/peerjs/dist/peer.js into the client folder and it works fine
// import {Peer} from 'peerjs';
import {AdapterJS as Adapter} from 'adapterjs';
Template.hello.onCreated(function helloOnCreated() {
// counter starts at 0
window.peer = new Peer({
and it works fine :)
I see in line 860+ of adapter.js that console is being defined (part of the shim) from https://github.com/Temasys/AdapterJS/blob/master/source/adapter.js
// IE 9 is not offering an implementation of console.log until you open a console
if (typeof console !== 'object' || typeof console.log !== 'function') {
/* jshint -W020 */
console = {} || console;
// Implemented based on console specs from MDN
// You may override these functions
console.log = function (arg) {};
console.info = function (arg) {};
console.error = function (arg) {};
This code defines the console if it doesn't find one to it's liking? Does this mean you are using IE9 or some other incompatible browser?
Try pasting this into your console and see what it tells you:
if (typeof console !== 'object' || typeof console.log !== 'function') alert("Console not present, needs to be shimmed?"); else console.log("console is ok");
Presumably the reason you are using adapter.js is for compatibility purposes - this will help you trouble shoot it. Please let me know what you find, as I will be following you down this path :)

Control getMultiCapabilities from command line

I use gulp to run protractor and want one separate task for each browser, Chrome and Firefox. So by running gulp protractor:chrome or gulp protractor:firefox I would launch different browsers. But I get my capabilities through the getMultiCapabilities and it isn't as simple as --capabilites.browserName chrome then. This is how my getMultiCapabilities-function looks like for Firefox:
'use strict';
var FirefoxProfile = require('firefox-profile');
var bbPromise = require('bluebird');
exports.getMultiCapabilities = function () {
var deferred = bbPromise.defer();
var firefoxProfile = new FirefoxProfile();
firefoxProfile.setPreference('media.navigator.permission.disabled', true);
firefoxProfile.encoded(function (encodedProfile) {
var capabilities = [{
browserName: 'firefox',
firefox_profile : encodedProfile
}];
deferred.resolve(capabilities);
});
return deferred.promise;
};
And I want to use this function by passing it in the command line somehow. I tried just requiring the function in the config file and then doing --getMultiCapabilities firefoxCapabilitiesHelper.getMultiCapabilities. Didn't work. Also tried to "stringify" the function and wrote everything inside it on the command line. Didn't work either. The arguments are ignored.
Is it even possible to do what I want to achieve?

Data context in Meteor's Template.rendered callback randomly disappears on hot code push

Short version:
Session.get(), and Template.currentData() that are supposed to be passed to the template by the Router often turn out to be undefined in Template.<templateName>.render callback. This happens quite randomly, most often on hot code pushes, but not always, and not only on hot code pushes.
Longer version:
I'm using Iron Router to pass data context to the template appBody:
Router.route('/:_mapid', function() {
if (!isNaN(this.params._mapid)) {
Session.set('currentMap',Maps.findOne({mapid: Number(this.params._mapid)}));
this.render('appBody', {
data: function() { return Session.get("currentMap") }
});
}
});
The template then uses d3 to generate a bunch of div's, and set the dynamic page title in Template.appBody.rendered callback:
Template.appBody.rendered = function() {
Deps.autorun(function() {
d3.select("#map_body").selectAll("div").remove();
d3.select("#map_body").selectAll("div")
.data(Nodes.find({mapid: Template.currentData().mapid }).fetch(), function(d) {return d.nodeparentid;})
.enter().append("div")
.attr("id", function(d){return "node"+ d.nodeparentid})
.style("position","absolute")
.style("top",function(d) {return d.toppos+"px"})
.style("left",function(d) {return d.leftpos+"px"})
.style("width",function(d) {return d.width+"px"})
.style("height",function(d) {return d.height+"px"})
.classed("node", true)
document.title = "Map - " + Session.get("currentMap").title;
}
As you can see I try to pass the data context to the template in two different ways: by setting a global currentMap object via Session.set and by passing data key via the Router, and then accessing it via Template.currentData() method.
For some reason both of these method often fail (although I still can't figure out under what conditions). Here is a sample error I get from the browser console when trying to set a document title:
Exception from Tracker afterFlush function: Cannot read property 'title' of undefined
TypeError: Cannot read property 'title' of undefined
at http://localhost:3000/client/templates/app_body.js?47b256634607ca16879aa0ed823593aec01ee840:122:31
at Tracker.Computation._compute (http://localhost:3000/packages/tracker.js?192a05cc46b867dadbe8bf90dd961f6f8fd1574f:288:36)
at new Tracker.Computation (http://localhost:3000/packages/tracker.js?192a05cc46b867dadbe8bf90dd961f6f8fd1574f:206:10)
at Object.Tracker.autorun (http://localhost:3000/packages/tracker.js?192a05cc46b867dadbe8bf90dd961f6f8fd1574f:476:11)
at Template.appBody.rendered (http://localhost:3000/client/templates/app_body.js?47b256634607ca16879aa0ed823593aec01ee840:117:10)
at null.<anonymous> (http://localhost:3000/packages/blaze.js?77c0809654ee3a10dcd5a4f961fb1437e7957d33:2970:21)
at http://localhost:3000/packages/blaze.js?77c0809654ee3a10dcd5a4f961fb1437e7957d33:1720:14
at Object.Blaze._withCurrentView (http://localhost:3000/packages/blaze.js?77c0809654ee3a10dcd5a4f961fb1437e7957d33:2029:12)
at http://localhost:3000/packages/blaze.js?77c0809654ee3a10dcd5a4f961fb1437e7957d33:1719:15
at Tracker.flush (http://localhost:3000/packages/tracker.js?192a05cc46b867dadbe8bf90dd961f6f8fd1574f:438:11)
Any ideas on what could be causing this?

Meteor/Iron-Router: how to define routes using data from settings.json

For the URL to which a route applies I have a part defined in settings.json, like this
baseUrl: '/private'
My settings are published and accessible through the collections 'Settings' (on the client). So I tried the following:
Meteor.subscribe('settings');
Deps.autorun(function () {
var settings = Settings.findOne():
if (settings) {
Router.map(function () {
this.route('project', {
path: settings.baseUrl + '/:projectId,
controller: 'ProjectController'
});
});
}
});
The problem is that during initialisation the data is not yet on the client available, so I have to wait until the data is present. So far this approach doesn't work (yet). But before spending many hours I was wondering if someone has done this before or can tell me if this is the right approach ?
Updated answer:
I published solution in repository : https://github.com/parhelium/meteor-so-inject-data-to-html
. Test it by opening url : localhost:3000/test
In this case FastRender package is useless as it injects collection data in the end of head tag -> line 63.
Inject-Initial package injects data in the beginning of head tag -> line 106.
Needed packages:
mrt add iron-router
mrt add inject-initial
Source code:
Settings = new Meteor.Collection("settings");
if (Meteor.isClient) {
var settings = Injected.obj('settings');
console.log(settings);
Router.map(function () {
this.route('postShow', {
path: '/'+settings.path,
action: function () {
console.log("dynamic route !");
}
});
});
}
if (Meteor.isServer){
if(Settings.find().count() == 0){
Settings.insert({path:"test",data:"null"});
}
Inject.obj('settings', Settings.findOne());
}
Read about security in the bottom of the page : https://github.com/gadicc/meteor-inject-initial/
OLD ANSWER :
Below solution won't work in this specific case as FastRender injects data in the end of head tag. Because of that Routes are being initialized before injected data is present.
It will work when data from Settings collection will be sent together with html.
You can do that using package FastRender.
Create file server/router.js :
FastRender.onAllRoutes(function(path) {
// don't subscribe if client is downloading resources
if(/(css|js|html|map)/.test(path)) {
return;
}
this.subscribe('settings');
});
Create also publish function:
Meteor.publish('settings', function () {
return Settings.find({});
});
The above code means that if user open any url of your app then client will subscribe to "settings" publication and data will be injected on the server into html and available for client immediately.
I use this approach to be able to connect many different domains to meteor app and accordingly sent proper data.

Resources