Structuring my jquery/javascript properly - javascript

These days I find myself putting a lot of my code in the $(document).ready() which does not seem clean to me. For example, if I am creating something that will query my database via ajax and return it and append it to my list i would do something like this:
$(function(){
//Initialize my DOM elements
$MyList = $("#MyList"),
$MyButton = $("#MyButton");
//Add my Click event
$MyButton.click(function(){
$.ajax({
type: 'POST',
url: "/lists/getmylist",
contentType: "application/json",
success: function(results){
//Parse my results and append them using my favorite templating helper
var jsonResults = $.parseJSON(result);
$MyList.mustache("my-template", jsonResults);
}
});
})
});
Now I know this is a small example but it starts to get really big and messy when I have multiple click events, ajax requests etc. It all ends up going in my document ready. I know that I can possibly put all my ajax requests in an external javascript file to help make it cleaner, but is this architecture in general ok? just seems like its really messy. I have seen others use plugin architectures or init functions. I usually have this document ready at the bottom of all my pages and just throw in whatever is necessary to make my page work correctly. Is this a good way to structure my js?

I think the addition of some Model objects and general object oriented programming principals might go a long way here. If you break your your data fetching and storing out into model classes it should help a lot.
Here are some links that should get you started thinking about OO with Javascript.
Writing Object-Oriented JavaScript
Javascript Design Patterns
Javascript: prototypal inheritance
Javascript: prototypal inheritance 2
Another thing that might help out would be to break the Javascript into multiple files. One for global scripts that might be included via a header that attaches to all your pages and a script for each of your pages that requires it.

Perhaps Backbone.js ( or one of the other frameworks ) could be part of the rescue you are looking for.
I found Backbone immensely helpful organising some inherited spaghetti. Your starting point might be to transition your massive document ready into a backbone view (or multiples of)
Organise your scripts by separating out the views, collections, models into individual files then bundle and minify them together into a single file so the browser only needs to make one request instead of many.
ASP.NET MVC4 can do the bundling for you, it also works similarly on MVC3
This is just a example of simple starting point, there are more advanced techniques (eg. AMD, require.js) to reduce the script size per page, but with caching and gzip I find that the single everything script bundle is fine for a lot of cases.
As for your example, here's a possible backbone implementation
Remember to namespace out your code...
var app = app || {};
$(function ($) {
// depending on your server setup you might be able to just override the url
// and get away with what you want. Otherwise you might look into overriding
// the save/fetch or sync methods of the collection
app.MyListCollection = Backbone.Collection.extend({
url: '/lists/getmylist'
});
app.MyListView = Backbone.View.extend({
//bind the view to the existing element in the HTML.
el: '#MyList',
// your mustache template
template:$('#list-template').html(),
// hook up your event handlers to page components
//(within the context of your el)
events: {
'click #MyButton': 'onMyButtonClick'
},
//called on object creation.
initialize: function () {
//you could create a collection here or pass it into the init function
this.collection = new app.MyListCollection();
//when the collection is changes, call the render method
this.listenTo(this.collection, 'reset', this.render);
},
// render is named by convention, but its where you would "redraw" your view and apply your template
render: function () {
this.$el.html(
Mustache.render(
this.template(this.collection.toJSON())));
return this;
},
//your click handler
onMyButtonClick: function(e){
this.collection.fetch();
}
});
});
use your doc ready to spin up whatever backbone functionality you need
and use it bootstrap your javascript with any server side data that you may have.
$(function () {
// create an instance of your view
new app.MyListView();
//example bootstrap using razor
app.title = #Model.Title;
});

Related

the best pattern to keep references to global collections in Backbone

I'm developing an application in Marionette, but the topic refers to raw Backbone as well.
In my app, I've got many collections. Among them, there are 3 important ones, that are used all over the application (e.g. user data, such as names, which is displayed in most of views). The main question is: what is the best way (a good pattern to follow) to keep references to collections in Backbone/Marionette? I came up with few ideas below:
should I attach them as attributes of the Application object? If so, I'd have to pass the reference to Application object to all views, then views to their subviews, subviews to subsubviews, etc. But this seems a lame and ugly design.
instead, I could pass each collection separately, but this seems even worse solution, because I can't predict which (sub)view will ever need a collection. Keeping those nested references in order would be much more difficult than passing the Application object which I can pass once, always.
the is also a possibility to import the Application as a singleton. I'm using requireJS, most of the modules return constructors now (constructors of views, models and collections). Now the app module returns Application constructor, but instead, it could return the Application object. Then if a view requires to display some data from the collections, it could just require the app module and that's all.
finally, basing on the previous point, I thought I could create a plain map of collections and make it a singleton just as before. This is only to disable all views to have access to Application object, which still seems a bad pattern.
Please, suggest what you think is the best (commentson points above are welcome as well). I just need a good design pattern here. As far as I know, Marionette docs doesn't suggest anything here.
I follow the suggestions made in David Sulc's book Backbone.Marionette.js: A Gentle Introduction. (also the next book on goes into how to then sturcutre the same project with require https://leanpub.com/structuring-backbone-with-requirejs-and-marionette)The code examples he uses are available on github so you could look at the final example which to get an idea of what he does if you didn;t want to buy the book but i really recommend it as it really helped me with how to structure my projects.
To start I have setup an Entities module. The structure of the files also follows this I have an Entities folder which has separate entities.
Each Entity file concerns all actions and stucture of that particular entity. I like this approach as I when i want to edit an entities strcuture or method of getting data from the server i only need to go to one place to make this change.
Interactions with entity are handled through marionettes req/res system. In this way you can expose a handler to the rest of your app but they don;t need to be concerned with how that req gets handled as long as it brings back the required response.
Here is an example of one of my entities to show what i mean - My app needs a collection called positions at various stages so this is something that is loaded early in the app then is available through-out it's life-cycle.
//i am using require so i define my app to use
define(["app", ], function(MyApp) {
//All of this will be added to the Entities module so if i want direct access
//i can go Myapp.Entities.whateverEntityIhaveDeclared
MyApp.module("Entities", function(Entities, MyApp, Backbone, Marionette, $, _) {
//model (nothing new here)
Entities.Position = Backbone.Model.extend({
urlRoot: "api/positions",
defaults: {
name: "",
}
});
//collection again nothing new here
Entities.PositionCollection = Backbone.Collection.extend({
url: "api/positions",
model: Entities.Position,
comparator: "name"
});
//an init function to attach a position collection onto Entities module so it can be available throughout my app
var initializePositions = function() {
if (Entities.positions === undefined) {
Entities.positions = new Entities.PositionCollection();
}
};
//
var API = {
//returns a jquery deferred promise so that this request can easily be handled async
loadPositionEntitiesRemote: function() {
//init positions make's sure i have the collectoin avaliable if it
//has not yet been defined
initializePositions();
//setup defer object
var defer = $.Deferred();
//I actually use a custom sever object here when dealing
//with ajax requests
//but because my app always go through this API i can
//easily swap out how the collection is retrieved.
// Here is an example using backbones fetch
Entities.positions.fetch({
success: function() {
defer.resolve();
},
error: function(data) {
defer.reject(data);
}
});
//setup promise to return
var promise = defer.promise();
return promise;
},
//get the positions collection from here i could
//directly access the attributes or add to the collection
refrencePositionEntities: function() {
initializePositions();
return Entities.positions;
},
//get a position from the collection based on id
//
getPositionEntity: function(positionId) {
initializePositions();
return Entities.positions.get(positionId);
}
};
//setup handlers for the app to use
MyApp.reqres.setHandler("position:entities:remote", function() {
return API.loadPositionEntitiesRemote();
});
MyApp.reqres.setHandler("position:entities:refrence", function() {
return API.refrencePositionEntities();
});
MyApp.reqres.setHandler("position:entity", function(id) {
return API.getPositionEntity(id);
});
MyApp.reqres.setHandler("position:entity:new", function(position) {
return new Entities.Position(position);
});
});
return;
});
now to use this in my app here is an example of how it can now be used
someFunction: function(){
//so early in the app i will get the positions
var positionPromise = MyApp.request("position:entities:remote");
$.when(positionPromise).done(function() {
//do what ever as data has been loaded
}).fail(function(data){
//something failed so handle here might through up an error page but up to you
}).always(function(){
//something to always do no matter if fail or sucess
});
}
anotherFunction: function(){
//later in the app in another controller i might to get the collection
// I could also get it through MyApp.Entities.positions but i rather use the
// API set up so if i ever decided i want to add so checks or something in
// when retrieving the collection its super easy
var positionsCollection = MyApp.request("position:entities:refrence");
}
Not sure if i've done a great job explaining this but if you are looking for a ideas on good design in marionette check out the book as it explains this a lot better than i have just done

Attribute-based helper with lambda expressions for data-bind for integrating knockout.js with MVC4/5 + Razor

I just tried KnockoutMvc and while I admire the creator's effort, I find it is a little too heavy-handed to be useful in general. However I like to minimize the amount of JavaScript in my Razor views, especially boilerplate JavaScript (like binding my serverside view models to clientside view models).
Is there any library out there that allows you to use syntax like:
<span data-bind="#Html.DataBindFor(m => m.MyProperty)"></span>
Alternatively, if I were to go about trying to write my own, approximately what (very general) components would I need to make the library useful? I am assuming at a minimum I would need:
A custom set of attributes for defining how each model / property / method would be bound
An extension method for use in Razor views to use reflection to find out the attribute values
An extension method for use in Razor to generate the (minimal?) JavaScript to bind the client view model to the Razor Model and functions to call the server while necessary
Finally, assuming this library doesn't exist, is there a good reason it doesn't exist? IE is there no way to really work this problem in generality such that a helper method is, uh, helpful?
The reason I ask is because the library Lib.Web.Mvc.JqGrid has helped me create interactive tables really quickly with absolutely minimal amounts of JavaScript (just enough to format columns and such) and I wonder why the same does not exist for knockout.js.
The get rid of manually translating the server-side models to the client-side view models, you can employ the ko.mapping plugin which is then used like this:
#model ViewModel
<script src="#Url.Content("~/Scripts/knockout.mapping-latest.js")" type="text/javascript"></script>
...
<script type="text/javascript">
function ViewModel(initData) {
var self = this;
ko.mapping.fromJS(initData, {}, self);
// Add custom view model logic
...
// Logic for persisting the current state of the model back to the server
self.Save = function() {
$.ajax('/model-uri', {
type: 'POST',
contentType: "application/json",
data: ko.toJSON(self),
success: function (response) {
alert('Success!');
},
error: function(xhr, status, error) {
alert('Error: ' + xhr.responseText);
}
});
};
var initialData = #(Html.Raw(JsonConvert.SerializeObject(Model)));
var model = new ViewModel(initialData);
ko.applyBindings(model);
</script>
Note that we're serializing the server-provided view model to a JavaScript object and then making its properties observable using the ko.mapping plugin which saves us duplicating the view model definition on the client side. We also employ the ko.toJSON utility function when sending the updated model back to the server.
The mapping procedure can be customized to ignore certain properties:
var mapping = {
'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}
ko.mapping.fromJS(data, mapping, self);
There are more mapping customizations available, described on the plugin page.

How to manage dependencies in JavaScript?

I have scripts that needs to wait for certain conditions to be met before they run - for example wait for another script to be loaded, or wait for a data object to be created.
How can I manage such dependencies? The only way I can think of is to use setTimeout to loop in short intervals and check the existence of functions or objects. Is there a better way?
And if setTimeout is the only choice, what is a reasonable time interval to poll my page? 50 ms, 100 ms?
[Edit] some of my scripts collect data, either from the page itself or from Web services, sometimes from a combination of multiple sources. The data can be ready anytime, either before or after the page has loaded. Other scripts render the data (for example to build charts).
[update] thanks for the useful answers. I agree that I shouldn't reinvent the wheel, but if I use a library, at least I'd like to understand the logic behind (is it just a fancy timeout?) to try and anticipate the performance impact on my page.
You could have a function call like loaded(xyz); at the end of the scripts that are being loaded. This function would be defined elsewhere and set up to call registered callbacks based on the value of xyz. xyzcan be anything, a simple string to identify the script, or a complex object or function or whatever.
Or just use jQuery.getScript(url [, success(data, textStatus)] ).
For scripts that have dependencies on each other, use a module system like RequireJS.
For loading data remotely, use a callback, e.g.
$.get("/some/data", "json").then(function (data) {
// now i've got my data; no polling needed.
});
Here's an example of these two in combination:
// renderer.js
define(function (require, exports, module) {
exports.render = function (data, element) {
// obviously more sophisticated in the real world.
element.innerText = JSON.stringify(data);
};
});
// main.js
define(function (require, exports, module) {
var renderer = require("./renderer");
$(function () {
var elToRenderInto = document.getElementById("#render-here");
$("#fetch-and-render-button").on("click", function () {
$.get("/some/data", "json").then(function (data) {
renderer.render(data, elToRenderTo);
});
});
});
});
There are many frameworks for this kind of thing.
I'm using Backbone at the moment http://documentcloud.github.com/backbone/
Friends have also recommended knockout.js http://knockoutjs.com/
Both of these use an MVC pattern to update views once data has been loaded by a model
[update] I think at their most basic level these libraries are using callback functions and event listeners to update the various parts of the page.
e.g.
model1.loadData = function(){
$.get('http://example.com/model1', function(response){
this.save(response);
this.emit('change');
});
}
model1.bind('change',view1.update);
model1.bind('change',view2.update);
I've used pxLoader, a JavaScript Preloader, which works pretty well. It uses 100ms polling by default.
I wouldn't bother reinventing the wheel here unless you need something really custom, so give that (or any JavaScript preloader library really) a look.

How to use javascript namespaces correctly in a View / PartialView

i've been playing with MVC for a while now, but since the project i'm on is starting to get wind in its sails more and more people are added to it. Since i'm in charge of hacking around to find out some "best practice", i'm especially wary about the possible misuses of javascript and would like to find out what would be the best way to have our views and partial views play nicely with javascript.
For the moment, we're having code that looks like this (only simplified for example's sake)
<script type="text/javascript">
function DisableInputsForSubmit() {
if ($('#IsDisabled').is(':checked')) {
$('#Parameters :input').attr('disabled', true);
} else {
$('#Parameters :input').removeAttr('disabled');
}
}
</script>
<%=Html.SubmitButton("submit", Html.ResourceText("submit"), New With {.class = "button", .onclick = "DisableInputsForSubmit(); if ($('#EditParameters').validate().form()) {SetContentArea(GetHtmlDisplay('SaveParameters', 'Area', 'Controller'), $('#Parameters').serialize());} return false;"})%><%=Html.ResourceIcon("Save")%>
Here, we're saving a form and posting it to the server, but we disable inputs we don't want to validate if a checkbox is checked.
a bit of context
Please ignore the Html.Resource* bits, it's the resource management
helpers
The SetContentArea method wraps ajax calls, and GetHtmlDisplay
resolves url regarding an area,
controller and action
We've got combres installed that takes care of compressing, minifying
and serving third-parties libraries and what i've clearly identified as reusable javascript
My problem is that if somebody else defines a function DisableInputsForSubmit at another level (let's say the master page, or in another javascript file), problems may arise.
Lots of videos on the web (Resig on the design of jQuery, or Douglas Crockford for his talk at Google about the good parts of javascript) talk about using the namespaces in your libraries/frameworks.
So far so good, but in this case, it looks a bit overkill. What is the recommended way to go? Should i:
Create a whole framework inside a namespace, and reference it globally in the application? Looks like a lot of work for something so tiny as this method
Create a skeleton framework, and use local javascript in my views/partials, eventually promoting parts of the inline javascript to framework status, depending on the usage we have? In this case, how can i cleanly isolate the inline javascript from other views/partials?
Don't worry and rely on UI testing to catch the problem if it ever happens?
As a matter of fact, i think that even the JS code i've written that is in a separate file will benefit from your answers :)
As a matter of safety/best practice, you should always use the module pattern. If you also use event handlers rather than shoving javascript into the onclick attribute, you don't have to worry about naming conflicts and your js is easier to read:
<script type="text/javascript">
(function() {
// your button selector may be different
$("input[type='submit'].button").click(function(ev) {
DisableInputsForSubmit();
if ($('#EditParameters').validate().form()) {
SetContentArea(GetHtmlDisplay('SaveParameters', 'Area','Controller'), $('#Parameters').serialize());
}
ev.preventDefault();
});
function DisableInputsForSubmit() {
if ($('#IsDisabled').is(':checked')) {
$('#Parameters :input').attr('disabled', true);
} else {
$('#Parameters :input').removeAttr('disabled');
}
}
})();
</script>
This is trivially easy to extract into an external file if you decide to.
Edit in response to comment:
To make a function re-usable, I would just use a namespace, yes. Something like this:
(function() {
MyNS = MyNS || {};
MyNS.DisableInputsForSubmit = function() {
//yada yada
}
})();

Model-View-Controller in JavaScript

tl;dr: How does one implement MVC in JavaScript in a clean way?
I'm trying to implement MVC in JavaScript. I have googled and reorganized with my code countless times but have not found a suitable solution. (The code just doesn't "feel right".)
Here's how I'm going about it right now. It's incredibly complicated and is a pain to work with (but still better than the pile of code I had before). It has ugly workarounds that sort of defeat the purpose of MVC.
And behold, the mess, if you're really brave:
// Create a "main model"
var main = Model0();
function Model0() {
// Create an associated view and store its methods in "view"
var view = View0();
// Create a submodel and pass it a function
// that will "subviewify" the submodel's view
var model1 = Model1(function (subview) {
view.subviewify(subview);
});
// Return model methods that can be used by
// the controller (the onchange handlers)
return {
'updateModel1': function (newValue) {
model1.update(newValue);
}
};
}
function Model1(makeSubView) {
var info = '';
// Make an associated view and attach the view
// to the parent view using the passed function
var view = View1();
makeSubView(view.__view); // Dirty dirty
// Return model methods that can be used by
// the parent model (and so the controller)
return {
'update': function (newValue) {
info = newValue;
// Notify the view of the new information
view.events.value(info);
}
};
}
function View0() {
var thing = document.getElementById('theDiv');
var input = document.getElementById('theInput');
// This is the "controller", bear with me
input.onchange = function () {
// Ugly, uses a global to contact the model
main.updateModel1(this.value);
};
return {
'events': {},
// Adds a subview to this view.
'subviewify': function (subview) {
thing.appendChild(subview);
}
};
}
// This is a subview.
function View1() {
var element = document.createElement('div');
return {
'events': {
// When the value changes this is
// called so the view can be updated
'value': function (newValue) {
element.innerHTML = newValue;
}
},
// ..Expose the DOM representation of the subview
// so it can be attached to a parent view
'__view': element
};
}
How does one implement MVC in JavaScript in a cleaner way? How can I improve this system? Or is this the completely wrong way to go, should I follow another pattern?
There are at least a couple of established and usable MVC frameworks for JavaScript JavaScriptMVC and pureMVC. There are probably more. I've used JavaScriptMVC for browser based and Air apps and keep coming back to it - it has its problems but I've found it to be quite useful.
There are other solutions too, have a look at Sammy, a new thing I've heard good things about. I haven't used myself but intend to try soon. I don't know enough about it to describe it properly, but to me it seems like a front controller which works on routes, a templating system and ReSTful data stores. I'm not sure if it is MVC but has similar ingredients.
I have to disagree with mway's answer. MVC may be a bit diferent to implement in JavaScript but its benefits are very important to organising this mess. The design patterns usually associated with OO languages don't go out the window just because js isn't class based.
I would say that MVC is more suitable for JavaScript apps than for request based (server side) applications. Those objects can hang around for a while in a one page JavaScript app - minutes if not hours - and having a well organised way of organising their interaction will make your code much more robust and easy to deal with. There are books on the subject.
A couple of other points regarding the code you posted.
The view objects have responsibility for applying event listeners to DOM elements. This is the controller's job. The view just renders the HTML - the controller listens for the events and acts accordingly.
Your models seem to know your views. The model layer should have minimal knowledge of the view layer (perhaps being registered as observers). Keep your model clean and to the point, I mean the business point - business logic. In js apps you may just be proxying for a sever side model layer but it is important for your sanity to keep your model to the business logic and nothing else. Application logic is the controllers job
To be honest, MVC isn't well-suited for Javascript. It can support the basic fundamentals of the design, sure - you can create pseudoclasses to act as controllers or models, support basic inheritance, and you can have it manipulate or create any number of DOM elements, but there's a price that you pay for that - in overhead, accessibility, and usability.
In my opinion, I consider Javascript more of an augmentation - the KISS mentality exists for a good reason. If you're interested in better ways to organize your code, there's always the option of packaging related functionality into modules (sic) and abstracting out portions as appropriate. For example, creating a factory to do more complex AJAX request management, or a pseudoclass to handle processing of similar types of data. Using a standard base function for controllers, another for models, etc, as prototypes for new instances of those objects can accomplish similar functionality... but again, it's sort of going against the grain of Javascript.
However, if you're stuck on the MVC idea just for the sake of structure, consider something like the following:
;(function(window, $) {
/**
* Event Object
* A quick description goes here.
**/
var Events = window.Events = {
'bindTrackables': function() {
$('a.trackable').live('click', function() {
if(!_gaq)
_gaq = [];
_gaq.push(['_trackPageview', '/ajax/foobar']);
});
},
'bindSomeEvent': function() {
// etc
}
};
/**
* Data Cache
* I'll need to remember stuff later, so I store it here
**/
var Cache = window.Cache = {
'data': {},
'store': function(key, value) {
Cache.data[key] = value;
},
'fetch': function(key) {
return Cache.data[key];
}
};
/**
* Request Object
* Stores native AJAX requests for later use
**/
var Request = window.Request = {
'current_requests': [],
'send': function(url, type, data, callback) {
Request.current_requests.push($.ajax({
'url': url,
'type': type,
'data': data,
'callback': callback
}));
},
}
// add some private logic here
})(window, jQuery);
It's extremely basic, but you get the idea. Modular code is key... in JS, this is more important than forcing your application (or the language) to fit a certain style.

Categories

Resources