? Failure

User tests: Successful: Unsuccessful:

avatar Fedik
Fedik
7 Mar 2015

Since version 1.5 Joomla made huge step to split HTML and PHP Logic, but JavaScript still in "younger brother" role, and very hard coded.

Here is a try to split PHP and JavaScript.

You ask: How that possible?
Very simple: Write PHP in PHP file, and JavaScript in JS file :neckbeard:
You ask: Then how to connect them?
Well, using JSON, maybe it not the best solution but also not the worst. ?

So

This pull request introduce Joomla Behavior API for client side.
It:

  • allow write custom Behaviors in JavaScript,
  • provide way to interaction between different Behaviors,
  • provide additional way to override each existing behavior, from client side.

Also introduce JDocument::setScriptOptions() that is important gear in connection betwen PHP Logic and client side.
Each Behavior could be called only if server provide the options for that Behavior, or always.

How it work.

Each Behavior it just a collection of callbacks that called when corresponding event happen.
All behavior could be stored in js files, and splitted by group: behavior-bootstrap.js, behavior-form.js so on.

Register custom behavior:

Joomla.Behavior.add('behaviorname', 'ready update', function(event){
	console.log(event.name, event.target);
}, true);

This will be called only if Server provide options for this behavior $doc->addScriptOptions('behaviorname', array(/*some options*/)), and when happened next events: ready, update

Register custom behavior that will be always called:

Joomla.Behavior.add('behaviorname', 'ready update', function(event){
	console.log(event.name, event.target);
});

You asked: wtf is update event?
update - is a custom event that allow to call one Behavior from another "on fly", also this event must be called if you replace part of DOM, so other Behaviors will know about changes.
There also remove custom event, that could be used to ask Behaviors for clean up in 'container', unbind events and so on.
This events we could rename of course to something more clear, if need ?
And, of course, anyone could use any custom event that need for his/her needs.

Example:

// Call all behaviors when you change part of DOM
Joomla.Behavior.call('update', changedContainer);
// Call specific behavior by name when you change part of DOM
Joomla.Behavior.call('update.behaviorname', changedContainer);
// Call specific behavior by name when you change part of DOM, and with custom options
Joomla.Behavior.call('update.behaviorname', changedContainer, options);

// Request other Behaviors to "clean up in" container
Joomla.Behavior.call('remove', container);

How it work on Live you can look in repeatable field in repeatable.js (see diff)

How to override existing Behavior.
Very simple, just register own behavior with same name, and it will override any existing:

Joomla.Behavior.add('behaviorname', 'ready update', function(event){
	console.log('I am override');
});

or you could override only specific event callback if need:

Joomla.Behavior.add('behaviorname', 'update', function(event){
	console.log('I am override for Update event');
});

B/C
I tried make it for keep b/c, with count that it could be in the core since ~~3.~~5.0
So you should not see any difference after apply this patch.

Conclusion
Split PHP logic and JavaScript is a small step to big improve ?
Next step could be dependency management using Requirejs or anything else.

Well to much text, I hope here understandable at least couple words :neckbeard:

ping @phproberto @dgt41 @okonomiyaki3000 @anibalsanchez I need your brains ?

ps. couple word about jquery ?

avatar Fedik Fedik - open - 7 Mar 2015
avatar joomla-cms-bot joomla-cms-bot - change - 7 Mar 2015
Labels Added: ?
avatar Fedik Fedik - change - 7 Mar 2015
The description was changed
avatar dgt41
dgt41 - comment - 7 Mar 2015

Now I can see how bootstrap 3 can be implemented without breaking B/C (at least for the js part)!
Excellent proposal, but I will need some time to get my head around :)
Also can we try to reopen #1227 for require.js? @anibalsanchez?

avatar Fedik
Fedik - comment - 7 Mar 2015

about require.js, I would like step by step, not all at once in this case :wink:

avatar zero-24 zero-24 - change - 7 Mar 2015
Category Components JavaScript Libraries
avatar dgt41
dgt41 - comment - 7 Mar 2015

@Fedik This means that #1260 should be closed?

avatar anibalsanchez
anibalsanchez - comment - 7 Mar 2015

This proposal is very similar to the current model, based on addScript/addScriptDeclaration. In both models, there is a Javascript declaration inside Joomla. I wouldn't rewrite everything just to introduce an alternative new semantic to declare and bind the same code in a different way.

As proposed in #1227, in my view, it is better to introduce a dependency management to declare modules. Based on RequireJS or not. For instance (like AngulaJS), Joomla could have Joomla.module: joomla.module(name, [requires], [configFn]);

// Create a new module
var myModule = joomla.module('myModule', []);

// register a new service
myModule.value('appName', 'MyCoolApp');
avatar Fedik
Fedik - comment - 8 Mar 2015

@anibalsanchez that is good argument :smile:

hm

I tried make it in simple way, if user use:

jQuery(function(){
   // some code 1
});
jQuery(function(){
   // some code 2
});

He "just" need to change it to :yum:

Joomla.Behavior.add('com_another.bla', 'ready update', function(event){
 // some code 1
});
Joomla.Behavior.add('com_another.blabla', 'ready update', function(event){
 // some code 2
});

And as advanced use, he could put all these in the one file eg com_another/js/behavior-another.js, and set the Flag for each Behavior optionsRequired = true, so each of these Behavior will be called only when it need.

Another thing I have no idea how to organize interaction between different Behaviors if use pure Requirejs module. :smile:
Example how could solve #1260 (comment) or more real problem (that could be solved more simple in Behavior case see here)

To be honest I have some doubt about requirejs, it is good for personal app, but not very imagine how it could live in CMS where everyone do what they like.
But it can be because lack of knowledge, I do not have big Requirejs experience. :neckbeard:

For future, my thought was to use each Behavior file as Requirejs module, instead of each Behavior as Requirejs module, something like:

require.config({
  baseUrl: 'media',
  paths: {
    'joomla': 'system/js/core',
    'jquery': 'jui/js/jquery',
    'bootstrap': 'jui/js/botstrap',
    'behavior-bootstrap':'jui/js/behavior-bootstrap',
    'behavior-another':'com_another/js/behavior-another'
  }
});
require(['joomla', 'behavior-bootstrap', 'behavior-another']);

// in behavior-bootsrap.js
define('behavior-bootstrap', ['jquery', 'bootstrap'])

// in behavior-another.js
define('behavior-another', ['behavior-bootstrap'])

In this case we still need: or load all available paths; or think about how we could count it automatically.

@dgt41 if current suggestion looks better, then yes

avatar dgt41
dgt41 - comment - 8 Mar 2015

@fedik @anibalsanchez All require.js and #1227 is needed, at least from my perspective, is one function that will automatically pick the correct path, something similar to what JHTML::_('script' 'bla-bla'); does. So we still do some pre-processing for the paths in php and then hand over the paths to require.js…

The argument of @anibalsanchez for introducing yet another similar model is valid, but I also see that an abstraction will work nicely for many transitions e.g. Bootstrap or any other framework. The hard part in this proposal is that the learning curve increases a lot, and that is also something we have to consider.

avatar anibalsanchez
anibalsanchez - comment - 8 Mar 2015

To split PHP and JavaScript: behavior-based events are still a Javascript (JQuery) declaration in PHP to manage files. Dependencies (and conflicts) can only be solved at DOM level, where browsers can do a better job than an abstraction on Joomla side.

In my view, instead of develop a new "loader", the Javascript standard way to manage files is module management (with named dependencies).

Concerning to modules: At this time, Javascript modules are just around the corner with the upcoming ES6 support. http://www.2ality.com/2014/09/es6-modules-final.html

Beyond the specific dependency manager (RequireJS, joomla.module or ES6 imports) , do Joomla have a need to improve well-written encapsulated functions (e.g. JQuery plugins)?

In my experience, working with RequireJS, I have not found any extension working with modules. You can only find a module, if (by chance) a developer included a file with an alternative initialization detecting AMD ( typeof define === "function" && define.amd ).

My personal opinion is that Joomla can add features to support Javascript Apps and modules. It is not clear if widgets or regular ajax routines can take advantage of techniques oriented to 100% Javascript projects.

Working with RequireJS on Joomla: I have been working with RequireJS since #1227, integrating modules with Backbone.js, Underscore.js and AngularJS (I just can't choose which one is better ;-) ). In a regular view, a Javascript App is declared in this way:

    $file = MyLoader::getRelativeFile('js', 'com_todo/todo.min.js');
    $dependencies = array();
    $dependencies['todo'] = array('mycore');
    MyLoader::initApp(MY_VERSION, $file, $dependencies);

The Loader can work in two modes to generate the Javascript loading routine.

Module Loader - Browser mode

        var deps = [], paths = [];
        deps.push({key:'todo', value:{deps: ["mycore"]}});
        deps.push({key:'backbone', value:{deps: ["underscore"]}});
        deps.push({key:'mycore', value:{deps: ["backbone"]}});
        paths.push({key:'todo', value: 'media/com_todo/js/todo.min'});
        paths.push({key:'underscore', value: 'media/lib_todo/js/backbone/underscore.min'});
        paths.push({key:'backbone', value: 'media/lib_todo/js/backbone/backbone.min'});
        paths.push({key:'mycore', value: 'media/lib_todo/js/mycore.min'});
    </script>
</head>
<body> ..
    <script type='text/javascript'>
            var allKeys, config = {
                baseUrl: 'http://.../',
                urlArgs: 'bust=7.7.0',
                shim: {},
                paths: {}
            };

            // Additional deps
            jQuery.each(deps, function(index, dependency) {
                config.shim[dependency.key] = dependency.value;
            });

            // Additional paths
            allKeys = [];
            jQuery.each(paths, function(index, path) {
                allKeys.push(path.key);
                config.paths[path.key] = path.value;
            });

            // Require.js allows us to configure shortcut alias
            require.config(config);

            require(allKeys, function() {

            });
    </script>   
</body>

Module Loader - Server mode

<script src="...media/lib_todo/js/require/require.min.js"></script>
<script src="...media/lib_todo/tmp/d97719a869966d81c4a1234c436b4250.js?7.7.0" ></script>
<script type='text/javascript'>
        require.config({
            baseUrl: "http://..../"
        });
        require(["todo","mycore"], function() {
        });
</script>
avatar anibalsanchez
anibalsanchez - comment - 8 Mar 2015

Additionally, it is simpler to just add a way to declare: JHtml::('foundation.framework'), JHtml::('ink.framework') etc

avatar Fedik
Fedik - comment - 8 Mar 2015

well, theory sounds good, what about practice? :wink:

Then,
how we could replace hardcoded javascript in JHtml (example1, example2 .. there a loot more)

And how, in this context, we could solve example that I worte above: #1260 (comment) and that(solution in suggested pull)

And remember, we talk about CMS (where user use extensions from different developers) and not about personal App (where you do all by yourself).

@dgt41

The hard part in this proposal is that the learning curve increases a lot

hm, I tried make it simple as possible,
maybe I really did something wrong :smile:
or my description very bad :alien:

avatar anibalsanchez
anibalsanchez - comment - 9 Mar 2015

There are two discussions going on:

  1. How to split PHP and JavaScript (e.g. Bootstrap carousel or tooltip)
  2. Javascript coordinadtion / events management (e.g. repeatable or ajaxian load)

How to split PHP and JavaScript

Current API already supports any framework. No need to introduce a new syntaxis binded to events. We already have JHtml and JLayouts to load view-related code in an overridable way.

Javascript coordinadtion / events management

In Joomla, there is no Javascript coordination. All Javascript declarations and files are loaded in the head, in the same order than executed code.

In general, there is no way to know (server-side) if two scripts are going to have a conflict. An API can try to coordinate events under a simplified Javascript model.... a JBehaviour.... It can work for some time, but Javascript evolves independently than this semantic. Thus, it is unkown if it is going to support the next Javascript iteration. We would have to talk with JBehaviour to generate Javascript code in some way... and work around limitations.

A dependency manager does not even try to solve DOM events conflicts or coordinate dependencies by event. It relays on the developer criteria to declare "a before b and c".

Particularly, Joomla.Behavior is not a loading manager. It is a scheduler, with a list of event-associated tasks. The API allows to "add", "update" or "remove" tasks. In a shared space, if update or remove are allowed, all extensions can compete to be the only one to execute. If only "add" is allowed, a serialization can be performed. It is going to be fair, but it may not solve DOM conflicts caused by automatic code generation.

PD: Sorry for the long post ;-) I tried to present these ideas in an orderly way.

avatar Fedik
Fedik - comment - 9 Mar 2015

well, yes, I tried fix a couple problem

about PHP, two thing:

  • you know that JHtml not possible override without tricky hack :wink:
  • I not fan of $doc->addScriptDeclaraion(JLayoutHelper::render('bootstrap.alretjs')), do not ask me why :neckbeard:

about coordination:
I had some problem with this, and I think it should be in CMS,
And from my point of view it is only one possible way to connect different extension between each other, on the client side.

I do not agree with:

It can work for some time

in Drupal It work well for years, of course they have some limitation, example each behavior they save in Object, so there no way to "reorder" existing behaviors and it always called alphabetically :alien: ...
One of my first try was very close to their solution ...

avatar Fedik
Fedik - comment - 9 Mar 2015

about dependency, I did not tried to fix it in current pull, but I thought it could be more simple after

avatar okonomiyaki3000
okonomiyaki3000 - comment - 9 Mar 2015

you know that JHtml not possible override without tricky hack

I have to object to this statement. Overriding jhtml is perhaps not as simple as it could be but its not tricky and it's absolutely not a hack.

avatar Fedik
Fedik - comment - 10 Mar 2015

@okonomiyaki3000 look on JHtml from point of view the template developer, and example, you need change options for JHtmlBootstrap::popover, but you cannot as it already loaded somewhere else :wink:
In this case, create the plugin that will load custom JHtmlBootstrap before everything else is a tricky hack :smile: ... from my point of view

avatar okonomiyaki3000
okonomiyaki3000 - comment - 10 Mar 2015

@Fedik I know what you mean but I disagree especially with the word 'hack'.

I don't think developing a plugin is any trickier than developing a template. A plugin that would override some JHtml functions could certainly be simpler than a full template. Still, it's a little different and someone who has never made one before might feel a little apprehensive about it. I do think Joomla! should have more of a 'drop-in' style approach to JHtml overriding.

But using the word 'hack' here is completely wrong. Overriding JHtml functions can not ever be considered a hack for the simple reason that JHtml::register exists. The class has a public function specifically for the purpose of overriding any of its keys. You can't really get any more un-hack-like than that.

avatar Fedik
Fedik - comment - 10 Mar 2015

well, it just a question of the determination :smiley:

avatar okonomiyaki3000
okonomiyaki3000 - comment - 10 Mar 2015

No. The word "hack" actually means that you need to something that is unorthodox or generally not recommended. A "hack" implies that there's a good chance that your code will not work in certain edge cases or that a change to the core libraries that shouldn't affect bc in any way will break your code.

avatar Fedik
Fedik - comment - 10 Mar 2015

you just confirmed that it is hack, because override the core class is not recommended as it could lead to unexpected b/c issue in future :wink:

avatar okonomiyaki3000
okonomiyaki3000 - comment - 10 Mar 2015

Sorry, I can't tell if you're kidding or not. There is no need to override any class.

avatar Fedik
Fedik - comment - 10 Mar 2015

ok, maybe I just not know something ...

avatar okonomiyaki3000
okonomiyaki3000 - comment - 10 Mar 2015

Jhtml::register(). Look it up. See what it does.

avatar Fedik
Fedik - comment - 10 Mar 2015

hm, you are right, I missed part that you can register function for specific key

avatar anibalsanchez anibalsanchez - reference | - 11 Mar 15
avatar anibalsanchez anibalsanchez - reference | - 11 Mar 15
avatar anibalsanchez
anibalsanchez - comment - 11 Mar 2015

Hi @Fedik, I have been reading the code in detail ... I am not convinced that PHP and JavaScript has to be decoupled in browser with Joomla.Behavior.

Until now, PHP and JavaScript are always declared together. It is auto-documented. You can read the code and understand how it works. Now, with Joomla.Behavior, you have to follow the execution path, via the associated event, to reach the final routine.

Furthermore, to override a behaviour, you have to talk with Joomla.Behavior. On browser side, if anything goes wrong (a buggy behaviour or anything else), you could end with a general broken execution. Similar to a broken Joomla.submitbutton. In this case, it is not so frequent on front-end site, but I guess Joomla.Behavior is headed to solve the multi-framework support (bootstrap3, foundation, etc).

On the server-side, keeping PHP-Javascript tight, e.g. $doc->addScriptDeclaraion(JLayoutHelper::render('bootstrap.alretjs')), it is easier to read, follow, and override. If anything goes wrong on browser Javascript execution, it is a different conflict than the render process that generated the page.

Joomla.Behavior can help to solve the case for event coordination. But, I think it should not try to also solve the need to decouple PHP and JavaScript.

avatar Fedik
Fedik - comment - 11 Mar 2015

About exception and debug.
if exception happen in Behavior that in the file, it will be like: error in behavior-bootstrap.js:30,
if the script in the body, then it will be: error in /current/page.html:126
For me more simple have a deal with first case, because I know that something went wrong in behavior-bootstrap.js and so I do not need to search what PHP code generate the HTML with error (for second case) :wink:

You can read the code and understand how it works

hm, that was my point to move in JS file, example code highlight works better there :wink:
and it is why I not fan of $doc->addScriptDeclaraion(JLayoutHelper::render('bootstrap.alretjs'))

But maybe it more personal preferences.

Also there already was some requests to avoid javascript in the body, but not remember details :smiley:

About override
It happen itself, when someone call Behavior.add() with behavior name that already exists.
Also if it in the one file, then could be overridden whole file, because JHtml::_('script').

Another good thing
Hope you already noticed how cool JDocument::addScriptOptions()?
at least from my point of view :smile:

It allow override existing options, example:

// this added in some core or component layout
$doc->addScriptOptions('bootstrap.popover', array(
  '.popover-selector' => array('position' => 'top')
));

// but you want different position in your template, then you just do:
$doc->addScriptOptions('bootstrap.popover', array(
  '.popover-selector' => array('position' => 'right')
));
// so it will override options for existing selector

that not really possible in case '$doc->addScriptDeclaraion(JLayoutHelper::render('bootstrap.alretjs'))' ...
hm, well, it is possible if inside the layout you will use Joomla.getOptions() or Joomla.optionsStorage

avatar Fedik
Fedik - comment - 11 Mar 2015

some note about core.js, after formating there difficult to see what new:
all new thing I added in the end of the file

avatar Fedik Fedik - change - 26 Nov 2015
Title
Behavior API
Client side, javascript Behavior API
avatar roland-d roland-d - change - 20 May 2016
Status Pending Closed
Closed_Date 0000-00-00 00:00:00 2016-05-20 15:27:38
Closed_By roland-d
avatar roland-d roland-d - close - 20 May 2016
avatar roland-d
roland-d - comment - 20 May 2016

Everybody, a great discussion but we can't seem to reach an agreement as to how to move forward with this. I am going to close this now but feel free to re-open if you plan to continue working on this.

Thank you for your cooperation.


This comment was created with the J!Tracker Application at issues.joomla.org/joomla-cms/6357.

avatar joomla-cms-bot joomla-cms-bot - change - 20 May 2016
Labels Added: ?
avatar dgt41
dgt41 - comment - 4 Aug 2016

@roland-d can you add a re evaluate for 4 tag here? This is totally needed IMHO

avatar roland-d roland-d - change - 4 Aug 2016
Labels Added: ?
avatar brianteeman
brianteeman - comment - 4 Aug 2016

Going to reopen so it will be seen even though it is a reevaluate fod 4

avatar brianteeman brianteeman - change - 4 Aug 2016
Status Closed New
Closed_Date 2016-05-20 15:27:38
Closed_By roland-d
avatar brianteeman brianteeman - change - 4 Aug 2016
Status New Pending
avatar franz-wohlkoenig
franz-wohlkoenig - comment - 7 Apr 2017

@mbabker for [4.0]-Projects?


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/6357.

avatar joomla-cms-bot joomla-cms-bot - change - 7 Apr 2017
Title
[4.0] Client side, javascript Behavior API
Client side, javascript Behavior API
avatar joomla-cms-bot joomla-cms-bot - edited - 7 Apr 2017
avatar franz-wohlkoenig franz-wohlkoenig - change - 7 Apr 2017
Title
Client side, javascript Behavior API
[4.0] Client side, javascript Behavior API
avatar joomla-cms-bot joomla-cms-bot - change - 7 Apr 2017
Title
Client side, javascript Behavior API
[4.0] Client side, javascript Behavior API
avatar joomla-cms-bot joomla-cms-bot - change - 7 Apr 2017
Category Components JavaScript Libraries Administration com_content com_modules JavaScript Templates (admin) Libraries Front End Templates (site) Unit Tests Components
avatar joomla-cms-bot joomla-cms-bot - edited - 7 Apr 2017
avatar dgt41
dgt41 - comment - 7 Apr 2017

@Fedik can you remove the DomReady? (you can safely user the native DOMContentLoaded)

avatar Fedik
Fedik - comment - 6 May 2017

@dgt41 I need some input here, what do you think

I have made possibility that the behavior can be loaded on the page all the time, but called only when the scriptOptions exists for this behavior.

Joomla.Behavior.add('behaviorname', 'ready update', function(event){
	console.log(event.name, event.target);
}, true <===);

I think about change it to:

Joomla.Behavior.add('behaviorname', 'ready update', function(event){
	console.log(event.name, event.target, event.options);
}, 'script-options-key-blablabla' <===);

it allow to put the behaviors in a single file, load it to the page, and they will be executed only if scriptOptions contain script-options-key-blablabla.

Or I just remove such possibility?

avatar dgt41
dgt41 - comment - 6 May 2017

So the ready event corresponds to document.('DOMContentLoaded') ?

What happens if scriptOptions not there? Crash and burn?

avatar Fedik
Fedik - comment - 6 May 2017

So the ready event corresponds to document.('DOMContentLoaded') ?

yes right

What happens if scriptOptions not there? Crash and burn?

blue screen with random bright yellow words on it, obviously ?

well, no
let say you have behavior do-stuff in my-cool-template.js, which loaded with all your scripts, but should be called only if the options there:

Joomla.Behavior.add('do-stuff', 'ready update', function(event){
     console.log(event.name, event.target, event.options);
     event.target.style.background =  event.options.background || '';
}, 'do-stuff-options-key');

for make it run you
OR add the options via PHP (example in template):

$doc->addScriptOptions('do-stuff-options-key', array('background' => 'black'))

so at time of DOMContentLoaded Joomla.Behavior will look at scriptOptions, and if there a do-stuff-options-key, then it will call your behavior.

OR can call it via your another script (or from another extension), with your custom options:

var changedContainer = document.getElementById("main-container")
Joomla.Behavior.call('update.do-stuff', changedContainer, {background:'yellow'});

if there no options at time of DOMContentLoaded (was not added by PHP), then your behavior do-stuff will be ignored.

avatar joomla-cms-bot joomla-cms-bot - change - 13 May 2017
Category Components JavaScript Libraries Administration com_content com_modules Templates (admin) Front End Templates (site) Unit Tests Administration com_content Templates (admin) JavaScript Front End Templates (site) Components
avatar Fedik
Fedik - comment - 14 May 2017

I guess the idea is tooooo complicated ?

"javascript Behavior API" is dead, long live "Joomla! Custom events"! #16016

avatar Fedik Fedik - change - 14 May 2017
Status Pending Closed
Closed_Date 0000-00-00 00:00:00 2017-05-14 16:59:19
Closed_By Fedik
Labels Removed: ?
avatar Fedik Fedik - close - 14 May 2017

Add a Comment

Login with GitHub to post a comment