Code Snippets: Part 2
29 Jan 2015It’s that time again. I’ve learned a lot more since the last time I posted code-snippets. A lot of these were small issues where the solution was not immediately obvious and took a non-insiginificant amount of time for me to find it. Amongst them, I’ve also included a few references that I think are useful resources.
1. Fix: Site looks zoomed-out on mobile
Due to the high resolution of mobile devices these days, it’s not all that straight-forward to use media-queries to detect the width of the device. Or maybe it is and I’m just not very good at it. In any case, remember to put the following meta
tag into your page’s <head>
section.
<meta name="viewport" content="width=device-width, target-densitydpi=high-dpi" />
2. Reset the database of deployed apps on meteor.com
Pretty simple, the first line deletes the app and the second line uploads it again.
meteor deploy myapp.meteor.com --delete
meteor deploy myapp.meteor.com
3. Ultimate Meteor Reference Guide
This particular blog post covers a good deal of Meteor. Now, I don’t think it is required or even a good thing for an entpreneur to know every single little thing about Meteor (unless you want to sell Meteor books). But it is a good reference nonetheless.
4. Customizing meteor packages for your project
This is actually surprisingly simple. This is the kind of code you’ll need to use:
> cd your/project/path
> mkdir packages && cd packages
> git clone https://github.com/meteor-useraccounts/semantic-ui.git
> cd ..
> meteor add useraccounts:semantic-ui
> meteor
To summarize:
- Create a directory named
/packages
in your project directory. - Use
git clone
to download the package you want to use into the/packages
directory. - Make the changes you want to the package source.
- Install the package as usual with
meteor add
.
Note: It’s probably a good idea to fork it first on github, and clone that into your packages directory. That way, if you ever wanted to contribute your changes back to the original source, you could easily push your commits up to github and do a pull request.
5. Loaded Boilerplate Meteor Project
This boilerplate project is based on Bootstrap (not my preferred framework of choice, I prefer Foundation), but it is very impressive and quite comprehensive. Instead of using Yogiben’s admin, I also prefer the Houston admin.
6. CSS nth-child selectors and responsive design
I had a series of divs and I wanted it to be in rows of three when the screen had the room for it, but rows of two when it didn’t. Finally, if the screen width really was too small, it would end up just being layed out vertically. The way I did this was with the nth-child selector and then selectively clearing either every third or every second div. Note that all these divs in question already had float: left
being applied to them.
.datablock:nth-child(2n+1) {
clear: left;
}
@media only screen and (min-width: 62.5em) {
.datablock:nth-child(3n+1) {
clear: left;
}
.datablock:nth-child(2n+1) {
clear: none;
}
}
7. Spacebars Reference Guide
This link has a lot of good examples showing how to work with Spacebars in Meteor.
8. Using template variables in meteor upon render
When Iron Router loads a template, it can pass in some variables. It’s pretty straightforward how to use these variables in the template or the template helper functions themselves. However, it’s a little different if you want to use those same variables on render.
Instead of using this.varName
, you have to use this.data.varName
. Observe:
Template.cityDataset.helpers({
airports: function() {
console.log("helper-name: " + this.name); // New York City
console.log("helper-country: " + this.country); // United States
return CityDatasets.findOne({name: this.name, country: this.country}).airports;
},
});
Template.cityDataset.rendered = function() {
console.log("name: " + this.name); // undefined
console.log("country: " + this.country); // undefined
console.log("name: " + this.data.name); // New York City
console.log("country: " + this..data.country); // United States
// doSomethingCoolToDOM(this.name, this.country);
}
9. Preventing form submit and substituting your own action
This will prevent the form from submitting:
$("form").submit(function( event ) {
event.preventDefault();
});
This is how you can bind your own event handler to the enter key:
$('#search').bind("enterKey",function(e){
e.preventDefault();
var input = $('#search').val();
// do stuff with the input
});
$('#search').keyup(function(e){
if(e.keyCode == 13)
{
$(this).trigger("enterKey");
}
});
10. Using Meteor’s findOne() in a non-case-sensitive way
There are two options, one is to basically create (in the collection) a fully uppercased or lowercased version of the parameter you want to match. The second (and the option that I chose) is to use regex to match the parameter instead.
return Cities.findOne({
name: {$regex : new RegExp(this.params.name, "i")},
country: {$regex : new RegExp(this.params.country, "i")}
});
11. Taking care of form submits in Meteor
This will require doing things in two files, namely the template helper (or controller in MVC parlance) and then collection javascript file. The reason for this is simple. The template helper simply lets the browser know what to do when the user hits the submit button. Ultimately, this ends up calling a function from the collection javascript file.
The collection javascript file manages what methods can be run on the server. Notice in the example below, it checks whether or not the user’s email is the admin’s email.
Remember, Meteor Methods can only run on the server, so there’s no issue of allow or deny here. The debate between when to use allow/deny or Meteor Methods can be found here.
city_submit.js
Template.citySubmit.events({
'submit form': function(e) {
e.preventDefault(); // prevent default behaviour
// create an object to house the info from the form
var city = {
name: $(e.target).find('[name=name]').val(),
country: $(e.target).find('[name=country]').val()
};
// call the function named 'cityInsert' with our object
Meteor.call('cityInsert', city, function(error, result) {
// display the error to the user and abort
if (error)
return alert(error.reason);
Router.go('cityPage', city); // redirect to the cityPage template
});
}
});
cities.js
Cities = new Mongo.Collection('cities');
Meteor.methods({
cityInsert: function(cityAttributes) {
check(Meteor.userId(), String); // check the userId is a String
check(cityAttributes, { // check the passed in attributes
name: String,
country: String
});
// does it already exist?
var cityWithSameNameCountry = Cities.findOne({
name: cityAttributes.name,
country: cityAttributes.country
});
// if so, throw an error
if (cityWithSameNameCountry) {
throw new Meteor.Error("already-exists",
"That city already exists!")
}
var user = Meteor.user();
// verify admin and then submit
if (user.emails[0].address === "admin@myapp.com") {
var city = _.extend(cityAttributes, {
submitted: new Date() // add the current time into the object
});
Cities.insert(city); // insert the object
return city;
} else {
throw new Meteor.Error("not-admin",
"You have to be the admin.");
}
}
});