templates vs DOM creation - highly dynamic interface - javascript

Building a browsergame I came from PHP to JavaScript, which I now also want to use at the server side.
As I'm going to require Users to have JavaScript either way, I'm going to take extensive use of it. I want to use in in a object-oriented way though.
Considering MVC, Models will be used on both client and server side. Views are only used at the client side.
The interface is split into multiple parts: a main sidemenu, main content and some widgets. I'll take the part I've already done as example:
The menu is split into three categories with multiple entries. Each entry is a link with an attached action (like switching the content).
// menuview:
var self = new View();
var generalMenu = new MenuCategory('generalmenu')
.addEntry(new MenuEntry('overview', new Action()))
.addEntry(new MenuEntry('buildings'))
.addEntry(new MenuEntry('resources'))
// [..more categories..]
self.toData = function() {
return {
id: this.id,
cat: [generalMenu.toData(), infosMenu.toData(), userMenu.toData()]
};
};
At the moment View is a compositum with a toData() method to create data for the template parser(selfmade, simple but supporting iteration). And the actions get attached after creation. I use jQuery as framework:
self.show = function(callback) {
$tpl(this.tpl).parse(this.toData()).lang('main').toHTML(function(html) {
var el = $(html);
el.find('a').click(function (e) {
MenuEntry.actionHandler.execAction(e.target.id);
return false;
});
el.appendTo('#'+self.target);
callback && callback();
});
return this;
};
I have declared an actionhandler to avoid iterating over the links.
I'm not feeling well with this solution, it's not flexible enough. I'd like to treat a view like a real compositum, not with a lot of strange dependencies. Also, I have to reparse the whole View if I change a part. Well, in this example this is not obvious, because the menu wont change while runningtime, but other parts of the interface will.
Now, to finally get to my question: Is there a better solution?
Like having dom references spread over the view, each menuentry having it's own reference and directly attached action? If I'm not using templates anymore, what kind of flexiblity am I losing?

I decided to go without template parser. Each view stores it's node and is able to manipulate it directly if it gets informed to update the data.

Related

HTML5 Data attribute with complex JSON object and javascript

I came across a strange requirement (set by myself)...
I'm creating an easy to integrate ajax content loader plugin with lots of options and callbacks. Since the loader is a class and the developer can have multiple instances on a single page, I wanted to get rid of all the ugly code required for every single initialization and decided to use data attributes instead - they look awesome and proficient!
The question is: How to add functions and javascript in general inside a data attribute?
Example:
var url = "someurl/goes/here/";
var Template = new TemplateEngine('Name', {
onCreate: function(template, parts) {
// do something with template parts
template.ID += 1;
},
onRender: function(template, parts) {
template.addClass('flash');
}
});
var settings = {
container: DOM_ELEMENT|STRING,
template: Template,
disableDefaultRender: true,
// a bunch of hooks and callbacks like this:
onBeforeRequest: function(loader, data) {
new_data = data;
// modify request data somehow
loader.requestData = new_data;
},
onRender: function(loader, data) {
loader.renderData(data, function(part) {
// define specific rendering logic for different template parts
// in required
});
},
onAfterRequest: function(loader, data) {
},
onError: function(loader, data) {
}
// etc, etc
};
var THE_LOADER = new SuperFancyAjaxLoader(url, settings);
My original idea is to somehow put all of the above inside the said data attribute:
<div data-fancy-stuff="{all-or-most-of-the-above}">more stuff</div>
and make the script itself find all elements and initialize instances for each of them like so:
var elements = document.querySelector('[data-fancy-stuff]');
for(item in elements) {
try {
var data = elements[item].getAttribute('data-fancy-stuff');
var THE_LOADER = new SuperFancyAjaxLoader(data.url, data.settings);
} catch (ex) {
console.log('Someone messed with prototypes');
}
}
Is the idea of putting javascript functions inside an attribute idiotic? Or is there a way to actually put some js inside an attribute?
I understand that if there's so much javascript required, it's pointless to try and put it inside an attribute, but in real life cases (for this particular task), I will have 3-5 content loaders per page, most of them (or all) will use the same template and rendering logic, but they will all have to modify the request data differently by themselves.
p.s. Eval is Evil.
edit: I'm open to design proposals which do not involve third party MVC frameworks.
May be I don't understand well, but You want provide some JavaScipt modules/classes/objects through HTML5 attribute???
I think it's bad design. It's seems to be mixin of distinct layers.
So technically U have just ONE ability - to call eval, even after your PS because eval is the only point where JavaScript can get other JavaScript from String - ONLY.
But if U want dynamically load some complex javascript as reaction to data in some elements it's very good idea to learn and apply most ultimate thing for such scenarios - well-old-knownn require.js http://requirejs.org/. And if you want hardly bind DOM with some data and behavior you must to learn some of MVC JavaScript solutions - AngularJS, Backbome, Amber and so on.
By design u have to split your application to presentation layer where DOM will live and business layer were JavaScript will live. To bind them to each other you use string/JSON descriptors in DOM attribute and load JavaScript dynamically using on-fly head rewriting or by XHR+eval, such design is asynchronous, quick and is main choise of all solid network-based applications from gmail to all-other-cool-staff. To help build application with such model - require.js is best and most known helper.

Is saving elements as object properties good practice?

I'm writing a small JavaScript framework for fun and possible implementation similar to backbone(hence the tag). I've started saving elements as object properties, as shown below. I'm not sure if I've seen this done, so I was curious if this causes any issues.
Similarly, If the module depends on other modules I list those at the top of the object in the form of....another object.
I wanted a way to list dependencies ( page elements or JavaScript modules ) and detect any issues up front. This has similar ( not same ) benefits as dependency injection.
This is a specific question on this code review post which explains a bit further on how the framework works.
/*MUserTry
**
**
**
*/
$A.modelAjax({
Name: 'MUserTry',
S: {
DynSma: SDynSma,
DynTwe: SDynTwe,
DynArc: SDynArc,
AniFlipPage: SAniFlipPage,
ClientStorage: SClientStorage
},
E: {
but: $A('#ut_but')[0]
},
J: {
box: $('#ut_box')
},
init: function () {
var pipe = {},
this_hold = this;
this.J.box.draggable();
this.E.but.addEventListener("click", function () {
pipe = $A.definePipe(this_hold.Name);
$A.ajaxMachine(pipe);
}, false);
},
pre: function (pipe) {
pipe.page.email = this.e_button.getAttribute('data-email');
pipe.proceed = true;
},
post: function (pipe) {
this.S.ClientStorage.setAll(pipe.server.smalls);
this.S.DynSma.run(pipe.server.smalls);
this.S.DynArc.run(pipe.server.arcmarks);
this.S.DynTwe.run(pipe.server.tweets);
this.S.AniFlipPage.run('ma');
},
finish: function (pipe) {
$A.log(pipe);
}
});
Ok first off let me offer the obligatory "you'll never get a better wheel by re-inventing the wheel" warning. Whatever you're trying to accomplish, you're almost certainly going to be more successful with it if you use an existing library. And even if there is good cause for you to make your own, it would still benefit you immensely to at least look at existing libraries.
But ... maybe you're just having fun with this project, and looking at other projects isn't fun so you're not doing it. Fair enough.
In any case, if you do look at Backbone, you'll find that this practice is core to the Backbone View class. Every View in Backbone has an "el" and "$el" property, which refer to the raw DOM element for the view and the jQuery-wrapped version of that element.
Backbone has no real performance issues with this because variables/properties in JS are just pointers; in other words, when you set the property of an object to an element, you aren't duplicating that element, you're just adding a reference to it (to put it another way, it's more like you're an A tag rather than a whole new document).
The one time Backbone does have a problem though (and your framework will too) is with stale references. In other words, if you just remove element X from the page, the browser will stop using memory for it (eventually, via garbage collection). But if there is an object out there which points to that element, it won't get garbage-collected, because anything with a reference isn't "garbage".
So, the main thing you have to watch out for is making sure that these objects either:
A) get deleted whenever their elements do, or
B) get rid of their references (eg. delete obj.reference) when their elements get deleted
If you don't do that, things will still probably work just fine ... until you use it on a page with lots of elements being created/deleted, at which point Firefox will start popping up "this script took way too long to run, are you really sure you want to be doing this?" messages.

Handling ASP.NET MVC Routing in External JavaScript

What's the best way to avoid hardcoding URL's in JavaScript (primarily used when making AJAX calls)?
In the past:
Render JavaScript variable with result of #Url.Action or #Url.RouteUrl
Pass result of #Url.Action or #Url.RouteUrl to JavaScript in init/ctor.
Is there a better way?
It would be good to do something like this:
var url = $.routes("actionName", "controllerName") // or "routeName" for named routes
$.post(url, { id = 1 }, function() { //.. });
Which of course isn't really possible (JavaScript doesn't have direct access the to the ViewContext and thus doesn't have access to the route tables).
But i'm wondering if there's a way i can kind of setup my own "route table" for JavaScript, with only the ones i know it would need? (e.g i set it up in the View)
How do people handle this?
in-spite of injecting javascript in views i rather prefer - let HTML do its job and javascript do its. Below is the pattern.
For Links
/*A cssclass=ajaxlink is added to all those links which we want to ajaxify*/
//html in view
<a class='ajaxlink' href='#Url.Action("Action","Controller")'>I am An Ajax Link</a>
//generated clean html
<a class='ajaxlink' href='/controller/action'>I am An Ajax Link</a>
//Js
jQuery('.ajaxlink').live('click',function(e){
e.preventDefault(); /*Prevent default behavior of links*/
var url= $(e.target).attr('href');
/*
Now u have url, do post or get:
then append received data in some DOM element.
*/
});
//Controller
public ActionResult()
{
if(Request.IsAjax())
{
/*Return partial content*/
return View();
}
else
{
return View("SomeOther_View.cshtml");
/*
At this point you may reject this request or return full view
whatever you feel is okie.
*/
}
}
This way both type of users can be handled javascript enabled and javascript disabled.
Same can be done for forms.
Implementing a Javascript routing engine wouldn't be too difficult. First, serialize the Routes from C# to Javascript. Second, recreate the Url.Action method.
However, that's a bit overkill for any of the projects I've worked on. My team's projects have always rendered a common Javascript variable that holds all necessary URL's.
This approach ensures strongly-typed action methods and lends better to refactoring too.
This is easier said than achieved in practice, but your website should be fully functional with JavaScript turned off. When this is achieved, you should be able to add AJAX support to your website and re-use existing HREF attributes in your anchor tags or action attributes in your FORM tags. The website will be easier to maintain as you won't need to update links in your JavaScript files.
I've decided to implement my own UrlFactory, using ASP.NET helpers directly (Html/Url) in my code, now I don't have the src with me, I'll post'em tomorrow.
Pros on this: I can track each and every url easily and perform some rewriting in a centralized fashion.
Example of usage:
#{
string myAjaxUrl = UrlFactory.GetUrl (ActionName, ControllerName, new { query-params });
}
Then using 'em in javascript with
var jsUrl = '#myAjaxUrl';
Once you've defined your own Factory, you can hijack "important" urls (eg. for rewriting), and leave common to the Url helper implementation.
However for having this fully client side, there's an extra step of rendering a Js routing context, for accessing client side variables.
EDIT: As promised my very simple Url class builder:
public static class UrlFactory
{
public static string GetUrl(string Action, string Controller, object RouteValues)
{
UrlHelper Url = new UrlHelper(HttpContext.Current.Request.RequestContext);
return Url.Action(Action, Controller, RouteValues);
}
// Common URLS for Denied et similars.
public static string GetDeniedUrl(PEDUtenti Utente, object RouteValues)
{
return GetUrl(Utente, "Denied", "Errors", RouteValues);
}
public static string GetDeniedUrl(object RouteValues)
{
return GetUrl("Denied", "Errors", RouteValues);
}
}

What options are there for building html from an ajax response?

What options or libraries are out there for building html from a returned ajax response?
Currently I am taking the json data I receive, building the html as a string, and using a jQuery DOM insertion function, but I have to think there is a better and more maintainable way out there to do what I am trying to do.
For example, I have a page with a list of projects, with just a thumbnail and title, which users can click on. Once a project is clicked and the json request comes back successfully, more detailed project information is shown. Currently I am doing something that is essentially:
build_project = function(data){
var projectHTML = "<div id='projectData_"+data.id+"'>"+data.contents+"</div>";
return projectHTML;
}
Then inserting it into the DOM where it needs to go. The problem arises when there is more than just one element, sometimes I'll have to create up to 6-10 different nested elements with stuff like the below cropping up:
projectHTML += "</div>";
projectHTML += "</div>";
projectHTML += "</div>";
I'm aware of mustache.js, but I'm not sure if that is exactly what I'm looking for or not - and whether there are any other options that I should investigate.
Thanks!
You are on the right track. Take a look at the jquery.tmpl library.
http://api.jquery.com/jquery.tmpl/
You can try http://knockoutjs.com/ alongside with JQuery templates.
"Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model. Any time you have sections of UI that update dynamically (e.g., changing depending on the user’s actions or when an external data source changes), KO can help you implement it more simply and maintainably."
You can make a printf style method in JavaScript.
You can live dangerously and extend the String object (as luck would have it I wrote this a couple of days ago)...
String.prototype.printf = function() {
var theseArguments = Array.prototype.slice.call(arguments).reverse(),
argumentsLength = theseArguments.length,
str;
str = this.replace(/%s/g, function() {
return theseArguments[--argumentsLength];
});
return str;
};
jsFiddle.
...or simply make it a normal function, and perhaps faux namespace it as util.printf = function() { ... }.
Of course, the function above isn't really printf() (it handles string interpolation only), but you could extend it to do integers, floats, padding, etc.
If you are using ASP.Net and if you do not need the JSON result on the client (other than to do DOM-insertions) you could render the html on the server.
You could then take advantage of you programmning language functionality to build up the html.
Finally you return the HTML-string and append it to you DOM.
As an example you can create your DOM-elements like this:
var panel = new Panel();
var label = new Label { Text = data.contents };
panel.Controls.Add(label);
return panel.ToHtml(); //use stringwriter
This teqnique have proven very useful in several projects i have been involved in.
another option which maybe more maintainable, testable etc would be for your remote function itself (that your calling via ajax) to return the formatted html instead of the data. For example in MVC, e.g ASP.NET MVC, you can return a view and the script would take this data as is without having to manipulate it.
[AcceptVerbs((HttpVerbs.Get))]
public ActionResult GetView(string param1)
{
var model = new ModelXXX;
...
...
return View("ViewName", model);
}

javascript mvc and ajax form submitting

I just started to investigate mvc on javascript client side (JavaScript MVC). Everything looked great until I got to form submitting :) View part won't do it, that's simple. Event is attached in Controller, so Controller is good place to validate form data, but I'm not sure I want my Controller to know specific server address (were to post my form), so would be great to have a method in Model, but then I don't want my Model to know about my Form (which is actually html structure...).
Well, what do I miss about MVC conception? I am also not sure I want to serialize my form in Controller and then pass it as parameter to my Model. For now, the only option I see to make Model independent is to have JavaScript structure (entity), which will be filled by controller (based on form data) and will be passed to the Model method to be saved on server. Very smplified code:
Info = {
name,
address,
// 15 more properties
...
}
InfoController = {
...
onFormSubmit: function() {
...
info.name = document.getElementById("info-name").value;
info.adress = document.getElementById("info-address").value;
...
InfoModel.save( info );
}
}
InfoModel = {
...
save: function( info ) {
// here some code to setialize info object
// send it to server
...
}
}
But it makes my code too complicated (comparing to simple form serizlization by some side frameworks and just sending it..). What's the right choice?
Just answering my own question. Short answer - yes, I was right with my assumptions ;)
I took a look at JavaScriptMVC, and noticed one simple thing I missed, a simple function can be developed which will create javascript object based on form (they have function called formParams which performs this type of converting). This way my controller is simplified:
InfoController = {
...
onFormSubmit: function() {
...
var info = $infoForm.formParams();
InfoModel.save( info );
}
}
Now it does not look that complicated, and its advantage is that there is one place (model) which knows how to save data (validation; url to send; some other stuff like add this entity to client side 'storage'; firing an event that something new is going to be created; whatever else according to our needs), and if I have one more place, or control flow to perform this operation again I won't write this code again, and it does not depend on presentation (is it form, or just set of inputs, wizard etc.). Also Model becomes quite reusable.
Actually before using this approach we had something similar, but it was not that structured (among different presentations for my application which can run javascript).

Categories