I want to build a jquery-ui widget and I am unsure of the best way to approach this.
The widget will manage the sate of some data that is hosted inside of a jquery-ui dialog.
Should I build a custom widget, in the widget create function add some elements to the widget target and then call the dialog widget on my widgets target.
Or
Is there a way to inherit from the jquery-ui dialog and just override the content part of it?
There is a way to extend other widgets:
$.widget("ui.customwidget", $.ui.dialog, {
options: {
// your options
},
_create: function() {
$.ui.dialog.prototype._create.apply(this);
// constructor
},
destroy: function() {
// destructor
$.ui.dialog.prototype.destroy.apply(this);
},
_setOption: function(key, value) {
$.ui.dialog.prototype._setOption.apply(this, arguments);
// process the setting of options
}
// other methods
});
But I'd not encourage using it on a dialog, slider etc. because e.g. buttonset relies on the existence of the button widget and will (and can) not recognize if the element is an instance of a widget that extended button. Therefore it just creates new pure button widgets, what leads to a messed up layout and DOM. Overriding parts of a widget is also critical: The extending mechanism for widgets was introduced not so long ago, when some widgets already existed. The developers of them did not have this feature in mind, so there may still be issues with this.
I aggregate my widgets (your first option): Just extend the $.Widget and make the element a dialog too. Then add event listeners for the properties that need to be synchronized between the dialog and your custom widget.
$.widget("ui.customwidget", $.Widget, {
// ...
_create: function() {
$.Widget.prototype._create.apply(this);
this.element.dialog();
}
// ...
});
This way more robust than extending other widgets (except you built the parent and know what you are doing), but it has it's disadvantages too. E.g. do you accept setting options of the aggregated widget too, or just parts of it? Or do you do none of these and force the user to call the dialog for everything that is not handled in your custom widget? I prefer the second option: It's at least honest, because your widget does't promise things it can't hold, but it's ugly too, because you may once call one, then the other widget.
I'm still not that happy with my solution, but extending widgets put me in front of a whole load of new problems whose solutions would have been either to patch the jQuery UI source or to write an ugly hack.
(I just noticed that this question is about a year old, and the asker may not have this problem anymore. But I'd written all the stuff above already and think it's not that bad to not be posted.)
Related
I'm learning Dart by making a simple webapp. the app ui I have in mind has two parts, one is a control panel, the other is a workspace. by clicking buttons in the control panel, user should be able to control the workspace.
both the control panel and the workspace are custom polymer elements. In the Control Panel's dart class, I can access itself by using shadowRoot.querySelector, but since the control panel needs to control the workspace, I need to access the workspace also. but I don't know how to do that. I tried querySelector for example, It gave me null. I understand it is a shadow DOM in the workspace tag, but how to access other tags' shadow DOM?
I can't find anything online, every example and document seems to only use shadowRoot to access self elements.
It is difficult to access the shadow DOM of another element, and this is by design. Instead of having your two custom elements so tightly coupled, a better approach would be to use events or signals. Your control panel element should take user input and fire appropriate events using the convenient fire() method it inherits from the PolymerElement class. Your application can catch and then relay those events to your workspace element. If that seems overly circuitous, you can use Polymer's <core-signals> element to pass events without dealing with intermediaries.
As an example, inside your control panel element, you might have a bold button.
<button on-click="{{boldClicked}}">Bold</button>
When that button is clicked, the control panel's boldClicked() method is executed in response. It might look something like this:
void boldClicked(Event event, var detail, Element target) {
fire('core-signal', detail: {'name': 'bold', 'data': null});
}
Then in your workspace element's HTML file, you might have:
<core-signals on-core-signal-bold="{{boldEventReceived}}"></core-signals>
And finally, in your workspace element's Dart class would be a method like so:
void boldEventReceived(Event event, var detail, Element sender) {
// manipulate workspace shadow DOM here
}
This is just one of several ways to accomplish this. You can look over the Dart team's <core-signals> example for more.
And of course, if you're using Polymer to its full potential, you will find that you need to do very little manual DOM manipulation. Using data binding and data-driven views is a winning strategy.
You can either use a selector that pierces though all shadow boundaries querySelector('my-tag /deep/ some-element') or querySelector('* /deep/ some-element') or as selector that just pierces through one level of shadow boundary querySelector('my-tag::shadow some-element') or alternatively
place both elements within the <template> of another Polymer element then you can connect attributes of both components with the same field on the common parent element (this is the preferred method in Polymer.
The solution of #user3216897 is fine of course especially if the elements don't share a common parent.
Instead of shadowRoot.querySelector you should be able to use $['abc'] if the element has an id attribute with the value 'abc'.
In (latest) Dart Web UI, what's the best way to process an element when it's added or removed from the DOM by a template? Ideally I'd like to register a callback right in the template, but that's not a requirement.
Background: I need to register/unregister certain DOM elements from two JS libraries (one of which is a JQuery plugin). Since my template uses loops and conditionals (and data binding), elements can come and go at any time, and I can't just register them after the initial rendering.
It is possible to add callbacks to your component's class that trigger when it is either created, inserted into the DOM, or removed from the DOM.
Web UI Specification: Lifecycle Methods
class MyComponent extends WebComponent {
inserted() {
// Do stuff when inserted into DOM.
}
removed() {
// Do stuff when removed from DOM.
}
}
I'm having trouble figuring out the correct syntax to use when a plugin/widget requires using a custom 'event' method instead of the standard jQuery events.
The jQuery UI MultiSelect Widget doesn't support the change method, and instead provides a click event which is fired when a checkbox is checked or unchecked. I'm trying to use the syntax supplied in the widget's documentation for the click event so that I can replace several change functions including the one shown below, but I'm having trouble figuring out the correct syntax. The structure for the click event is:
$("[name=Item]").on("multiselectclick", function (event, ui) {...});
I've tried inserting the function in between the curly brackets, and I've also tried .on("multiselectclick", function () {...}); without success, so I'm assuming that I need to insert something here: (event, ui)
I've managed to get the functions partially working by using a slightly different method $("[name=Item]").multiselect({ click: function (event, ui) {...}, but I'd really like to understand how to use the plugin's custom event so that I can get the functions working properly. I posted a fiddle with a very simple example here http://jsfiddle.net/chayacooper/cZRy9/5/, and included notes to indicate what wasn't working properly.
One of the functions where I'm trying to replace a change event with the widget's click event:
$("[name=Item]").change(function(){
$("#Styles1, #Styles2").hide();
$(".accordion").accordion("option", "active", false);
if( $("[name=Item]").val() === "Dresses"){
$("#Styles1").show();
$(".accordion").accordion("option", "active", 0);
$('varItem').val('Dresses');
}
else {...}
});
First issue about not hiding the Styles element when dresses is unchecked is straightforward. There is nothing in code to check state of the checkbox, only the value
Fix with:
if ((ui.value) === "Dresses" && ui.checked)
Using the ui object within a jQuery UI patterned widget is usually best source for your data or state , rather than using jQuery selector to look for the same thing.
I have never used this plugin, so to figure out what was available to me, I simply used console.log(ui) within the click handler so I could inspect the ui object to see what properties it contained, and found the checked property to add to above if conditional
If you do the same on a more complex widget like jQueryUI datepicker will see quite a significant number of ui properties , some of which are also method functions
DEMO (using custom event)
http://jsfiddle.net/cZRy9/2/
I'm using Eric Hynds' multiselect widget.
I'd like to modify it, adding an option and including an extra step in the _refresh method. Now I can modify the plugin easily enough to accomplish my aims, but what I'd like to do is to create a wrapper or similar that means I can update the widget in future, while keeping my changes separate.
The widget is built using jquery ui 1.8 widget factory .
What is the best way to achieve my aims?
Without knowing all your details, you can try extending the widget.
$.extend($.widget, {refresh:function(){your function here}});
I've used this for adding functionality to a widget, but not for overriding existing functions.
You also try using inheritance, which is probably the right call here. When defining your new widget there is an argument to pass in an existing base widget
(function ($) {
$.widget('an.originalwidget', $.a.yourwidget, {
...
refresh: function() {
// override code
}
});
}(jQuery));
Here is a great link to try:
http://msdn.microsoft.com/en-us/library/hh404085
Edit: I did this using Jquery 1.7.2, I notice you are using 1.8, while I don't think there will be any changes to this, you might want to check
I'm trying to maintain a widget that triggers events using one of these two lines of code:
this.element.trigger('change'); // or...
stop: function (event, ui) { that.element.change(); }
The word 'change' occurs only 4 times in the code, in one of the 2 forms above. However, I've got no idea what's going on here. There's no event handler in the change call, and there are no bind, delegate, on, or live calls, so something external is presumably setting this up. Can anyone fill me in? Are there any docs on this? Thanks.
Those two lines of code simply trigger a change event to this.element using two different allowed syntax.
Using .trigger():
this.element.trigger('change');
Or using a shorthand method .change():
that.element.change();
You can actually bind an event handler to the element represented by this.element to handle this event.
Without knowing your plugin, it is difficult to answer you precisely on what is this.element.
But take the example of the autocomplete plugin. In this one, this.element is actually the input field the autocomplete plugin is applied to. If the change event was triggered like supposedly done in your question, you could bind an event handler to the input like this:
$('#myinput')
.autocomplete()
.bind('change', function() { });
Now if this plugin relies on the jQuery UI Widget Factory, it is advisable to use the method _trigger() to trigger events instead of the jquery .trigger().
Using _trigger() will make sure to execute any callback defined in the plugin's option with a correct context and also trigger that event for this.element (like above). So you could have:
$('#myinput')
.somePlugin({
change: function(e, someData) {
// "this" here will be `this.element´
}
})
.bind('change', function() { ... });
The answer turned out to be simple - there was no event handler, there were no bind/etc calls, jQuery does nothing behind the scenes, so the trigger calls did nothing. I commented them out and the widget behaved exactly the same. Doh.