So here is my problem :
Currently, I have a dozen of functions related to WEBRTC within a template js file. My objective is to have those functions in a separate file, called webRTCWrapper.js for example, and to call those functions in my template without using global variable.
I think I must use namespaces, am I correct ?
If so, how do you use them ?
EDIT : For anyone interested, this is exactly what I was looking for :
http://themeteorchef.com/snippets/using-the-module-pattern-with-meteor/
Make a directory called packages/ parallel to your .meteor/ directory. You can create a package that exports a single object/function. On the command line, use meteor create --package <yourpackagename> and meteor add <yourpackagename> You can edit the js file to add a namespace.
MyNamespace = {};
MyNamespace.myFunction = function () { };
Then, in the package.js, simply export that namespace.
api.export('MyNamespace');
You can use a common pattern of having a global object and your functions inside that object.
Greetings = {
hello: function(name) { return "Hello "+name+" how are you?"; }
}
And then you can call it inside the template helpers :
Template.GreetingsTemplate.helpers({
sayHello: function() { return Greetings.hello('Maxence'); }
})
Take note of the loading order of files in Meteor, anything inside the lib folders is loaded first. If you run into problems where "Greetings" object is not defined, then its because that file was not loaded already.
Edit:
You can reuse the same pattern for adding more functions in different files (you could use App = App || {} but it will throw error in Chrome for example).
App = (typeof App === 'undefined')? {} : App;
App.someFunction = function(){};
or even, if you use underscore.js:
App = (typeof App === 'undefined')? {} : App;
_.extend(App, {
someFunction: function(){}
});
Since now the regular way to use the code from another file was going through a global (server and client). As Joao suggested you can make your own global App variable where you will store or more generically a global MODULE one (basically the same solution as Joao but with explanation).
But with with the arrival of ES2015 support, we will very soon be able to have an official pattern to achieve this. However as the 1.2 does not supports yet the import/export syntax:
Note, The ES2015 module syntax (import/export) is not supported yet in Meteor 1.2.
If you want to start using those features earlier, I would recommend using this package which is an temporary solution to fill the current import/export gap, the meteor team development are currently looking for an elegant solution to support this.
Related
I try to organise my frontend as I used to do in Rails 5. I had some js file with functions and used this functions in different places of code up to my needs. But in Rails 6 work with js is quite different. Anyway, I think I got the main idea about packs and webpacker. But how to use custom js functions? Write it in one file and use in another? There is should be the way to do it.
For example, I have some custom js pack:
app/javascript/packs/custom_pack_with_functions.coffee:
console.log 'hey'
#hi = () ->
console.log 'HI'
And I expect that hi function will be available in my view.
some_view.html.slim:
= javascript_pack_tag 'custom_pack_with_functions'
javascript:
hi()
But when I come to appropriate page, I see in console only following messages:
hey
ReferenceError: hi is not defined
How to define hi function to use it from anywhere?
Webpack does not make modules available to the global scope by default. Here are a few ways you can do it:
Assign the function to the global window object, i.e., window.hi = function() { ... }. I don't like side effects like this in a lot of places so it's my least favorite option but perhaps the easiest to understand.
You could look at using expose-loader. This would mean customizing your webpack config to "expose" selected functions from selected modules to the global scope. It could work well for a handful of cases but would get tedious for many use cases.
Export selected functions from your entrypoint(s) and configure webpack to package your bundle as a library. This is my favorite approach if you prefer to call global functions from the view. I've written about this approach specifically for Webpacker on my blog.
// app/javascript/packs/application.js
export * from '../myGlobalFunctions'
// config/webpack/environment.js
environment.config.merge({
output: {
// Makes exports from entry packs available to global scope, e.g.
// Packs.application.myFunction
library: ['Packs', '[name]'],
libraryTarget: 'var'
},
})
:javascript
Packs.application.hi()
Don't use global functions at all; use a different mechanism to trigger the function from within your webpack JS, such from within an event listener for the given page or in the presence of a given element.
// app/javascript/initializer.js
import hi from '../hi';
document.addEventListener('DOMContentLoaded', () => {
if ( /* some logic for my page is true */ ) {
hi()
}
});
I am using Node.js. I defined a custom method to the String obj like this:
if (!String.prototype.myMethod) {
String.prototype.myMethod= function () {
//do something
return this;
};
}
I found that myMethod maybe used in many different files, so that I have to require the file where this piece of code in. Is there any way that does the many 'requires' ?
Don't do that.
Node is intentionally designed in a module pattern, where each module gets it's own scope to run in and without polluting the global variables. This very intentional and very important.
https://nodejs.org/api/modules.html#modules_the_module_wrapper
Last time I built a large-scale application with JS I used require.js - which is great, but can be quite an overhead, especially if you don't want to load files asychronously, so this time I'm going without it.
This time I'm writing in pure JS and concatenating and minifying everything with Grunt (I'm a Grunt n00b here). Because I'm keeping all the functions in separate files, I can't wrap everything in a closure like I could if I was using a single file. What's the best solution to keeping all functions out of the global namespace?
I'm not sure I need a full dependency management solution, but I'd consider one if it's lightweight and simple.
If you want to do it without any dependency management tool you can for example use the Revealing Module Pattern and namespaces, simplified example:
Top/Application file
window.SomeApplication = (function () {
// Add functions you want to expose to this
this.require= function (path) { // Creates namespace if not already existing, otherwise returns reference to lowest level object in path
var current = window,
i;
path = path.split('.');
for (i = 0; i < path.length; ++i) {
if (!current[path[i]]) {
current[path[i]] = {};
}
current = current[path[i]];
}
return current;
};
return this;
})();
Some other file
SomeApplication.require('SomeApplication.SomeSubNamespace').SomeModule = (function () {
// Module code
return this;
})();
Then use your grunt concat and specify the top file first. This way you will only expose one item on the window object and your module will be accessible through window.SomeApplication.SomeSubNamespace.SomeModule.
There are a number of common and easy to use JavaScript tools for managing application-wide dependencies by either implementing the CommonJS (the specification used by require.js) or the ES2015 module specification, including (as Benjamin suggested) Webpack, Browserify, and Rollup.
I am trying to access a togetherJS (https://togetherjs.com/docs/contributing.html) module from an external requireJS app. It seems impossible.
TogetherJSConfig_noAutoStart = true;
var CJS = CJS || {};
CJS.require = require.config({
paths: {
togetherjs: 'https://togetherjs.com/togetherjs-min',
cjs: 'scripts/c'
}
});
CJS.require(['togetherjs'], function() {
// not working
peers = require({context: "togetherjs"})("peers");
// not working
// Module name "peers" has not been loaded yet for context: togetherjs. Use require([])
TogetherJS.require = require.config(TogetherJS.requireConfig);
TogetherJS.require("peers");
});
If I do not use requireJS in my app, I can access the module I want:
// works if I do not use requireJS in my app.
var peers = TogetherJS.require('peers').getAllPeers();
Is it possible, and if so, how? I could not find any information anywhere.
Thanks
I'm not entirely sure of what you are trying to accomplish here. It looks like you are mixing up require.js asynchronous api with node.js synchronous require api.
I think that when you do require.config without specifying a context name, you are configuring the global require context. So calling CJS.require(..) should be equivalent to calling window.require(..).
The line
peers = require({context: "togetherjs"})("peers");
looks strange to me. Usually you would specify the context name in the require.config call and the first parameter to a require(..) call would be the dependency list. Also, you would normally pass a callback function to the require(..) call as well.
I hope those pointers can help you (or others) get better acquainted with require.js
In Meteor we normally attach javascript functions to Templates. Where do we place standard javascript functions?
For instance, in one of my apps I have a UserInfo.js file which has a bunch of javascript functions for handling users logging in and getting user information.
Below are two of my functions in UserInfo.js
File is located in the client/scripts folder:
isAdminById = function(userId) {
var user;
user = Meteor.users.findOne(userId);
return user && isAdmin(user);
};
isAdmin = function(user) {
if (!user || typeof user === 'undefined') {
return false;
} else {
return !!user.isAdmin;
}
};
When I run the app and call isAdmin() from the browser console it says:
ReferenceError: isAdmin is not defined
---- Edit ----
It seems the problem was fixed temporarily when I placed the javascript file under the client/compatibility folder but now the issue has resurfaced. The only thing I remember changing was calling >> Meteor Reset
More Info:
I think the issue arises when I use coffeescript. When I convert my coffeescript files to js files everything seems to work.
You need to declare coffeescript variables as global with #:
#isAdmin = user -> ...
This is due to how Meteor variable shadowing works in connection with coffeescript automatic variable declaration.
Coffeescript by default does the "smart" variable declaration by itself - basically by placing var variableName in the first place in javascript where the variable is visible. In your case, this causes isAdmin to be declared by var in js, and therefore it's scoped to the file.
Using # char supersedes this default behavior by binding the variable to this, global or window object instead.
Your code is correct, it's probably a load order problem.