asfman
android developer
posts - 90,  comments - 213,  trackbacks - 0

Model-View-Controller (MVC) with JavaScript

The article describes an implementation of Model-View-Controller software design pattern in JavaScript.

I like JavaScript programming, because it is the most flexible language in the world. With the JavaScript language developers can create applications either in procedural or object-oriented style. It supports almost all programming styles and techniques that I know. I've seen procedural, object-oriented, aspect-oriented JavaScript code snippets. A determined developer can even use functional programming techniques in JavaScript applications.

My goal for this article is to write a simple JavaScript component that can show a power of the language. The component is a kind of the HTML ListBox ("select" HTML tag) control with an editable list of items: the user can select item and remove it or add new items into the list. Component will consist of three classes that implement the Model-View-Controller design pattern.

I hope, this article can be just a good reading for you, but it would be much better if you consider running the example and adapting it to you needs. I believe you have everything to create and run JavaScript programs: brains, hands, text editor (notepad), and Internet browser (IE or Firefox).

The Model-View-Controller pattern, which I'm about to use in the code, requires some description here. As you may know, the name of the pattern is based on the names of its main parts: Model, which stores an application data model; View, which renders Model for an appropriate representation; and Controller, which updates Model. Wikipedia defines typical components of the Model-View-Controller architecture as follows:

  • Model: The domain-specific representation of the information on which the application operates. The model is another name for the domain layer. Domain logic adds meaning to raw data (e.g., calculating if today is the user's birthday, or the totals, taxes and shipping charges for shopping cart items).
  • View: Renders the model into a form suitable for interaction, typically a user interface element. MVC is often seen in web applications, where the view is theHTML page and the code which gathers dynamic data for the page.
  • Controller: Processes and responds to events, typically user actions, and invokes changes on the model and perhaps the view.

The data of the component is a list of items, in which one particular item can be selected and deleted. So, the model of the component is very simple - it is stored in an array property and selected item property; and here it is:

/**
 * The Model. Model stores items and notifies
 * observers about changes.
 */
var ListModel = function (items) {
    this._items = items;
    this._selectedIndex = -1;
 
    this.itemAdded = new Event(this);
    this.itemRemoved = new Event(this);
    this.selectedIndexChanged = new Event(this);
};
 
ListModel.prototype = {
 
    getItems : function () {
        return [].concat(this._items);
    },
 
    addItem : function (item) {
        this._items.push(item);
        this.itemAdded.notify({item: item});
    },
 
    removeItemAt : function (index) {
        var item = this._items[index];
        this._items.splice(index, 1);
        this.itemRemoved.notify({item: item});
        if (index == this._selectedIndex)
            this.setSelectedIndex(-1);
    },
 
    getSelectedIndex : function () {
        return this._selectedIndex;
    },
 
    setSelectedIndex : function (index) {
        var previousIndex = this._selectedIndex;
        this._selectedIndex = index;
        this.selectedIndexChanged.notify({previous: previousIndex});
    }
 
}; 

Event is a simple class for implementing the Observer pattern:

var Event = function (sender) {
    this._sender = sender;
    this._listeners = [];
};
 
Event.prototype = {
    attach : function (listener) {
        this._listeners.push(listener);
    },
    notify : function (args) {
        for (var i = 0; i < this._listeners.length; i++) {
            this._listeners[i](this._sender, args);
        }
    }
};

The View class requires defining controls for interacting with. There are numerous alternatives of interface for the task, but I prefer a most simple one. I want my items to be in a Listbox control and two buttons below it: "plus" button for adding items and "minus" for removing selected item. The support for selecting an item is provided by Listbox's native functionality. A View class is tightly bound with a Controller class, which "... handles the input event from the user interface, often via a registered handler or callback" (from wikipedia.org).

Here are the View and Controller classes:

var ListView = function (model, controller, elements) {
    this._model = model;
    this._controller = controller;
    this._elements = elements;
 
    var _this = this;
 
    // attach model listeners
    this._model.itemAdded.attach(function () {
        _this.rebuildList();
    });
    this._model.itemRemoved.attach(function () {
        _this.rebuildList();
    });
 
    // attach listeners to HTML controls
    this._elements.list.change(function (e) {
        _this._controller.updateSelected(e);
    });
 
};
 
 
ListView.prototype = {
 
    show : function () {
        this.rebuildList();
        var e = this._elements;
        var _this = this;
        e.addButton.click(function () { _this._controller.addItem() });
        e.delButton.click(function () { _this._controller.delItem() });
    },
 
    rebuildList : function () {
        var list = this._elements.list;
        list.html('');
        var items = this._model.getItems();
        for (var key in items)
            list.append($('<option>' + items[key] + '</option>'))
        this._model.setSelectedIndex(-1);
    }
 
};
 
var ListController = function (model) {
    this._model = model;
};
 
ListController.prototype = {
 
    addItem : function () {
        var item = prompt('Add item:''');
        if (item)
            this._model.addItem(item);
    },
 
    delItem : function () {
        var index = this._model.getSelectedIndex();
        if (index != -1)
            this._model.removeItemAt(this._model.getSelectedIndex());
    },
 
    updateSelected : function (e) {
        this._model.setSelectedIndex(e.target.selectedIndex);
    }
 
};

And of course, the Model, View, and Controller classes should be instantiated. The sample, which you can below, uses the following code to instantiate and configure the classes:

$(function () {
    var model = new ListModel(['PHP''JavaScript']);
    var view = new ListView(model, new ListController(model),
        {
            'list' : $('#list'),
            'addButton' : $('#plusBtn'),
            'delButton' : $('#minusBtn')
        }
    );
    view.show();
});
<select id="list" size="10" style="width: 15em"></select><br/>
<button id="plusBtn">  +  </button>
<button id="minusBtn">  -  </button>


posted on 2010-04-01 23:01 汪杰 阅读(144) 评论(0)  编辑 收藏 引用
只有注册用户登录后才能发表评论。

<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

常用链接

留言簿(15)

随笔分类(1)

随笔档案(90)

文章分类(727)

文章档案(712)

相册

收藏夹

http://blog.csdn.net/prodigynonsense

友情链接

最新随笔

搜索

  •  

积分与排名

  • 积分 - 468991
  • 排名 - 6

最新随笔

最新评论

阅读排行榜

评论排行榜