Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create extendable controllers in ExpressJS

I'm new to Node and I'm trying to create an MVC app with ExpressJS (http://expressjs.com/). I'm using the same folder structure as the MVC example (https://github.com/visionmedia/express/tree/master/examples/mvc) on GitHub.

In my controllers folder, I have 2 folders: main and system. What I'd like is to have a base controller defined in /controllers/system/index.js and have /controllers/main/index.js inherit the system controller. Every other module will extend system and override a few functions to generate a page.

In another tutorial I found the following code.

Base.js

var _ = require("underscore");
module.exports = {
    name: "base",
    extend: function(child) {
        return _.extend({}, this, child);
    },
    run: function(req, res, next) {

    }
};

Home.js

var BaseController = require("./Base"),
    View = require("../views/Base"),
    model = new (require("../models/ContentModel"));

module.exports = BaseController.extend({ 
    name: "Home",
    content: null,
    run: function(req, res, next) {
        model.setDB(req.db);
        var self = this;
        this.getContent(function() {
            var v = new View(res, 'home');
            v.render(self.content);
        })
    },
    getContent: function(callback) {
        var self = this;
        this.content = {};
        model.getlist(function(err, records) {
            if(records.length > 0) {
                self.content.bannerTitle = records[0].title;
                self.content.bannerText = records[0].text;
            }
            model.getlist(function(err, records) {
                var blogArticles = '';
                if(records.length > 0) {
                    var to = records.length < 5 ? records.length : 4;
                    for(var i=0; i<to; i++) {
                        var record = records[i];
                        blogArticles += '\
                            <div class="item">\
                                <img src="' + record.picture + '" alt="" />\
                                <a href="/blog/' + record.ID + '">' + record.title + '</a>\
                            </div>\
                        ';
                    }
                }
                self.content.blogArticles = blogArticles;
                callback();
            }, { type: 'blog' });
        }, { type: 'home' });
    }
});

How do you do this without Underscore's extend function? Does Express have a built in method to extend modules? I'm using doT.js for templating so I don't want to include another large library for one function.

Thanks!

Edit: Had to make a few changes to get the Base code from dc5 to work. System works but for main I'm getting this error on the inherits call:

util.js:555
  ctor.prototype = Object.create(superCtor.prototype, {
                          ^
TypeError: Object prototype may only be an Object or null
    at Function.create (native)
    at Object.exports.inherits (util.js:555:27)

/controllers/system/index.js:

var util = require( 'util' ),
    system = { };

system.index = function( req, res, next ) {
    res.render( 'main' );
};

module.exports = system;

/controllers/main/index.js:

var util = require( 'util' ),
    system = require( './../system/index' ),
    main = { };

util.inherits( main, system );

module.exports = main;
like image 693
Jonathan Avatar asked Jan 13 '23 10:01

Jonathan


1 Answers

You can use util.inherits for what you've described. It isn't an replacement for _.extend(), but all that is needed for the example above is straight inheritance.

Usage: util.inherits(constructor, superConstructor)

base.js:

var util = require('util');

function Base() {…}

Base.prototype.run = function(req,res,next) {…}
module.exports = Base;

home.js:

var util = require('util');
var BaseController = require("./base");

function Home() {…}

util.inherits(home, BaseController);

Home.prototype.run = function(req,res,next) {…}

Or a standard JS inheritance pattern:

base.js:

function Base() {…}

Base.prototype.run = function(req,res,next) {…}
module.exports = Base;

home.js:

var BaseController = require("./base");

function Home() {
    BaseController.apply(this, arguments);
}

Home.prototype = Object.create(BaseController.prototype);
Home.prototype.run = function(req,res,next) {…}

From the updated samples in the question, the modules should look like this:

System:

var util = require('util');

function System() {
    this.txt  = "hello from ";
    this.name = "System"; 
}

System.prototype.sayHello = function() {
    console.log(this.txt + this.name);
}

System.prototype.run = function(req,res,next) {
    this.sayHello();

    console.log('calling ==> overrideMe');
    this.overrideMe();

    console.log('calling ==> noOverride');
    this.noOverride();
    next ? next() : "";
}

System.prototype.overrideMe = function() {
    console.log('System.overrideMe');
}

System.prototype.noOverride = function() {
    console.log('System.noOverride');
}


module.exports = System;

Main:

var util = require('util');
var System = require("../system/");

function Main() {
    // Makes sure the System constructor is run
    System.apply(this, arguments);
    this.name = "Main"; 
}

util.inherits(Main, System);

Main.prototype.run = function(req,res,next) {
    this.sayHello();

    console.log('calling ==> overrideMe');
    this.overrideMe();

    console.log('calling ==> noOverride');
    this.noOverride();

    next ? next() : "";
}

Main.prototype.overrideMe = function() {
    console.log('Main.overrideMe');
}

module.exports = Main;

app.js in the root - a simplified express server:

var System  = require('./controllers/system');
var Main    = require('./controllers/main');
var express = require('express');
var path    = require('path');
var http    = require('http');
var app     = express();
var server;

var system = new System();
var main   = new Main();

app.configure(function() {
    "use strict";
    app.set('port', process.env.PORT || 3000, '127.0.0.1');
    app.use(system.run.bind(system));
    app.use(main.run.bind(main));
    app.use(app.router);
    //app.use(express.compress());
    app.use(express.static(path.join(__dirname, '..', 'public'), {redirect: false}));
    app.use(express.static(path.join("/Users/dave/personal/playground/yo"), {redirect: false}));
});


server = http.createServer(app);
server.listen(app.get('port'), function() {
    "use strict";
    console.log("Express server listening on port " + app.get('port'));
});

From the browser access: http://localhost:3000/

Console output:

Express server listening on port 3000
hello from System
calling ==> overrideMe
System.overrideMe
calling ==> noOverride
System.noOverride
hello from Main
calling ==> overrideMe
Main.overrideMe
calling ==> noOverride
System.noOverride

Important

Because this example is using instances of System and Main to provide routes, the run method must be bound to the instance when passing it to express. See: MDN .bind documentation for more information.

like image 155
dc5 Avatar answered Jan 16 '23 21:01

dc5