Skip to main content

Components

UI Components can be used to bind specific dom parts of your view to a Javascript Widgets defined in your module. This can be achieved by extending the action.Component or the more powerful ui.Widget class.

Simple Components

Components consist of a root node, which can be accessed by this.$ within your component functions as in the following example.

View:
<div id="myComponent" data-action-component="example.MyComponent">
<div class="message"></div>
<button data-action-click="hello">Say Hi!</button>
</div>
Module:
humhub.module('example.MyComponent', function(module, require, $) {
var object = require('util').object;
var Component = require('action').Component;

var MyComponent = function(node, options) {
Component.call(this, node, options);
}

object.inherits(MyComponent, Component);

MyComponent.prototype.hello = function(evt) {
this.$.find('.message').text('Hi!');
}

// Export a single class
module.export = MyComponent;
});

After clicking the button of the previous example the action module will search for the closest surrounding component with existing hello action handler and execute it.

Get a Component Instance

If you need the instance of your component, for example in another module, you can retrieve it by calling Component.instance:

humhub.module('example.two', function(module, require, $) {
var Component = require('action').Component;

var myOtherAction = function() {
var myComponent = Component.instance('#myComponent');
}

[...]
});

Tip: You can also search for a component by using Component.closest or Component.find

Nested Components

Components can be nested, which can be handy for example if you want to implement a list view with a single root componentent and multiple entries, whereas the root component could define some configurations as urls and texts or some helper actions.

View:
<div id="myComponent" data-action-component="example.mylist.List" data-some-setting="1">
<div data-action-component="example.mylist.ListEntry" data-id="1" >...</div>
<div data-action-component="example.mylist.ListEntry" data-id="2" >...</div>
</div>
Module:
humhub.module('example.mylist', function(module, require, $) {
var object = require('util').object;
var Component = require('action').Component;

// our parent component
var List = function(node, options) {
Component.call(this, node, options);
}

object.inherits(List, Component);

ListEntry.prototype.listAction = function(evt) {
/* ... */
}

// child component
var ListEntry = function(node, options) {
Component.call(this, node, options);
}

object.inherits(ListEntry, Component);

List.prototype.someAction = function(evt) {

// we can access the data setting of our parent (if not overwritten by our own root)
if(this.data('some-setting') {
/* ... */
}

// access parent component
this.parent().listAction();
}

module.export({
List: List,
ListEntry: ListEntry
});
});

Widgets

The humhub.modules.ui.widget.Widget class extends the Component class and provides some additional functionality as:

  • Advanced event handling
  • Eager or lazy initialization
  • Widget options

Widget Initialization

A Widgets init function is called once the widget is created. A Widget is created either immediately within the humhub initialization phase in case the widgets root node contains a data-ui-init flag or by lazily creating it when calling a widget action or initializing the Widget by means of calling Widget.instance('#myWidget').

Note: If you load a Widget by an ajax call, make sure to apply the ui.additions on your inserted dom nodes, otherwise the data-ui-init behavriour won't be recognized.

View:
<div id="myWidget" data-ui-widget="example.MyWidget" data-ui-init="1" style="display:none">
<!-- ... -->
</div>
Module:
humhub.module('example.MyWidget', function(module, require, $) {
var object = require('util').object;
var Widget = require('ui.widget').Widget;

var MyWidget = function(node, options) {
Widget.call(this, node, options);
}

// Make sure this is called before your function definitions, otherwise they will be lost!
object.inhertis(MyWidget, Widget);

MyWidget.prototype.init = function() {
this.$.fadeIn('fast');
}

// Export a single class
module.export = MyWidget;
});

Widget Options

Your widgets option can be set by using data-* attributes on your Widgets root node. The Widgets getDefaultOptions() method can be used to define default Widget options.

View:
<div id="myWidget" data-ui-widget="example.MyWidget" data-some-setting="0">
<!-- ... -->
</div>
Module:
humhub.module('example.MyWidget', function(module, require, $) {
var object = require('util').object;
var Widget = require('ui.widget').Widget;

var MyWidget = function(node, options) {
Widget.call(this, node, options);
}

// Make sure this is called before your function definitions, otherwise they will be lost!
object.inherits(MyWidget, Widget);

var MyWidget.prototype.getDefaultOptions = function() {
return {
someSetting: '1'
}
}

MyWidget.prototype.init = function() {
if(this.options.someSetting) {
/* ... */
} else {
/* ... */
}
}

// Export a single class
module.export = MyWidget;
});

Note: Notice the transformation of data-some-setting to the camelcase option name someSetting.

Widget Events

TBD

JsWidget class

In order to implement a Yii widget responsible for rendering your widgets markup, you can implement a PHP class derivated of humhub\widgets\JsWidget as in the following examples.

Here are some of the available attributes of the JSWidget class:

  • id: the widget root id, if not provided a generated id will be used by default
  • jsWidget: defines the Javascript widget namespace
  • init: will add the data-ui-init flag if set to true
  • visible: can be set to false in case the root node should be rendered hidden on startup
  • options: used to overwrite or set the Widgets htmlOptions
  • events: defines widget action events
  • container: defines the root node name when using the default rendering mechanism
  • content: defines the content of the root node when using the default rendering mechanism

Functions:

  • getData(): returns an array of widget settings which will be transformed into data-* attributes.
  • getAttributes(): returns an array of html attributes/values
  • getOptions(): merges the given options with the result of getData() and getAttributes() and is used as root node options in most of the cases
Default widget rendering:

The following example shows a simple JsWidget implementation without overwriting the widgets run method.

class MyWidget extends \humhub\widgets\JsWidget
{
// Javascript widget namespace
public $jsWidget = 'example.MyWidget';

// will add the data-ui-init="1"
public $init = true;

// defines the root container node name
public $container = 'div';

// our widget setting
public $someSetting = '1';

public function getAttributes()
{
return [
'class' => 'myWidget'
];
}


public function getData()
{
return [
'some-setting' : $this->someSetting
];
}
}

Note: in this case the container setting could be omitted, since div is the default container name.

The widget could be used within a view as follows:

<?= 
MyWidget::widget([
'someSetting' => '0', // overwrite the default setting
'content' => 'Some content'
]);
?>

which would render the following output:

<div class="myWidget" data-some-setting="0" data-ui-init="1">Some content</div>
Custom widget rendering:

For more complex JsWidgets, you can overwrite your widgets run method and use the getOptions method to merge the widgets options with the default options provided by getData und getAttributes as follows.

class MyWidget extends \humhub\widgets\JsWidget
{
public $jsWidget = 'example.MyWidget';
public $someSetting = '0';

/* ... */

public function run()
{
$this->render('myWidgetView', ['options' => $this->getOptions()]);
}

public function getAttributes()
{
return [
'class' => 'myWidget'
];
}


public function getData()
{
return [
'some-setting' : $this->someSetting
];
}
}

myWidgetView.php

<?= Html::beginTag('div', $options); ?>
<!-- Complex widget markup -->
<?= Html:: endTag('div');