Best Place for This Function


#1

I have a function called urlHasProtocol(), which simply ensures that any URLs have either ‘http://’ or ‘https://’ at the beginning of the string, before allowing them to be saved to Mongo.

Originally, I defined it in a call to Template.registerHelper(), so it would be globally available, and I put it in /client/helpers/main.js.

But it wasn’t available. Meteor threw an error in the console saying it urlHasProtocol() was undefined.

So I moved it to a new file: /lib/validators.js

But, to my surprise, I’m still getting the same error, that urlHasProtocol() is undefined.

I read the File Load Order section in the docs, but I must be missing something. Can anyone see what I’m missing, why the function is not being defined (or not being defined before my code that calls it is)?

Here is the code that calls urlHasProtocol():

/client/helpers/post_submit.js

Template.postCreate.events({ 'submit form': function(e) { // Prevent form from being submitted over HTTP e.preventDefault();
var post = {
title: $(e.target).find(’[name=title]’).val(),
url: $(e.target).find(’[name=url]’).val()
};

// Ensure URL begins with HTTP or Secure HTTP protocol
if ( ! urlHasProtocol(post.url)) {
  post.url = 'http://' + post.url;
}  

Meteor.call('postInsert', post, function(error, result) {
  // Display the error to the user and abort
  if (error)
    return throwError(error.reason);
    
  // If a Post with the submitted URL already exists, route the user
  // to the previous post instead
  if (result.postExists)
    throwError('This link has already been posted.');
  
  Router.go('postPage', {_id: result._id});  
});

}
});
`


#2

replace

if ( ! urlHasProtocol(post.url)) {

with

if ( ! Blaze._globalHelpers['urlHasProtocol'](post.url)) {

The issue here is that templateHelpers are not global in the ‘this’ context of your events, they are global in templates, not in the rest of your code.

OR

Create a global namespace where you declare that function and reference that function in your template helper

Global.util.urlHasProtocol = function () {...}

Template.registerHelper('urlHasProtocol', Global.util.urlHasProtocol);

// use anywhere
if ( ! Global.util.urlHasProtocol ...)

// use in any templates
{{ urlHasProtocol url }}

#3

Make sure your function doesn’t have var = when you define/declare it, or it willl be local to that file only.

i.e. do this:

urlHasProtocol = function (postUrl) {
  ...
}

not this:

var urlHasProtocol = function (postUrl) {
  ...
}

when declaring the function.


#4

Oh ok, I see. So, you’re both saying it’s a namespace and/or scope issue.

I thought that, because the docs say Template.registerHelper() defines a helper which can be used from all templates, that it was automatically global.

Where can I read more about Blaze._globalHelpers? I didn’t see it in the docs. And about creating namespaces? Is defining namespaces like the Global one in your example a Meteor convention? Or just vanilla JavaScript?

I’ve only been using Meteor now for about 3 weeks. I need to spend more time reading through the full API docs.

@babrahams Good call. I actually defined it as a named function e.g. function urlHasProtocol() { ... } for that reason. But that’s a good point.


#5

Nope. Just within a template’s scope.

There are a couple of places where I found it when looking for something similar. I don’t like using _variables though which is why I do the latter, declaring it globally and having my own reference to them.

Global variables are a thing with meteor. As are Sessions. If you do a search, you’ll find alot of information on these two topics because it’s a bit of an anti-convention but if you start using non-globals, you start to go down a rabbit hole of how do I reference this particular scope when I’m in that place. The problem with templates is that there is alot of scopes. Each template and nested template has it’s own scope, IR functions have their own, hooks and helper functions have their own, events as well. And between all this, there isn’t a good way for them to talk to each other. I’ve been at meteor for 2 months? and about half of that is trying to figure out scope which isn’t nice… angular was alot nicer in this respect.

So I’d recommend stick with globally declared stuff until you start to see problems (two templates sharing the same state when it should be different) then start looking at reactive vars, template.data, IR controller, etc…


#6

Blaze.registerHelper puts the helper in the global scope, accessible from any template.


#7

looks like he was trying to access it from a event callback


#8

@mordrax, @babrahams: That’s right, I did use Blaze.registerHelper (well , Template.registerHelper, specifically, but I suppose it’s the same thing (no?)), and I was trying to access it from a template helper, unsuccessfully.

Global variables are a thing with meteor. As are Sessions. If you do a search, you'll find alot of information on these two topics because it's a bit of an anti-convention but if you start using non-globals, you start to go down a rabbit hole of how do I reference this particular scope when I'm in that place. The problem with templates is that there is alot of scopes. Each template and nested template has it's own scope, IR functions have their own, hooks and helper functions have their own, events as well. And between all this, there isn't a good way for them to talk to each other. I've been at meteor for 2 months? and about half of that is trying to figure out scope which isn't nice... angular was alot nicer in this respect.

I hear what you’re saying. I’m new to Meteor also (about three weeks in), and while I really (really) like it, I am finding the hardest thing to wrap my head around is the scope landscape. It seems like there are a lot of “gotchas” (so to speak) in terms of scope that aren’t obvious to a Meteor beginner, but that you must learn about as you go.

I hear what you’re saying about the convenience of global variables. But I agree with the philosophy that advocates avoiding pollution of the global scope with a lot of global variables. And, that’s not to say I believe they should never be used - definitely, there are times when they are appropriate, or even the only logical option, in certain circumstances.

With that said, this is my first time (with Meteor) in using JavaScript on the server. All of my experience with JavaScript is from front-end development with various frameworks, and I think the philosophy of avoiding global scope pollution with a bunch of global variables grew out of the nature of JavaScript on the client, where the global scope is the browser’s window object. If all the framework authors were heavily dependent on using the global scope for variables, there would have been a lot of naming collisions.

So, I’m not sure how big of a problem it is on the server. In fact, I’m not even sure what takes the place of the window object at the global scope level on the server. Maybe it’s not an issue.

But since I’m kind of in the habit of avoiding putting new variables in the global scope whenever possible, maybe the best solution for simple variables you need access to globally (in templates, template helpers, and other scripts in Meteor) is to put them in Minimongo on the client, or Mongo on the server, or some other type of datastore.

For functions you want to have access to from everywhere throughout your app, I’m not sure. I thought, from reading the docs and Discover Meteor, based on the special directory names and the official file loading order, the best place to put functions that should be accessible everywhere was in a directory named lib (at any level within your app’s directory hierarchy).

So, anyway, I moved the urlHasProtocol() function to a new file, /lib/validators.js, and re-defined it as an anonymous function: i.e. var urlHasProtocol = function() { ... }. At first when I moved it to that file, it was defined as a non-anonymous, normal function (i.e. function urlHasProtocol() { ... } ), but Meteor complained that it was undefined:

Uncaught ReferenceError: urlHasProtocol is not defined

That was odd, I thought also. I’m not sure why it couldn’t find it when it was defined as a named, non-anonymous function. But it works as an anonymous function, so I’m happy with that.

Maybe the Meteor developers would like to weigh in on the best place to put (and the best way to define) functions that need to be accessible in every scope, on the client as well as the server? Is the lib directory a good place for that? Is it correct that such functions must be defined anonymously and assigned to a variable?

Thanks


#9

The MDG (Meteor Development Group) has said on this forum that they are interested in how we as a community come up with best practices for such things rather than give us best practices. There are those that like to put everything in packages, some want to put everything in modules, I think lib is good enough.

Global = {};
Global.util // util functions
Global.schema // smart schemas
Global.enums
Global.staticStrings // like help messages

db = {};
db.Collections

No. It’s because you used var a = function () {...} but function a() gets translated to a = function () {...} as per regular javascript. These two mean different things to meteor.


#10

The MDG (Meteor Development Group) has said on this forum that they are interested in how we as a community come up with best practices for such things rather than give us best practices

Wow, that’s nice to hear. And, now that you mention it, I suddenly remembered reading in Discover Meteor that there were multiple philosophies regarding how to structure apps. I like that Meteor doesn’t enforce strict conventions in that regard.

I would like to learn more about package-oriented app structure. I really like loosely-coupled APIs. At a company I used to work for, we tried to build as many of our internal backend services in a microservices-type of architecture, so that various apps (and various parts of a single app) made API calls to get things done, rather than loading components/modules. It worked well and made updating any single piece of functionality nice and easy, with (virtually) no breaking things.

Not that microservice architecture has anything to do with Meteor’s package structure; but it does sound nice and loosely coupled.

I didn’t catch on the first time you mentioned your top-level Global object, but now I see what you mean, and I can see how that would make a very nice, encapsulated structure for your data. It also appears to work as a nice adhoc namespace system, since you’ll never have trouble figuring out how to refer to various bits of data throughout your app with that approach. I may have to steal that idea from you.

D’oh! That’s right. I got in a habit of always using var declarations from client-side JavaScript development, and I keep forgetting how it affects variable scope in Meteor. Thanks for the reminder.


#11

Meh, double edged sword IMO, leaves alot of newbies like us hanging. I kinda preferred angular’s this is how we’re going to do it, even though it made no sense to anyone. Less irrelevant choices makes me happy :smile:

Believe me, when you get into sub/child/sibling templates, 3rd party packages etc, this is such a time saver… However, I have to say that I don’t practice what I preach which is why I’m a week into it with nothing to show my boss :frowning:

Ideas are like the flames on a candle. BYO and we’re all happy :slight_smile:

That’s cool, I actually had to look that up on SO and meteor docs and re-learnt it myself! I swear that answering questions actually helps me more than asking them sometimes :wink:


#12

Yep, there is that. I guess each approach has its pros and cons. In frameworks like Django, Yii, and Rails, there were times that I liked the structure and conventions it imposed, because you didn’t have to spend much time planning how to structure your application, or which component should be responsible for this vs that. But at other times, like when you needed to implement some feature that the framework didn’t support out of the box, it could be too constraining.

Anyway, I’m glad I found Meteor. I don’t miss having to manage a huge sprawling set of jQuery files and plugins, on top of a server-side app. It was like developing two separate apps for every project, almost.

Well, I hope to continue to learn from your experiences building more complex apps with Meteor. As soon as I finish the Discover Meteor book and the Microscope app, I have plans to build an app to manage my class schedule, assignments, and lecture notes. I know there are a ton of apps for taking notes and to-do style apps specifically for students, but none of them have all the features I want in a single app. So, I’m looking forward to that project. It would be fun to build something like Trello using Meteor.

I also want to use Meteor as a front-end to some hardware projects I have, like a Beaglebone home automation UI, and a remote control UI and data monitoring interface for a robot I’m building with this thing. Node has really become popular in the hardware DIY community because it excels at event-driven applications, so I’d like to try it. I probably should have learned Node before getting into Meteor, but Meteor was too compelling to wait for.

I also want to try building some iPhone applications.

I hear that. I’ve been working with JavaScript for years, but my focus was always on the back-end stuff, so I never really learned it backwards and forwards. I can get by, but I’m on MDN all the time and definitely am not going to be writing the next hot framework anytime soon.

How long have you been building stuff with Meteor? You said you’re working on a project your boss needs - are you guys using Meteor heavily in production? It certainly seems production-ready to me, but I haven’t tried it.


#13

tl;dr, may be you can try to wrap the function as an IIFE under the Meteor.startup, something like

Meteor.startup(function(){
   URLHasProtocol=(function(){
    function urlHasProtocol(){
     you code here...
    }
    return urlHasProtocol;
   })();
})

then you should be able to use the URLHasProtocol object(I hope it helps)


#14

I’m pretty much a newbie like you, started about 2 months ago, working on a prototype which will hopefully replace a asp/asp.net/mvc website that’s been in production for the last 15 years. The thing’s a nightmare, no one knows enough about it to improve it and no one’s been stupid enough to rewrite it. Enter me.


#15

Oooh, that sucks. I hate re-writing legacy code. Especially when the original author and/or the last person to touch it has left the company. Of course, if he/she were still there, they’d probably be the ones who got stuck updating it.

Hey, I’m glad you’re here. I was just about to post a question. It’s not too complex, but it’s off topic. Can I message ya?