From bc9705275ff0891da22668da709c69958d851dd0 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Mon, 29 Apr 2013 01:15:08 +0200 Subject: [PATCH 1/4] allow override of default template location and app name in server.js --- README.md | 16 +++++++++++++++- server.js | 31 ++++++++++++++++--------------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index af5a884..b67b487 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ #Angularjs in Meteor + ##How to use it -The angularjs app is always called meteorapp. +The angularjs app is by default called meteorapp. angular.module('meteorapp', []). config(['$routeProvider', function($routeProvider) { @@ -8,12 +9,24 @@ The angularjs app is always called meteorapp. when('/index', {templateUrl: 'partials/index.html', controller: MeteorCtrl}). otherwise({redirectTo: '/'}); }]); + +You can change this in server.js + + var myAppName = 'meteorapp'; + + ###Directory structure +Default structure: + /public /partials angular.html(Main screen should contain body content) +You can change partial location in server.js + +var angularTemplatePath = "public/angular.html"; + ###Usage app.controller('MeteorCtrl', ['$scope','$meteor',function($scope,$meteor){ $scope.todos = $meteor("todos").find({}); @@ -28,6 +41,7 @@ The angularjs app is always called meteorapp. + ###Deploying Make sure that you always write angularjs code that can be minified, else use the --debug function. To deploy with Heroku use this buildpack. Thanks to @mimah https://github.com/mimah/heroku-buildpack-meteorite diff --git a/server.js b/server.js index 76eaaba..7394a18 100644 --- a/server.js +++ b/server.js @@ -3,6 +3,9 @@ var fs = Npm.require("fs"); var path = Npm.require("path"); var Fiber = Npm.require("fibers"); +var myAppName = 'meteorapp'; +var angularTemplatePath = "public/angular.html"; + __meteor_bootstrap__.app .use(connect.query()) .use(function (req, res, next) { @@ -18,29 +21,27 @@ __meteor_bootstrap__.app try{ angular = fs.readFileSync(path.resolve('bundle/static/angular.html')); }catch(e){ - if(fs.existsSync("public/angular.html")){ - angular = fs.readFileSync(path.resolve('public/angular.html')); + if(fs.existsSync(angularTemplatePath)){ + angular = fs.readFileSync(path.resolve(angularTemplatePath)); }else{ - console.log("Angularjs\n______\nCreate public/angular.html\n This is used as your main page, this should contain the contents of the body."); + console.log("Angularjs\n______\nCreate " + angularTemplatePath + "\n This is used as your main page, this should contain the contents of the body."); } } code = new String(code); - // console.log((new String(angular)).join()); code = code.replace("",new String(angular)); - code = code.replace("",''); - if (typeof __meteor_runtime_config__ !== 'undefined') { - code = code.replace( - "// ##RUNTIME_CONFIG##", - "__meteor_runtime_config__ = " + - JSON.stringify(__meteor_runtime_config__) + ";"); - } + code = code.replace("",''); + if (typeof __meteor_runtime_config__ !== 'undefined') { + code = code.replace( + "// ##RUNTIME_CONFIG##", + "__meteor_runtime_config__ = " + + JSON.stringify(__meteor_runtime_config__) + ";"); + } res.writeHead(200, {'Content-Type': 'text/html'}); - res.write(code); - res.end(); - return; - //next(); + res.write(code); + res.end(); + return; }).run(); }); From 0c7bb34116228e0cb91cc22e197f06657781c5f9 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Mon, 29 Apr 2013 02:11:37 +0200 Subject: [PATCH 2/4] major improvement of server.js customization and configuration - needs testing --- README.md | 63 ++++++++++++++++++++++++++++++++++++--- client.js | 4 +++ server.js | 89 +++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 123 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index b67b487..8a11398 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,24 @@ #Angularjs in Meteor +## Configuration + +The angular app generation can be configured in the server.js file via the 'angularApp' config object. + + var angularApp = { + name: 'meteorapp', + withAppCtrl: true, + template: { + placeholderElement: "", + name: 'angular.html', + defaultPath: 'bundle/static/' + this.name, + publicPath: 'public/' + this.name + + paths: [this.defaultPath, this.publicPath] + } + } + ##How to use it -The angularjs app is by default called meteorapp. +The angularjs app is by default called 'meteorapp'. angular.module('meteorapp', []). config(['$routeProvider', function($routeProvider) { @@ -12,7 +29,7 @@ The angularjs app is by default called meteorapp. You can change this in server.js - var myAppName = 'meteorapp'; + angularApp.name = 'my-app'; ###Directory structure @@ -23,9 +40,10 @@ Default structure: /partials angular.html(Main screen should contain body content) -You can change partial location in server.js +You can change angular template location in 'server.js', f.ex: -var angularTemplatePath = "public/angular.html"; + angularApp.template.name = "base.html"; + angularApp.template.defaultPath = "public/angular/" + angular.template.name; ###Usage app.controller('MeteorCtrl', ['$scope','$meteor',function($scope,$meteor){ @@ -42,6 +60,43 @@ var angularTemplatePath = "public/angular.html"; +### Current user +See https://github.com/lvbreda/Meteor_angularjs/issues/18 + +You can use something like: + + app.directive('currentUser', function($timeout) { + return function(scope, element) { + var timeoutId; // timeoutId, so that we can cancel the user updates + // used to update the UI + function updateUser() { + element.text((Meteor.user() != null)? Meteor.user()._id : "anonymous"); + } + // schedule update in one tenth of a second + function updateLater() { + // save the timeoutId for canceling + timeoutId = $timeout(function() { + updateUser(); // update DOM + updateLater(); // schedule another update + }, 100); + } + // listen on DOM destroy (removal) event, and cancel the next UI update + // to prevent updating user after the DOM element was removed. + element.bind('$destroy', function() { + $timeout.cancel(timeoutId); + }); + updateLater(); // kick off the UI update process. + } + }); + +The 'currentUser' directive is by default a HTML element attribute. + + + +Then in a controller: + + $scope.currentUser = $meteor('users').find({_id:Meteor.userId()}) + ###Deploying Make sure that you always write angularjs code that can be minified, else use the --debug function. To deploy with Heroku use this buildpack. Thanks to @mimah https://github.com/mimah/heroku-buildpack-meteorite diff --git a/client.js b/client.js index 8c5acb7..fa4b899 100644 --- a/client.js +++ b/client.js @@ -3,6 +3,10 @@ function() { var self = this; self.collections = {}; self.getCollection = function(name) { + if(name == 'users'){ + return Meteor.users; + } + if (self.collections[name]) { return self.collections[name]; } else { diff --git a/server.js b/server.js index 7394a18..a30253e 100644 --- a/server.js +++ b/server.js @@ -3,8 +3,64 @@ var fs = Npm.require("fs"); var path = Npm.require("path"); var Fiber = Npm.require("fibers"); -var myAppName = 'meteorapp'; -var angularTemplatePath = "public/angular.html"; +// TODO: These vars should be defined as Angular constants? + +var angularApp = { + name: 'meteorapp', + appController: 'AppCtrl', + template: { + placeholderElement: "", + name: 'angular.html', + defaultPath: 'bundle/static/' + this.name, + publicPath: 'public/' + this.name + + paths: [this.defaultPath, this.publicPath] + notice: "This is used as your main page, this should contain the contents of the body.", + } +} + +var appConfig = { + readFile: function(filePath) { + new String(fs.readFileSync(path.resolve(filePath))); + }, + getAppHtml: function() { + try { + return this.readFile('bundle/app.html'); + } catch(e) { + return this.readFile('.meteor/local/build/app.html'); + } + }, + + getAngularTemplate: function() { + var templatePaths = angularApp.template.paths; + + // iterate all templatePaths and try each + for (var templatePath in templatePaths) + // only attempt read if file exists + if (fs.existsSync(templatePath) + return this.readFile(templatePath); + } + console.log("Angularjs\n______\nCreate any of: " + templatePaths.join(', ') + "\n " + angularApp.template.notice); + }, + replaceCode: function() { + code = appConfig.getAppHtml(); + code = code.replace(angularApp.template.placeholderElement, appConfig.getAngularTemplate()); + code = code.replace("",new String(angular)); - code = code.replace("",''); - if (typeof __meteor_runtime_config__ !== 'undefined') { - code = code.replace( - "// ##RUNTIME_CONFIG##", - "__meteor_runtime_config__ = " + - JSON.stringify(__meteor_runtime_config__) + ";"); - } - + var code = appConfig.replaceCode(); + res.writeHead(200, {'Content-Type': 'text/html'}); res.write(code); res.end(); From 879a8d2fe4f4881971763f92f5e59be910860c73 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Mon, 29 Apr 2013 03:15:05 +0200 Subject: [PATCH 3/4] better config options --- README.md | 37 +++++++++++++++++++++---------------- server.js | 48 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 8a11398..040b899 100644 --- a/README.md +++ b/README.md @@ -6,19 +6,26 @@ The angular app generation can be configured in the server.js file via the 'angu var angularApp = { name: 'meteorapp', - withAppCtrl: true, + appController: 'AppCtrl', template: { - placeholderElement: "", + placeholderElement: '', name: 'angular.html', - defaultPath: 'bundle/static/' + this.name, - publicPath: 'public/' + this.name - - paths: [this.defaultPath, this.publicPath] - } + locations: ['bundle/static/', 'public/'], } +You can also configure using a config file 'angularAppConfig.json' with your overrides: + + { + name: 'my-meteorapp', + appController: 'AppController', + template: { + placeholderElement: '
', + name: 'main-view.html', + locations: ['bundle/static/', 'public/'], + } + ##How to use it -The angularjs app is by default called 'meteorapp'. +The angularjs app is by default called 'meteorapp' (see Configuration above). angular.module('meteorapp', []). config(['$routeProvider', function($routeProvider) { @@ -27,10 +34,6 @@ The angularjs app is by default called 'meteorapp'. otherwise({redirectTo: '/'}); }]); -You can change this in server.js - - angularApp.name = 'my-app'; - ###Directory structure @@ -40,12 +43,12 @@ Default structure: /partials angular.html(Main screen should contain body content) -You can change angular template location in 'server.js', f.ex: - - angularApp.template.name = "base.html"; - angularApp.template.defaultPath = "public/angular/" + angular.template.name; +Note: To change the defaults, see Configuration above ###Usage + +You can simply inject the $meteor module provided anywhere you want to use it, typically in your meteor-enabled controllers. Then execute the provided Meteor commands (see client.js) + app.controller('MeteorCtrl', ['$scope','$meteor',function($scope,$meteor){ $scope.todos = $meteor("todos").find({}); $meteor("todos").insert({ @@ -61,6 +64,8 @@ You can change angular template location in 'server.js', f.ex:
### Current user +In order to add Meteor current user functionality... + See https://github.com/lvbreda/Meteor_angularjs/issues/18 You can use something like: diff --git a/server.js b/server.js index a30253e..bd91bb4 100644 --- a/server.js +++ b/server.js @@ -3,22 +3,28 @@ var fs = Npm.require("fs"); var path = Npm.require("path"); var Fiber = Npm.require("fibers"); -// TODO: These vars should be defined as Angular constants? -var angularApp = { +// default config +var angularAppConfig = { name: 'meteorapp', appController: 'AppCtrl', template: { placeholderElement: "", name: 'angular.html', - defaultPath: 'bundle/static/' + this.name, - publicPath: 'public/' + this.name - - paths: [this.defaultPath, this.publicPath] + locations: ['bundle/static/', 'public/'], notice: "This is used as your main page, this should contain the contents of the body.", } } +angularAppConfig.template.resolvedPaths = function() { + var self = this; + var resolved = []; + for (var path in this.paths) { + paths.push(path + self.name); + } + return resolved; +} + var appConfig = { readFile: function(filePath) { new String(fs.readFileSync(path.resolve(filePath))); @@ -43,12 +49,38 @@ var appConfig = { console.log("Angularjs\n______\nCreate any of: " + templatePaths.join(', ') + "\n " + angularApp.template.notice); }, replaceCode: function() { + var element = function(tag) { + return "<" + tag + " "; + } + var ngAttr = function(name, value) { + return " ng-" + name + "='" + value + "' "; + } + + var angularApp = angularAppConfig; + + if (fs.existsSync('angularAppConfig.json')) { + try { + angularApp = JSON.parse(this.readFile('angularAppConfig.json')); + } catch (e) { + console.log("Angularjs\n______\nCreate a file: 'angularAppConfig.json' to override the default settings" + } + } + + angularApp.template.resolvedPaths = angularAppConfig.template.resolvedPaths; + code = appConfig.getAppHtml(); code = code.replace(angularApp.template.placeholderElement, appConfig.getAngularTemplate()); - code = code.replace(" Date: Mon, 29 Apr 2013 09:47:20 +0200 Subject: [PATCH 4/4] better readme and few fixes to server.js --- README.md | 77 +++++++++++++++++++++++++++++++++++++++++++++---------- server.js | 27 ++++++++++++------- 2 files changed, 81 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 040b899..76a6c87 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -#Angularjs in Meteor +# Angularjs in Meteor ## Configuration -The angular app generation can be configured in the server.js file via the 'angularApp' config object. +The angular HTML page will be served by meteor. The page generation can be configured in the `server.js` file. The `angularAppConfig` object is used by default: - var angularApp = { + var angularAppConfig = { name: 'meteorapp', appController: 'AppCtrl', template: { @@ -13,7 +13,19 @@ The angular app generation can be configured in the server.js file via the 'angu locations: ['bundle/static/', 'public/'], } -You can also configure using a config file 'angularAppConfig.json' with your overrides: +* `name` - name of the Angular app (used for `ng-app` attribute and name of app module, default: `'meteorapp'`) +* appController - name used to define the main controller on the body element (default: `'AppCtrl'`) +* template - config options for how to load and insert the Angular template + +### Angular template config options + +* `placeholderElement` - the tag element to substitute with the template (default: `''`') +* `name` - the name of the template file (default: `'angular.html'`) +* `locations` - relative folder locations to search for the Angular template. + +Note that `locations` are set up to first try in the expected "deploy" location, then the development location. + +If you define a config file `angularAppConfig.json`, it will be used for angular app configuration. Here an example of a custom config file: { name: 'my-meteorapp', @@ -24,8 +36,9 @@ You can also configure using a config file 'angularAppConfig.json' with your ove locations: ['bundle/static/', 'public/'], } -##How to use it -The angularjs app is by default called 'meteorapp' (see Configuration above). +## Configuring the Angular app + +Define the main angular module, named to match the config Angular app name config setting for the meteor server (default: `'meteorapp'`): angular.module('meteorapp', []). config(['$routeProvider', function($routeProvider) { @@ -34,8 +47,21 @@ The angularjs app is by default called 'meteorapp' (see Configuration above). otherwise({redirectTo: '/'}); }]); +For a complete example, see [meteor-angular-leaderboard](https://github.com/bevanhunt/meteor-angular-leaderboard) + + # coffeescript example -###Directory structure + angular.module('meteorapp', []) + .config ['$routeProvider', '$locationProvider', ($routeProvider, $locationProvider) -> + $locationProvider.html5Mode(true) + $routeProvider.when '/' + controller: 'home' +] + + +### Directory structure + +Create the angular template file with a name and location matching your server config settings. Default structure: @@ -43,9 +69,29 @@ Default structure: /partials angular.html(Main screen should contain body content) -Note: To change the defaults, see Configuration above +Custom structure: -###Usage + /public + /partials + /angular + main-view.html(Main screen should contain body content) + +The router should load the index file when you navigate to root `'/'` of your app. The index file will then be served by meteor (see server.js) + +Example 'index.html' file + + /client + index.html + +Example index HTML file + + + Leaderboard + + +Meteor server will magically create the `` and `` element. + +###Usage of $meteor You can simply inject the $meteor module provided anywhere you want to use it, typically in your meteor-enabled controllers. Then execute the provided Meteor commands (see client.js) @@ -64,9 +110,10 @@ You can simply inject the $meteor module provided anywhere you want to use it, t ### Current user -In order to add Meteor current user functionality... -See https://github.com/lvbreda/Meteor_angularjs/issues/18 +In order to add Meteor current user functionality: + +See [current user issue #18](https://github.com/lvbreda/Meteor_angularjs/issues/18) You can use something like: @@ -94,7 +141,7 @@ You can use something like: } }); -The 'currentUser' directive is by default a HTML element attribute. +The `currentUser` directive is by default a HTML element attribute. @@ -102,6 +149,8 @@ Then in a controller: $scope.currentUser = $meteor('users').find({_id:Meteor.userId()}) -###Deploying -Make sure that you always write angularjs code that can be minified, else use the --debug function. To deploy with Heroku use this buildpack. Thanks to @mimah +### Deploying + +Make sure that you always write angularjs code that can be minified, else use the `--debug` function. To deploy with Heroku use this buildpack. Thanks to @mimah + https://github.com/mimah/heroku-buildpack-meteorite diff --git a/server.js b/server.js index bd91bb4..3191cb2 100644 --- a/server.js +++ b/server.js @@ -38,7 +38,7 @@ var appConfig = { }, getAngularTemplate: function() { - var templatePaths = angularApp.template.paths; + var templatePaths = angularApp.template.locations; // iterate all templatePaths and try each for (var templatePath in templatePaths) @@ -56,26 +56,35 @@ var appConfig = { return " ng-" + name + "='" + value + "' "; } - var angularApp = angularAppConfig; - - if (fs.existsSync('angularAppConfig.json')) { - try { - angularApp = JSON.parse(this.readFile('angularAppConfig.json')); - } catch (e) { - console.log("Angularjs\n______\nCreate a file: 'angularAppConfig.json' to override the default settings" - } + var loadAngularAppConfig = function() { + if (fs.existsSync('angularAppConfig.json')) { + try { + angularApp = JSON.parse(this.readFile('angularAppConfig.json')); + } catch (e) { + console.log("Angularjs\n______\nCreate a file: 'angularAppConfig.json' to override the default settings" + return null; + } + } } + var angularApp = loadAngularAppConfig || angularAppConfig; + angularApp.template.resolvedPaths = angularAppConfig.template.resolvedPaths; + // code = appConfig.getAppHtml(); + + // insert Angular template code = code.replace(angularApp.template.placeholderElement, appConfig.getAngularTemplate()); var htmlElem = element('html'); var htmlReplacement = htmlElem + ngAttr('app', angularApp.name); + // insert ng-app on html element code = code.replace(htmlElem, htmlReplacement); + // TODO: Allow insert on html element? + // insert ng-controller="AppCtrl" on angular placeholder element if (angularApp.appController) { var plh = element(angularApp.template.placeholderElement); var plhReplacement = plh + ngAttr('controller', angularApp.appController);