How to Connect dojo/aspect to a Widget? - javascript

I am having trouble figuring out how to use dojo/aspect with widgets.
Consider the following:
require( [ 'dijit/form/Button' ],
function( Button)
{
var myButton = new Button({label: 'Click me!'});
} );
How would I connect to the button's postCreate() or startup() methods to discover when it has been rendered?
There seems to be no point when I can add advice to a method. See the comments, here:
require( [ 'dijit/form/Button', 'dojo/aspect' ],
function( Button, aspect )
{
// I cannot use aspect.after() here as I have no instance yet
var myButton = new Button({label: 'Click me!'});
// ...but I cannot do it here either as the lifecycle has already kicked off
} );
(The button is just to make it easier to explain the issue. My real-world problem involves widgets that contain other widgets, so I need to know when the whole lot have rendered before performing an action).

By instantiating the widget programmatically, the postCreate method of the widget is implicitly being called. As far as I know there isn't an easy (or really a good reason to) to connect to the postCreate stage in the widget lifecycle.
startup, on the other hand you need to call explicitly when programmatically instantiating a widget:
var myButton = new Button({label: 'Click me!'});
aspect.after(myButton, 'startup', function(){
console.log('startup called');
});
//place button in page (since startup implies the widget has been placed in the page
myButton.placeAt(someDomNode)
myButton.startup();
If you want to do work during the postCreate lifecycle of a widget, you'll likely want to subclass that widget. Doing so would look something like this:
//in a file like my/custom/widget.js
define(['dojo/_base/declare','dijit/form/Button'],function(declare,Button){
return declare('my.custom.widget',[Button],{
postCreate:function(){
//we still want to call the parent class's postCreate function
this.inherited(arguments);
//create some other widgets as well
}
});
});

Related

Notify main page of variable changed by external js

I'm attempting to create a modular sign in script for some webpages I'm developing. In short, I load the script on the main page, fire the main signIn function from a button press, and an overlay div is created on the main page which is managed by the external signIn.js. The external js sets some sessionStorage variables that will be utilized in the main page.
The hope for modularity would be to have signIn.js handle the authentication from the database and have the main page do with the process of signing in as needed (in this specific instance, it gives users access to their projects). Ideally, the sign in will not force a refresh of the main page due to other project goals.
The problem I'm encountering, is how do I notify the main page that the user has signed in without destroying any sense of modularity?
On top of other efforts, the most hopeful was attempting to create a custom event on the main page's document using $(document).on('userSignedIn', function() {...}); but signIn.js apparently cannot trigger this event.
Any suggestions for how to accomplish this or am I just going about this entirely wrong?
EDIT:
So, this was definitely a scope related issue I was experiencing. To flesh out the process, if anyone finds it relevant, signIn.js adds an overlay div to mainPage.html. $("#signInContainerDiv").load("signIn.html") is used to load the sign in form into the page. It turns out, when I was trying to reference $(document), it was using signIn.html's document, and not mainPage.html's. Upon that realization, I just created a div (signInNotify) on the mainPage that I bind the event to ($("#signInNotify").on("userSignedIn", function() {...});) and trigger it in signIn.js.
My own inexperience has conquered me, yet again.
jQuery can help you out when it comes to this. Here's an example from the main page for trigger
$( "#foo" ).on( "custom", function( event, param1, param2 ) {
alert( param1 + "\n" + param2 );
});
$( "#foo").trigger( "custom", [ "Custom", "Event" ] );
jQuery Page Reference
Another solution is to use some library like amplify.js, it has publish/subscribe functionality which can be useful for implementing the "observer pattern". You could also implement your own library for that, the code could be something like this:
// the implementation
function Notify () {
this.listeners = {};
}
Notify.prototype.subscribe = function (event, callback, context) {
this.listeners[event] = this.listeners[event] || [];
this.listeners[event].push({ callback: callback, context: context || null});
};
Notify.prototype.publish = function (event/*, args...*/) {
var args = Array.prototype.slice.call(arguments, 1);
(this.listeners[event] || []).forEach(function (x) {
x.callback.apply(x.callback.context, args);
});
};
// usage:
// an instance, or can be implemented as a singleton
var global_events = new Notify();
// wherever you want to be notified of login events
global_events.subscribe('login_success', function () {
// do something with the arguments
}, myContext/*optional*/);
// after success login
global_events.publish('login_success', user_credentials, other_data);
// and all subscribers (listeners) will be called after this
I have used that code for similar purposes and also used amplifyjs a couple times, you can read more about Amplify Pub/Sub.

Bind class method to AJAX success callback within event handler bound outside of class

My issue:
I have created a JavaScript class that is used by our dev team across our site. It is essentially functionality for a grid/table like structure that allows the user to select items and perform actions on these items with provided action buttons.
Action button workflow:
User clicks action button
Popup appears: "Are you sure you want to perform this action on these items?"
User clicks "Yes": AJAX call is made and popup closes upon AJAX success.
User clicks "No": Popup closes.
Right now, these action buttons are individually bound in jQuery by our Devs on each page that needs it. Any given page could have a handful of event bindings.
After successful completion of any of these actions, I would like to run Grid.afterActionComplete() from any given instantiation. I would like to run Grid.afterActionComplete() within the actions AJAX success callback. I know I could expose (return) afterActionComplete in my class and have the Devs run the function themselves, but this is not ideal.
My requirements:
Would like to keep the amount of additional code for Devs to a minimum
Many AJAX request can be made from any given page (some from non-action buttons), so using a global ajaxSuccess event wouldn't necessarily work. Plus, I would hate to use an event with that global of a scope.
My question is two-fold:
How could I dynamically bind Grid.afterActionComplete() to any given action's AJAX success callback? (if possible)
How would I best incorporate the action bindings into the Grid class upon instantiation to further encapsulate my code?
My sample code:
/* [START] Pre-existing code */
var Grid = function(gridID){
var gridID = $(gridID),
afterActionComplete = function(){
// Ideally, I'd like to bind the function here
},
refresh = function(){
// Refresh grid
},
return {
refresh : refresh
}
}
var popup = function(){
$('.popup').show();
// Pops up a window with an Action button and Cancel button
// Just a placeholder to help explain concept
}
/* [END] Pre-existing code */
/*
[START] Dev defined code
Devs will be creating these event bindings across the
site.
*/
var myGrid = new Grid("#grid1");
$('#actionPopupButton').click(function(){
popup();
$('.popup #actionButton').click(function(){
$.post( "ajax/test.html", function( data ) {
myGrid.refresh();
$('.popup').hide();
// I'd like to inject Grid.afterActionComplete() here
// Maybe create custom event and trigger() it here?
// Ideally, I would love to not require the Devs insert additional code hre, but I'm not sure that's possible
});
});
});
/* [END] Dev defined code */
I've been pondering these questions for a week or so now, and would love any suggestions to help me solve this issue. Thanks!
Assuming all of the "developer code" is very similar, I would think ideally you would want to have the developers pass in appropriate parameters instead of create a bunch of very similar code.
For instance, if you made the popup method part of Grid and has the url and callback passed to the function you could do something like this:
popup = function(url, callback){
var that = this;
$('.popup').show();
$('.popup #actionButton').click(function(){
$.post( url, function( data ) {
// call the passed in callback
callback(data);
// do your post-callback stuff
that.refresh(); // assuming this happens in every callback
$('.popup').hide(); // assuming this happens in every callback
that.afterActionComplete();
});
});
}
Then your example developer code would become this:
var myGrid = new Grid("#grid1");
$('#actionPopupButton').click(function(){
myGrid.popup("ajax/test.html", function(data){
// do instance-specific stuff here
});
});
Correct me if I am wrong. You want Grid.afterActionComplete() called only on specific AJAX requests, correct? This is why you cannot use .ajaxSuccess()? If that is the case, the best thing you can do is to trigger a custom event.
If you feel that is too much work for the developers, you can abstract the $.post functionality inside a custom function of your Grid class. After you execute the callback, you can then make the call to Grid.afterActionComplete(). If it is mandatory that Grid.afterActionComplete() be called after those requests, it would make more sense to take this route since it seems to be part of the contract. This way you can protect the developers from themselves (i.e., if they forgot to call the function or trigger the custom event) by making it so that they can only make the post using the Grid API.

Programming jQuery UI like ExtJS

I'm trying to develop an abstraction layer to jQuery UI that allows define Widgets as Objects just like (or similar) to ExtJS. This is the concept:
var mydialog = new $.ui.dialog({
modal:true,
renderTo:'body',
title:'The Windows Tittle',
content:'The content of the Window'
});
Now I can say:
mydialog.show();
The first step (i think) was to add a Class creation function to jQuery, this allow to make classes:
$.MYNAMESPACE.dialog = $.Class ({
constructor:function(){}
//methods and properties
});
And here comes the real problem: What I have to put inside the previous class definition to link the real $.ui.dialog with mine? What I meant is that I don't want to create a new widget, I just want to reuse the code behind predefined jQuery UI widgets in order to create an abstraction layer that allows OOP with jQuery UI.
Thanks in advance
have you tried the jquery-ui widget factory? You might be re-inventing the wheel.js
slideshow on what you get with the widget factory
official splash page and the api
quick idea what it's doing. I want a new dialog with some custom events on it
//this is instantiating the widget, does not need to be in the same file
$(function(){
$(".some-selector").miDialog({autoopen:true //because we can});
});
//this is a definition, not an instantiation of the widget. aka,
$.widget("mi.miDialog" //namespace
,$.ui.dialog //inherit from this jquery widget
,//start your widget definition
{ options:{autoopen:false,//overwrite parent default option, while still giving instance option to override our definition's override of parent
someInstanceSafeOption: {why:"not",have:"a",subobject:"option"} },
//underscore functions are 'private' unless you dig into the prototype manually
_create :function(){
//you'll need this function. guaranteed to run once.
// upcoming version call parent create
this._super();
//current version call parent create
$.ui.dialog.prototype._create(this.options);
this.element.addClass("mi-dialog"); //help with custom styling
this._trigger("created"); //trigger custom events
//register for events here, as _create() will only run once per individual instance
},
_init:function(){
//this will run every time someone calls $('some-selector').miDialog();
//i have yet to use it much
},
publicFunction: function(some, params){
//this function does whatever you want, and is called $('some-selector'.miDialog("publicFunction", some,params);
},
_destroy: function(){
//clean up after your widget's create function, if needed.
}

Event is firing multiple times (Parse.com)

I'm writing an app in Parse, using the JavaScript framework. In my view, I have a link with the class 'new-page'. In the JS code, I have:
events: {
"click .new-page" : "createPage",
}
createPage is:
createReel: function() {
var self = this;
// Get current pagelist
var pages= new PageList;
pages.query = new Parse.Query(Page);
pages.query.equalTo("owner", Parse.User.current());
pages.query.ascending("order");
pages.fetch({
success: function(pagelist) {
var newPage = new Page;
newPage .save({
success: function(newpage) {
// Redirect to page edit
new PageEditView();
}
});
}
});
}
First time round, this works fine - the new page is created, and it goes to edit mode for that page. But if I then go back to the view with the 'Add Page' button and click it again, I get 2 new pages. If I do it again, I get 4, and so on.
I assume the event is 'building up', so that the more times the button is clicked, the more times the event gets fired.
I'm not sure where to start looking.
Late answer, but I just had a similar issue.
The mistake I made was that I created the view instance again every time I needed it.
That is not how Backbone is ment to work.
Views are (usually) created once. And when their model changes, the are being rendered again.
you seem to be instantiating your view every time the user saves at this line
PageEditView();
That's when the events stack up.
Instantiate that view once when you start up the page and then render it whenever you update the model for that view.
In native bakbone, event handlers can listen to model changes. Somehow it won't work in the Parse backbone version (or I can't get it to work). So for now, I do that manually with
PageEditView.model = model
PageEditView.render();
Make sure you undelegate events before calling the new PageEditView.

Can an Orbeon event in an XBL be dispatched from Javascript?

Orbeon version: Orbeon Forms 3.8.0.201005270113
I have the following code in a Javascript file. This code is executed, but it seems like the model in the XBL is not found.
ORBEON.xforms.Document.dispatchEvent("model-name", "event-name");
Here is the model in the XBL. There are several models in the XBL. I don't see any message, so it seems as though the model isn't found. I don't see any errors in the logs.
<xforms:model id="model-name" xxforms:external-events="event-name">
<xforms:action ev:event="event-name">
<xforms:message>Test</xforms:message>
</xforms:action>
</xforms:model>
Does anyone know if there is some trick to getting a dispatch to work from Javascript to XBL?
Thanks very much!
UPDATED:
Another thing that could be the problem (maybe?) is that calling the javascript from the XBL using instance(this) isn't working. I wonder if the instance of the class isn't tied to a component instance, therefore it can't find the model?
Here's the call to the javascript from the xbl that doesn't invoke the init method:
<xxforms:script>YAHOO.xbl.fr.myTest.instance(this).init();</xxforms:script>
Here's the call that does invoke the init() method:
<xxforms:script>YAHOO.xbl.fr.myTest.prototype.init();</xxforms:script>
Here's the javascript:
YAHOO.namespace("xbl.fr");
YAHOO.xbl.fr.myTest = function() {};
ORBEON.xforms.XBL.declareClass(YAHOO.xbl.fr.myTest, "xbl-fr-myTest");
YAHOO.xbl.fr.myTest.prototype = {
},
init: function() {
alert('test');
},
valueChanged: function() {
},
};
AFAIK you can't address the XBL-internal model directly from outside, because of its strong encapsulation.
Instead, you'll have to dispatch the event to the xbl component node. For example, if you want an instance of the fr:currency XBL to handle a certain event, you'll have to dispatch the event to that fr:currency element that's part of your XForm.
Inside the XBL, you can define xbl:handlers to act upon that event, triggering some JavaScript action or something else.

Categories

Resources