How does alfresco's javascript( not webscript) mechanism - javascript

When I play with alfresco share, I found it is difficult to track the UI and javascript. you can only see some class name in the HTML tags, But you are difficult to know how are they constructed, And When, where and how can these scattered HTML code can render such a fancy page.
Can someone help me ? Please offer several example and explain how they work!
Thanks in advance!

Here is some example that will hopefully help you (it's also available on Wiki). Most of the magic happens in JavaScript (although the layout is set in html partly too).
Let's say you want to build a dashlet. You have several files in the layout like this:
Server side components here:
$TOMCAT_HOME/share/WEB-INF/classes/alfresco/site-webscripts/org/alfresco/components/dashlets/...
and client-side scripts are in
$TOMCAT_HOME/share/components/dashlets...
So - in the server side, there is a dashlet.get.desc.xml - file that defines the URL and describes the webscript/dashlet.
There is also a dashlet.get.head.ftl file - this is where you can put a <script src="..."> tags and these will be included in the <head> component of the complete page.
And finally there is a dashlet.get.html.ftl file that has the <script type="text/javascript"> tag which usually initializes your JS, usually like new Alfresco.MyDashlet().setOptions({...});
Now, there's the client side. You have, like I said, a client-side script in /share/components/dashlets/my-dashlet.js (or my-dashlet-min.js). That script usually contains a self-executing anonymous function that defines your Alfresco.MyDashlet object, something like this:
(function()
{
Alfresco.MyDashlet = function(htmlid) {
// usually extending Alfresco.component.Base or something.
// here, you also often declare array of YUI components you'll need,
// like button, datatable etc
Alfresco.MyDashlet.superclass.constructor.call(...);
// and some extra init code, like binding a custom event from another component
YAHOO.Bubbling.on('someEvent', this.someMethod, this);
}
// then in the end, there is the extending of Alfresco.component.Base
// which has basic Alfresco methods, like setOptions(), msg() etc
// and adding new params and scripts to it.
YAHOO.extend(Alfresco.MyDashlet, Alfresco.component.Base,
// extending object holding variables and methods of the new class,
// setting defaults etc
{
options: {
siteId: null,
someotherParam: false
},
// you can override onComponentsLoaded method here, which fires when YUI components you requested are loaded
// you get the htmlid as parameter. this is usefull, because you
// can also use ${args.htmlid} in the *html.ftl file to name the
// html elements, like <input id="${args.htmlid}-my-input"> and
// bind buttons to it,
// like this.myButton =
// so finally your method:
onComponentsLoaded: function MyDaslet_onComponentsLoaded(id) {
// you can, for example, render a YUI button here.
this.myButton = Alfresco.util.createYUIButton(this, "my-input", this.onButtonClick, extraParamsObj, "extra-string");
// find more about button by opening /share/js/alfresco.js and look for createYUIButton()
},
// finally, there is a "onReady" method that is called when your dashlet is fully loaded, here you can bind additional stuff.
onReady: function MyDashlet_onReady(id) {
// do stuff here, like load some Ajax resource:
Alfresco.util.Ajax.request({
url: 'url-to-call',
method: 'get', // can be post, put, delete
successCallback: { // success handler
fn: this.successHandler, // some method that will be called on success
scope: this,
obj: { myCustomParam: true}
},
successMessage: "Success message",
failureCallback: {
fn: this.failureHandler // like retrying
}
});
}
// after this there are your custom methods and stuff
// like the success and failure handlers and methods
// you bound events to with Bubbling library
myMethod: function (params) {
// code here
},
successHandler: function MyDAshlet_successHandler(response) {
// here is the object you got back from the ajax call you called
Alfresco.logger.debug(response);
}
}); // end of YAHOO.extend
}
So now you have it. If you go through the alfresco.js file, you'll find out about stuff you can use, like Alfresco.util.Ajax, createYUIButton, createYUIPanel, createYUIeverythingElse etc. You can also learn a lot by trying to play with, say, my-sites or my-tasks dashlets, they're not that complicated.
And Alfresco will put your html.ftl part in the page body, your .head.ftl part in the page head and the end user loads a page which:
loads the html part
loads the javascript and executes it
javascript then takes over, loading other components and doing stuff
Try to get that, and you'll be able to get the other more complicated stuff. (maybe :))

You should try firebug for stepping through your client side code.
Alfresco includes a bunch of files that are all pulled together on the server side to serve each "page".
I highly recommend Alfresco Developer Guide by Jeff Potts (you can buy it and view it online instantly).
James Raddock
DOOR3 Inc.

Related

What's the correct way to send Javascript code along with rendered HTTP to a client?

Mid development I decided to switch to server-side rendering for a better control amongst other benefits. My web application is completely AJAX based, no url redirecting, so the idea here is a website that builds itself up
I just couldn't figure out the proper way to send javascript events/functions along with the html string, or should all the necessary javascript always be preloaded in the static files?
Let's say client clicks a pre-rendered button 'open table'
The server will make a query, build the html table and send it back, but this table also needs javascript triggers and functions to work properly, how are these sent, received and executed?
There are a couple of articles that mention to not use eval() in Javascript, is there any way around this? I don't want to have to preload unnecessary events for elements that don't yet exist
The server is Python and the Client is Javascript/JQuery
Theoretical example :
Client Base Javascript :
$("body").on("click", "#open_table", function() {
$.getJSON('/get_table', function(response){
$("#table_div").append(response.html);
eval(response.javascript()); //??
}
});
Python Server(views.py) :
def get_table(request):
data = {}
#String containing rendered html
data['html'] = get_render_table()
#String containing Javascript code?
data['javascript'] = TABLE_EVENTS_JAVASCRIPT
return HttpResponse(json.dumps(data),content_type='json')
Worth noting my question comes from an experimental/learning perspective
Update:
You can use jQuery.getScript() to lazy load JS. I think this solution is as close as you can get to run JS without using eval().
See this example:
jQuery.getScript("/path/to/script.js", function(data, textStatus, jqxhr) {
/* Code has been loaded and executed. */
console.log( data ); // Data returned
console.log( textStatus ); // Success
console.log( jqxhr.status ); // 200
console.log( "Load was performed." );
});
and "/path/to/script.js" could be a string returned from $.getJOSN response.
Also, the documentation for getScrippt() has examples on how to handle errors and cache files.
Old Answer:
Using .on() attaches events to current and future DOM elements.
You can either attache events prior to DOM insertion or attache event after DOM insertion.
So in your example you can do something like:
$("body").on("click", "#open_table", function() {
$.getJSON('/get_table', function(response){
var code = $(response.html);
code.find(".elementToFind").on("click", function (){
// Code to be executed on click event
});
$("#table_div").append(code);
}
});
I did not test the code but I think it should work.
Assuming you can't just set up an event-binding function and then call it from the main script (the JavaScript you need can't be guessed ahead of time, for example) then one really easy way is just to append the JavaScript to the bottom of the returned HTML content within script tags. When it's appended along with the HTML, the script should simply execute, with no eval() required.
I can't swear that this would work in old browsers, but it's a trick I've used a couple of times, and I've had no problems with it in Firefox, Chrome, or any of the later IE versions.
I think I see what you're asking here, from my understanding you want to send the new "page" asynchorously, and render the new javascript and html. It looks like you already got your request/response down, so i'm not gonna go and talk about sending JSON objects, and the whole "how-to" of sending html and javascript because it looks like you got that part. To do what you want and to dynamically add your javascript in, this stackoverflow question looks like it has what you need
Is there a way to create a function from a string with javascript?
So pertaining to your example, here is how it would look when you recieve the JSON string from your python script:
$("body").on("click", "#open_table", function() {
$.getJSON('/get_table', function(response){
$("#table_div").append(response.html);
/* Create function from string */
var newFunction = Function(response.javascript['param_1'], response.javascript['param_2'], response.javascript['function']);
/* Execute our new function to test it */
newFunction();
}
});
*Your actual function contents would be the string: response.javascript['function']
*Your parameter names if any would be in separate strings ex: response.javascript['param_1']
That is almost a direct copy of the "String to function" code that you can see in the linked question, just replaced it with your relevant code. This code is also assuming that your object is sent with the response.javascript object containing an array with your actual function content and parameter names. I'm sure you could change the actual name of the var too, or maybe put it in an associative array or something that you can keep track of and rename. All just suggestions, but hopefully this works for you, and helps you with your problem.
I am also doing similar work in my project where I had to load partial html using ajax calls and then this partial HTML has elements which requires events to be attached. So my solution is to create a common method to make ajax calls and keep a js method name to be executed post ajax call in html response itself. For example my server returns below html
<input type="hidden" data-act="onPartialLoad" value="createTableEvents" />
<div>.........rest of html response.....<div>
Now in common method, look for input[type='hidden'][data-act='onPartialLoad'] and for each run the method name provided in value attribute (value="createTableEvents")
Dont Use Eval() method as it is not recommended due to security
issues. Check here.
you can run js method using window["method name"]...so here is a part of code that I use.
$.ajax(options).done(function (data) {
var $target = $("#table_div");
$target.fadeOut(function () {
$target.html(data);
$target.fadeIn(function () {
try {
$('input[data-act="onPartialLoad"]', $target).each(function () {
try {
//you can pass parameters in json format from server to be passed into your js method
var params = $(this).attr('params');
if (params == undefined) {
window[$(this).val()]();
}
else {
window[$(this).val()]($.parseJSON(htmlutil.htmlDecode(params)));
}
} catch (e) {
if (console && console.log) {
console.log(e.stack);
console.log($(this).val());
}
}
});
}
catch (e) {
console.log(e.stack);
}
});
});
});
use jQuery.getScript() (as suggested by Kalimah Apps) to load the required js files first.

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.

extend a javascript option to add functionality

I need to call "MyOtherFunction" when "MyFunction"(which creates an element) completes, without MyFunction knowing what MyOtherFunction is.
The reason I need this is for extension of a jquery powered fileupload User Control that is used in several places with different functionality. A specific page shows a header and file count for it, and when the upload completes, I need to modify the file count according to how many files are displayed(by created elements) I thought :
$(UserControl).on(MyFunction, UploadElem, MyOtherFunction);
but this route is not accomplishing anything. The most I can alter the User Control is add in a function call, but without effecting the original user control functionality.
I'm not sure if because MyFunction isn't an event and doesn't bubble up or if it just isn't possible to use a defined function as a parameter of .on() is the reason I cannot get this code to work. Any suggestions?
Easiest way I can think of, is duck punching respectively hooking that method:
var _oldMyFunction = MyFunction;
MyFunction = function() {
_oldMyFunction.apply( this, arguments );
MyOtherFunction();
};
I managed to solve my own issue, but the context is important for the answer:
// Using a Global JavaScript object I created:
GlobalNameSpace.ExtensionFunction = function(oParam1, oParam2, oParam3)
{
/// <summary>All parameters are optional</summary>
return; // For instances when it is not being overwritten, simply return
}
//In the Code for the user control:
GlobalNameSpace.UploadControl.UploadComplete(oSender, oArgs)
{
///<summary>Handles the Upload process</summary>
// process the upload
GlobalNameSpace.ExtensionFunction(oSender, oArgs);
}
//and finally in the code to extend the functionality
GlobalNameSpace.Page.Init
{
///<summary>Initializes the page</summary>
// redefine the extension function
GlobalNameSpace.ExtensionFunction = function(oSender, oArgs)
{
GlobalNameSpace.Page.Function(oSender, oArgs);
}
}
This allows me to extend anything I need it to without polluting my objects, and having something generic already existing to call on to make my changes. This solution solves my problem of needing a onCreate function for the elements I create to represent my uploaded items to trigger the header displaying the number of files. Very useful

Use of prototype for single instance functions in JavaScript

For performance optimization I'm using a single JavaScript file to handle all the pages of the website I'm working on.
The basic structure I'm using is as followed:
(function($) {
// Shared functions ...
// A function for every page
function Page1() {
}
Page1.prototype = {
init: function() {
//...
},
//more functions
};
// more pages
$(document).ready(function() {
if ($('#page1uniqueidentifier').length) {
var page1 = new Page1();
page1.init();
}
// more pages
}
}) (jQuery);
I'm not an experienced JavaScript programmer so I've been searching a lot about best practices and different ways of structuring my code and I've ended up choosing this one but I'm not really sure about it and I have a few questions:
Is it worth it to use prototype if I'm never gonna have more than a single instance of a page? I think I understand how prototype works and that I'm not gaining any performance there. But I'm using it just as a best practice because in the case different instances would exist, these functions would be the same in every instance.
Is there a better way to structure the code?
Should I put the call of the init function inside the constructor and then only call new Page1()?
function Page1() {
this.init();
}
if ($('#page1uniqueidentifier').length) {
new Page1();
}
For performance optimization I'm using a single JavaScript file to
handle all the pages of the website I'm working on
That makes no sense. You should separate code into files, and then run all your js files thru a minimizer/concatenator to package it up.
Anyway, to answer your questions,
if you are only going to have 1, then prototype won't buy you anything. However, if you are going to use more than 1, would you go back and change it? Plus, using prototype wont hurt you either, so you might as well do it for learning.
You should create the files that make sense according to the functionality implemented. I would separate your object definition into its own file, for example, so when you look at that file, all you see is the code for that object.
If you have a constructor function, you don't really need init, do you?

templates vs DOM creation - highly dynamic interface

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.

Categories