Just is a fast, small, thoughtfully written javascript MVC Framework. It's has 3 main goals.
- To be everything you need.
- To solve common problems and pitfalls that large and small apps have.
- To be easy to write.
- J
- J.extends`
- "Static" Methods
- The Blueprint
- Getting and Setting properties
- super
- The Mapper
- Data Binding
- Templating Engine
- Views
- Models
- Events
- App
Just has 0 dependecies. IE >= 9 is supported.
- Main
J
Object which contains static methods.
- Create inheritance easily:
J.extends(Superclass, Class def (optional))
Example:
var TinyModel = J.extends(MiniModel, {
bump : function() {
console.log("bumpping")
},
init : function() {
console.log("initting Tiny Model")
}
});
You can read this as TinyModel
will extend or inherit from MiniModel
.
When creating a class definition you can create a constructor with an init
method as in the the previous example.
You can create an instance of the previous class like so:
tm = TinyModel();
tm.dance();
tm.bump();
You can also add instance methods using the prototype method as you normally would.
var TinyModel = J.extends(MiniModel);
TinyModel.prototype.init = function() {
console.log("Being created")
}
tm = TinyModel();
You can create static methods by capitalizing the name of the method you create.
var TinyModel = J.extends(MiniModel, {
bump : function() {
console.log("bumpping")
},
Plus : function(a,b) {
return a + b
},
init : function() {
console.log("initting Tiny Model")
}
});
tm = TinyModel();
console.log(TinyModel.Plus(1,2));
Everything in J
extends from the Blueprint
object.
You can initialize Blueprint
with getters and setters.
var blueprint = J.Blueprint({
});
The Blueprint
object/class gives everything that inherits it the ability to get
and set
properties. Those properties can then be watched for changes.
//inherit from the Blueprint
var SomeModel = J.extends(J.Blueprint);
//create a new Some Model
var sm = SomeModel();
//Listen for changes to properties of some model
sm.watcher = function(key,val) {
console.log("sm change","key:",key, "sm changed to:",val)
}
console.log(sm.get("name")); // undefined
sm.set("name","John") // sm change key: name sm changed to: John
console.log(sm.get("name")); // John
sm.set("name","Frank") // sm change key: name sm changed to: frank
console.log(sm.get("name")); // Frank
You can create read only properties by creating a property starting with an underscore. This will create the properties sans underscore as a getter only.
var Blue = J.extends(J.Blueprint);
var blue = Blue({
name : "John",
age : 25,
_i : 0
});
console.log(
blue.get("name"),
blue.get("age"),
blue.get("i"));
// John 25 0
blue.set("name","Jeff");
blue.set("age",30);
blue.set("i",20);
blue.set("_winner",false);
blue.set("winner",true);
// Object {name: "Jeff", age: 30, i: 0, winner: false}
console.log(
blue.get("name"),
blue.get("age"),
blue.get("i"),
blue.get("winner"));
// Jeff 30 0 false
When you inherit from Blueprint
you will have a _super
method avaiable to you. You can use it to call the parent class method and override your own. Here is a basic implementation.
var container = document.getElementsByClassName("container")[2];
var View = J.extends(J.View, {
render: function(data) {
this._super(J.View,"render",[data]);
}
});
this._super
takes the SuperClass
as the first parameter. The name of the function as a string as the second parameter and the parameters you want to pass to said function as an array. In this example we are passing just one parameter to the super call. Then we can call render
on our instance of our custom View
class
var view = app.addView("list",View({
el: container,
template: "sometemplate.html#item"
}));
view.render({link:"google.com",
name:{
first:"jeff",
middle: {
initial: "B",
full:"Benjamin"
},
last:"Johnston",
quote:"and you know he was a good dog.",
nickname: "Jacky"
}
});
See more on template rendering below.
You can use the Mapper
class to get back a single instance of your model, or a new instance of your model. Make sure to create a single instance of a new Mapper
with something like this:
var mapper = J.Mapper();
Then you add models to it with two methods mapAModel
which will always return the same instance of a model. The mapper will instantiate it for you. You also have mapModel
which will also instantiate a new model everytime you grab the model back. You can retrieve models with getAModel
and getModel
.
var mapper = J.Mapper();
var SomeModel = J.extends(J.Blueprint);
mapper.mapAModel("some",SomeModel);
var oneModel = mapper.getAModel("some"); //give it a name. Mapping "some" model.
oneModel.set("name","john");
console.log(oneModel.get("name")); // returns John
var sameModel = mapper.getAModel("some"); // get that same instance on a new variable.
console.log(sameModel.get("name")) // returns John. We are dealing with the same instance as before.
You can setup to have data binding between different fields. Initialize a view with a el
and setup your template and bindings.
var view = J.View(document.getElementById("content"),{
bind : {
"name" : "keyup #name-input"
}
});
This will bind the name
property to the keyup
of #name-input
.
In your HTML you can setup an element to receive that data like so:
<div data-j-bindable="name"></div>
<input type="text" id="name-input" />
Using data-j-bindable
means that anything that is populating a name property will appear in that div.
You can create dynamic templates in HTML. You can use your own templating engine as well but to use the built in one just create a template file. For example templates/hello.html
<!-- somefile.html -->
*/list\*
<ul></ul>
*\list/*
*/item\*
<li><a href="{link}">
A dog named {name.first} {name.middle.full|capitalize} {name.last|capitalize}
<p>{name.quote|titleize}</p>
<p>AKA: {name.nickname|camelCase}</p>
</a></li>
*\item/*
You can tell Just where to find your templates.
J.registerApp( J.App() );
var app = J.getApp();
app.config = {
root: "http://localhost:3000",
templates: "http://localhost:3000/templates"
};
With this info in hand Just will make a url request for your templates, but only when you render. You tell Just which template on the page you want to grab. */templatename\*
refers to the template name and wrap the end with *\templatename/*
. You can then grab the template like so
var container = document.getElementsByClassName("container")[2];
var View = J.extends(J.View);
var view = app.addView("list",View({
el: container,
template: "somefile.html#item", //#item refers to the template name.
}));
Notice that our view can grab variables. You can wrap the variable with a single curly brace to add a variable in there.
We also added a filter to our variable using the pipe |
. There are a few included filters. You can add your own very easily.
J.addFilter("camelCase",function(str){
return str.replace(/^([A-Z])|\s(\w)/g, function(match, p1, p2, offset) {
if (p2) return p2.toUpperCase();
return p1.toLowerCase();
});
});
You can create a view and link it to a main element. When rendering you can render in context of a child of that view or for the whole view. There is a couple of typical MVC issues with Views
that we have solved. See Mediators
below. When the render
method is called your view can load their own template by making a URL request to your templates directory (which you can configure on your App.config
configuration. Any subsiquent calls to your render
method will use a cached version of your templates.
var view = J.View({
el: document.getElementById("content"),
template : J.html("<div>{{name}}</div>");
});
Blueprint
and Model
are similar but Model
is meant for being the M in MVC. You can set all of it's properties at creation time. You can do this for the model and when creating the model through the Mapper
.
var mapper = J.Mapper();
var mainModel = mapper.mapAModel("mainModel",J.Model,{
"height" : 50
});
mainModel.set("name","Jeffery")
mainModel.set("name2","John")
var otherModel = J.Model({
color : "#9b59b6",
size : {
height : 50,
width : 25
}
});
Models also have a toJSON()
method and toObject()
method.
var otherModel = J.Model({
color : "#9b59b6",
size : {
height : 50,
width : 25
}
});
J.l(otherModel.toObject());
J.l(otherModel.toJSON());
We also have a convenience method you'll notice from the last example J.l
which pipes directly to console.log
. It's just easier to type.
We have cross browser events available and you would (but won't have to see App
below) create 1 event class/object for app wide event triggering and listening.
// New instance of Events class.
var events = J.Events();
// Subscribe
events.add(document.body,'customEvent',function() {
console.log("Event triggered")
});
// Trigger
events.trigger(document.body,'customEvent');
You shouldn't need to create the Event
on your own. It is included with your app object as well as other App
nessesities. Here is a refactored version of the previous example using the App object for event delegation.
var App = J.App();
// Subscribe
App.events.add(document.body,'customEvent',function() {
console.log("Event triggered")
});
// Trigger
App.events.trigger(document.body,'customEvent');
You can set config values on your App
.
J.registerApp( J.App() );
var app = J.getApp();
app.config = {
root: "http://localhost:3000",
templates: "http://localhost:3000/templates"
};
The Mediator (sometimes called the controller) is listening on behalf of the view and communicates with Models. The view in MVC is supposed to only be dealing with display information. If that's true then we don't do any logic for the view on the view. The problem in typical javascript MVC frameworks is that it is hard to tell the difference between the HTML view and the javascript view. Well in Just, we have solved that. You'll typically have one of two situations:
- You have your HTML written already and you just need to listen for events (buttons to be click, and text to be typed).
- You have an empty HTML element you want to populate with some data from the server. And it may change over time.
In the case of #1 you can use the Mediator
. In that case your View is your HTML, since it alone is responsible for displaying data. The Mediator
will listen to changes on your HTML view like it is supposed to do.
In the case of #2 you can use the View
class and attach a Mediator
to your view. You'll use template files to render your html. In that case your HTML is called templates and the responsibility of making things appear on the screen is left to your View
class.
All that to say you can use your Mediator
class in two ways.
-
Where it acts with the view as the HTML. In that case you'll pass an HTML element to your
Mediator
. -
Where you have a view responsible for populating the template. In that case you'll pass a view class to the
Mediator
.
JQuery is not required. However if you happen to be using JQuery (if the JQuery object is available), Just will use it to support browsers. Bottom line: Add JQuery if you want.