Does DOJO offers an alternative to "extension points" for custom widgets? - javascript

I am designing a custom widget using DIJIT and DOJO 1.10.
Basically my custom widget needs to have some behavior like a button, so when user click on it something can happen. I need to make sure other developers can add custom code when onClick it is fired on that widget.
After reading this guide I understood that my custom widget should implement extension points. I have notice in the source code in DIJIT for Button.js and I see they using a special mixin called dijit._OnDijitClickMixin.
Below code for my widget, so far it works fine, but I would like to know:
Is extension point the right way? Does a better alternative exists?
Reading at the documentation I see the following code.
_onButtonClick: function( /*Event*/ e){
... // Trust me, _onClick calls this._onClick
},
_onClick: function( /*Event*/ e){
...
return this.onClick(e);
},
onClick: { // nothing here: the extension point!
}
My custom widget does not implement any of these functions and seems working fine.
Shall I include these functions? What is the reason for that?
Widget
define([
'dojo/_base/declare',
'dijit/_WidgetBase',
'dijit/_OnDijitClickMixin',
'dijit/_TemplatedMixin',
'dojo/text!./templates/template.html'
], function (
declare,
_WidgetBase,
_OnDijitClickMixin,
_TemplatedMixin,
template
) {
return declare([_WidgetBase, _TemplatedMixin, _OnDijitClickMixin], {
templateString: template
});
});
HTML template
<div data-dojo-attach-event="ondijitclick:onClick"> </div>
Initialize the widget
this._iconPage = new IconPages({
id: 'iconPage',
onClick: function () {
//do smt
}.bind(this)
}).placeAt('content');

What you have is fine as the onClick method is meant to be overwritten to hook into events. What you can also do is hook into your IconPages "click" event using dojo/on by doing something like this:
on(this._iconPage, "click", /*function here*/);

Related

execute js function after tinymce is loaded

I have a select that onchange trigger a js funtion to show/hide tinymce editor:
<select id="mySelect" onChange="myFuntion()">
<option value="1">Yes</option>
<option value="0">No</option>
Then I have a textarea with tinymce (loaded on page load).
<textarea class="mce" id="myTextarea"></textarea>
<script src="tinymce.js></script> // file with global tinymce.init({ ... });
The js function is like:
<script>
function myFuntion(){
if( $( '#mySelect' ).val() == '1' ) { tinymce.get( 'myTextarea' ).show(); }
else { tinymce.get( 'myTextarea' ).hide(); }
}
$( document ).ready(function() { myFuntion(); }); // show/hide tinymce based on how the mySelect setting is on page load
Everything works great except for the "$( document ).ready(function(){ myFuntion(); });" that throw an error "Uncaught TypeError: Cannot read property 'show' of null", I think its because tinymce is not yet loaded.
There is a way to change the "document ready function" with "when tinymce is loaded > execute myFunction()"
PS: I use tinymce 4, the tinymce.init() is on a external files and used on other pages, so i prefer not to edit this file
EDIT:
my actual workaround is to use:
setTimeout( function(){ myFunction(); }, 1500 );
but if there is a callback or similiar, for example $(document).on('tinymce:init') will be great
In tinymce 4 try to do with Promise
tinymce.init({
//some settings
}).then(function(editors) {
//what to do after editors init
});
To add to the answer that Kim Gysen wrote ... you can always use JavaScript to modify/extend your standard init on a page by page basis.
For example, start with your standard configuration:
baseConfig = {
selector: 'textarea'
....
}
...since this is just a simple JavaScript object you can inject additional properties/methods into that object before you use it to initialize TinyMCE.
For example:
customConfig = {
setup: function (editor) {
editor.on('init', function () {
//Do what you need to do once TinyMCE is initialized
});
}
}
Then you can "inject" customConfig into baseConfig. The easiest way is to use jQuery's extend method:
$.extend(baseConfig, customConfig);
...this will take all the methods and properties from customConfig and add them to baseConfig. Once you are done you can then load TinyMCE using the newly updated baseConfig:
tinymce.init(baseConfig);
This general technique allows you to create a "standard" configuration with all the base capabilities you need while injecting additional configuration options on a page by page basis.
So now you can trigger the "onInit" capability in TinyMCE when you need it without modifying the core configuration used elsewhere.
According to the docs, you have to declare the callback as follows:
function myCustomOnInit() {
alert("We are ready to rumble!!");
}
tinyMCE.init({
...
oninit : myCustomOnInit
});
This is the handle they offer to assure that tinyMCE is ready.
What you're saying here:
I use tinymce 4, the tinymce.init() is on a external files and used on
other page, so i prefer not to edit this file
Doesn't make a lot of sense to me.
According to the docs:
oninit: This option enables you to specify a function to be executed
when all editor instances have finished their initialization. This is
much like the onload event of an HTML page.
Makes it abundantly clear that this is the way you should go.
It doesn't matter where tinymce.init() is declared, just make sure that you provide the function you wish to execute is added.

understanding Plugin destroy function

hey guys i am very new to js and jquery in genenral and i was just going throught the plugin code of a gallery plugin , i can across the function called _loadevents , that had the following content , see below :
this.$navPrev.on('click.gallery', function (event) {
});
this.$navNext.on('click.gallery', function (event) {
});
this.$wrapper.on('webkitTransitionEnd.gallery transitionend.gallery OTransitionEnd.gallery', function (event) {
});
now $navPrev , $navNext , and $wrapper are obviously some HTML element , now my question is about another method i came across in the same plugin , look below :
destroy: function () {
// console.log('inside destroy');
this.$navPrev.off('.gallery');
this.$navNext.off('.gallery');
this.$wrapper.off('.gallery');
}
now i see that if this function is called all the event handlers will be taken off. now , can somebody tell me what is the necessacity of such a function , does it improve a plugins efficiency ? how or when does such a function get used and is it a common practice to write e destroy function for events in plugins ?
Thank you.
Alex-z .
Destroy functions in plugins enable a developer to reset or remove a plugin from an element, restoring the element to before the plugin was initialised. This is useful if, for example, you have a gallery plugin that works and looks fantastic on desktop, but you don't want it on mobile. You can listen to resize event on window and if the window size is smaller than e.g. 710px then destroy the plugin. This will remove all the added events, undo any DOM manipulation, and restore the html elements back to how they were before the plugin was first initialised (turn-wise, if the window size is larger than 710px then initialise the plugin).
They are generally considered good practice.

Load jQuery after ExtJS

Disclaimer: ExtJS - intermediate level;
jQuery - beginner
I've looked for a solution for this but had not found anything yet.
I have both ExtJS and jQuery in one file and I want to be able to execute the jQuery right after the ExtJS is done building a page (is this even possible).
Example:
function renderPage(){
// ExtJS goes here
}
How do I call jQuery after renderPage() is completed?
I tried the following:
$(document).ready(function() {
$("tr").css("background-color", "yellow");
});
$(document).ready(function(renderPage) {
$("tr").css("background-color", "yellow");
});
function renderPage(){
// ExtJS goes here
// ...
$("tr").css("background-color", "yellow");
}
but it doesn't work. I assume it is because the ExtJS is not done rendering the page when jQuery is getting called and it is not finding anything to select.
Additional information:
I know my jQuery works and is connected to the library because I was able to select HTML elements that are rendered before any script ExtJS or jQuery script is run and modify their style.
Thanks!
Ext components fire an afterrender event that you can hook into, but you have to be careful here because it can fire more than once if you render more than once (which is easy to do accidentally). This is an example of a simple app that fires afterrender once for a panel.
Ext.application({
name : 'Fiddle',
launch : function() {
var panel = Ext.create('Ext.Panel',{
renderTo:Ext.getBody(),
title:'myPanel',
items: [
Ext.create('Ext.Button', {
text: 'Click me!!!!',
handler: function() {
alert('You clicked the button!');
}
}),
{
xtype:'toolbar',
items: [{text:'Button 1'}, {text:'Button 2'}],
listeners: {
afterrender: function () {
Ext.Msg.alert('Fiddle', 'done with ext rendering, do jQuery stuff');
}
}
}
]
});
}
});
sencha fiddle: https://fiddle.sencha.com/#fiddle/eqg
I would recommend not mixing these two libraries without a really good reason - Ext has virtually the same DOM helper functionality as jQuery - if you are just changing a class something like Ext.dom.Element.addCls might be more appropriate (http://docs-origin.sencha.com/extjs/4.2.2/#!/api/Ext.dom.Element)

.datepicker call inside of Durandal activate function not working

I'm Composing this in a view, then trying to call .datepicker() on the result, but nothing happens.
The compose container
<div>
<!--ko compose: { model:'viewmodels/schedule', view: 'views/schedule.html', activate:true} -->
<!--/ko-->
</div>
schedule.html
<div class="schedule-editor">
</div>
And the schedule module
define([], function () {
var vm = {
activate: activate,
};
return vm;
function activate() {
$('.schedule-editor').datepicker();
console.log("activated schedule module");
return true;
}
});
Console logs "activated schedule module", but the datepicker is not created.
If I go to the chrome console and run the jQuery call,
$('.schedule-editor').datepicker(); it brings up the datepicker just fine.
The Durandal docs claim that the activate function is called after the DOM is full composed, so I don't know what else to try.
Like nemesv mentioned you should use viewAttached instead.
define([], function () {
var vm = {
viewAttached: viewAttached,
};
return vm;
function viewAttached(view) {
$(view).find('.schedule-editor').datepicker();
console.log("activated schedule module");
return true;
}
});
Activate happens in the lifecycle before your model has been data-bound to the new view and before the view has been added to the dom. viewAttached happens after the view has been data-bound to your model and attached to the dom.
EDIT
Durandal 2.0 has renamed viewAttached to attached
There is another approach to this that stays true to the declarative UI philosophy that knockout.js and durandal are striving for.
It will allow you to declare the datepicker within the HTML like this:
<div class="schedule-editor" data-bind="
jqueryui: {
widget: 'datepicker',
options: {
// you can set options here as per the jquery ui datepicker docs
}
}">
</div>
Simply include the jquery ui widget bindings found in this gist: https://github.com/SteveSanderson/knockout/wiki/Bindings---jqueryui-widgets
Make sure you include the above javascript after you have loaded jquery, jquery ui and knockout.

How to add events to javascript runtime created html widgets

I'm developing a web community in CakePHP and started to have doubts about using jQuery to provide useful widgets around the views when needed.
For example, I've wrote a jQuery plugin which searches the data inside specific input text, asks to my database and get the results handled with events.
Basically the plugin is perfect for simple applications but it's basically useless for a community where I use it almost in all the views and every time handling it with different events and methods, making it with huge event declarations and very annoying to be debugged.
I thougt to solve the problem by using default events from the widget and add the possibility set additional events specific for the view, but how can i do that?
this is the situation i thought
The Green area of the image is where I'm not sure, where I should put the default events to be retrived every time i need them? After known that then, in the view, I could add some event to the widget to be more easy to use.
For widget I intend every kind of html portion is loaded via javascript and is interactive, maybe an input search which retrieves a list of results or something like that.
My question is how can I set default events in runtime to the widget without copy and paste every time?
And my second question is, how can I add to them specific events for the view only?
Some tutorial somewhere online would be also nice.
My answer requires backbone.js so I am not sure if this will help you.
You could separate your widget into a wrapper and the real widget.
The wrapper could handle events like your close event:
var WidgetWrapper = Backbone.View.extend({
tagName: 'div',
// This should be a mustache template:
template: '<a class="close" href="#">close</a><div class="content"></div>',
events: {
'.close click': 'close',
'.open click' : 'open'
},
close: {
this.$el.hide();
},
open: {
alert('I am open');
}
render: {
this.$el.html(Mustache.to_html(view.template, view.model.toJSON());
}
});
The real widget could render itself inside the wrapper widget and both views could interact with the data model (this.model).
var SpecialWidget = Backbone.View.extend({
tagName: 'div',
// This should also be a mustache template:
template: '<input> open',
events: {
'input change': 'edit'
},
render: function() {
if(!this.wrapper) {
this.wrapper = new WidgetWrapper();
}
// Hand over the model to the wrapper
this.wrapper = this.model;
// Render the wrapper
this.wrapper.render();
// Insert the widget content inside the wrapper
this.$el.empty().append(this.wrapper.$el);
this.$(".content").html(Mustache.to_html(view.template, view.model.toJSON());
},
edit: function() {
alert("Changed");
},
});
This would allow you to separate your events.
You could also do it the other way round and use a wrapper with a sub view.

Categories

Resources