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'.
Related
For a forms framework in which I like to use JSF as the real UI frontend, I am searching for a way that a parent component gets informed if in a child component the value is changed. The facelet of a basic 'control' looks like this(body/head omitted since no-one can run it anyway without a dozen classes):
<xf:input ref="/my/xpath/value">
<xf:label>Label</xf:label>
</xf:input>
The xf:input component which I developed, dynamically creates a real ui component (PrimeFaces ones) based on the type of the value that ref="/my/xpath/value" points to. This real ui component is created in a preRenderView event like is done in this example. It is handled in the following method in the 'parent' control
#Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
FacesContext context = FacesContext.getCurrentInstance();
if (!context.isPostback()) {
control = createControl(context);
//context.getApplication().unsubscribeFromEvent(PostValidateEvent.class, getControl().getClass(), this);
control.subscribeToEvent(PostValidateEvent.class, this);
}
}
The actual controls all have an programmatically added ajax handler added to it, which makes it possible to just process the specific input ('implicit ajax'). Default JSF component validations are normally applied and this all works great.
The issue/challenge is that in this 'wrapper' component I'd like to be informed of value changes after the validation. My first idea was to us the subscribeEvent on the dynamically added control like this:
control.subscribeToEvent(PostValidateEvent.class, this);
The subscribing works, but on the postback, an NPE is thrown in the UIComponent (Mojarra 2.2.9) because the wrapped is null in the following method
public boolean isListenerForSource(Object component) {
if (wrapped instanceof SystemEventListener) {
return ((SystemEventListener) wrapped).isListenerForSource(component);
} else {
return instanceClass.isAssignableFrom(component.getClass());
}
}
This might be because the actual component seems to be newly created when the data is submitted en hence the 'subscription' is lost.
Registering on the ViewRoot does not work since the source of the event is always the ViewRoot and registering on the Application is plain wrong.
It might be that I'm looking for a solution in the wrong direction but for now I'm clueless. Keep in mind that I have no direct control over the created ui controls, nor do I want to override their renderers if I can prevent to. So signalling the parent from the child control is not an option to.
Other things I tried:
Using valueChangeListeners but that did not work either with lots of other problems (including ways to make it extensible)
Using composite components with binding but that failed including them dynamically, requiring naming containers that conflict with the id's required by the rest of the framework, the positions of labels, hints and alerts in the xhtml and/or resulting dom
Taghandlers to manipulate the tree when creating them
This all is with Mojarra up to 2.2.9 (did not check newer yet or MyFaces)
Adding the component in the PreRenderViewEvent works nicely. The thing is that you do not seem to be able to have subscriptions to events survive a request. The actual components are recreated (in the RestoreViewPhase I assume, did not check) and then the subscription to the event is still there, just the 'wrapped' context where it should be called is empty.
Adding the PostValidationEvent event in the PostRestoreStateEvent of this specific component (it is the only one in the FacesContext.getCurrentInstance().getPartialViewContext().getExecuteIds()) makes it fire as mentioned in the comments. The trick (hack/workaround/...) to get rid of the NPE in the next request is to actually remove the event again.
((UIComponent) event.getSource()).unsubscribeFromEvent(PostValidateEvent.class, this);
I'll try to create an example without any PrimeFaces or OmniFaces and see what happens then since they both seem to be wrappers around the context and I want to make sure they are not the cause of the behaviour.
I have my-app as main application component in index.html file and uses model.dart as its model which is my application model.
my-app has my-component as its content. When user interacts with my-component, I need to update values in model.dart.
<my-app>
<my-component></my-component>
</my-app>
one approach I thought is to access my-app in my-component dart file and use its model property to access model.dart.
Is this the right approach to access model of the application? Also how to get my-app from within my-component?
I would submit that having the child component have awareness of its parent is not a particularly good pattern.
But you are right, often what happens in the child component changes a value in the parent, or in a model bound to the parent. For cases like these, I have found that the child can dispatch an event, and the parent can choose to interact with that event as it sees fit.
Dispatching an event is as simple as doing the following (this is class MyComponent):
dispatchEvent(new CustomEvent('foo'));
And the parent can listen for that event like this:
<my-app>
<my-component on-foo="{{someMethodOnTheParent}}"></my-component>
</my-app>
In effect, the child simply broadcasts that something has happened, but has no control over how (or even if) the parent responds. If <my-component> is used by a different parent, that parent could choose to respond to the custom event in a different way:
<another-element>
<my-component on-foo="{{someOtherMethod}}"></my-component>
</another-element>
The callback that is triggered in parent could do pretty much anything, including modifying the model.
Hope that helps.
Dart Polymer >= 1.0.0-x
new PolymerDom(this).parentNode
See also https://www.polymer-project.org/1.0/docs/devguide/local-dom.html
Dart Polymer <= 0.16.x
#ShailenTuli is right about encapsulation should not be broken.
But also JS Polymer elements access the parent in their layout elements because it's still convenient in some scenarios.
This works now in PolymerDart too.
(this.parentNode as ShadowRoot).host
Using Polymer Dart I often need to get hold of the Polymer-Element object behind one of the child elements.
ButtonElement nextButton;
void inserted()
{
//Get hold of the elements
nextButton = shadowRoot.query('#nextButton');
//Do some thing useful with nextButton
}
<template if="{{emailValid}}">
<button id="nextButton" on-click="nextStep">
</template>
This works fine. However if in this case nextButton is underneath a conditional template its not part of the DOM when inserted() is called and is therefore not found. Is there anyway other way to get hold of it?
Otherwise I will have to some how determine when that conditional template is displayed and grab it then.
This might depend on what exactly "Do something useful with nextButton" means, but the Polymer-ic way to accomplish this is generally to encapsulate any reusable behavior together with the DOM it operates on. That is, instead of including code to operate on #nextButton in the enclosing element's inserted method, create a new custom element, let's call it super-button, and put the relevant code in super-button's ready or inserted method.
Then, if you find some behavior that really should be outside of super-button, follow the same pattern as the on-click handler you use above. Have super-button fire a custom event at the appropriate time and then declaratively map a handler to that event:
<template if="{{emailValid}}">
<super-button on-click="nextStep" on-my-special-event="mySpecialEventHandler"></super-button>
</template>
I want to increase my knowledge concering jquery-ui, so I look at their source code.
I work with jquery-ui 1.19m5.
When I look at the 1.19m5 source code of ui-dialog I see after the call to the widget factory:
$.extend($.ui.dialog, {
...
getTitleId: function($el) {
...
I do not understand this. Why don't they put the getTitleID method directly into the prototype literal (the 3rd parameter with the widget factory call)?
jQuery UI is designed to trigger methods from the appropriate widget API. So to call a method, you would do $('.selector').widgetName('methodName',arguments);
This allows the plugin developer to easily add private/public functions, as well as avoid namespace collision. It is possible to have an object that is resizeable, draggable, and dropable all at one...these three plugins share some method names, such as the enable/disable function.
In this example, if they added it to the prototype under 'enable', this would only allow you to enable/disable the functionality of the last widget you added, making it impossible to have control over all of the widget types. With their API, you can specifically select a widget to modify. Ex. $('.selector').draggable('disable'); which would disable the drag feature, and keep the resizable and droppable widgets fully functional. Keep in mind, many widgets have the same or similar methods, events, and/or option names.
Long and short, it is for namespacing purposes. This also makes it easier on the developer because he only has to worry about the top level widget name when writing his/her own widgets, without worry of what other methods, options, data, etc that has been added to the prototype directly.
They could add it to the prototype under a second layer, possibly, but this doesn't really save you (the user) any time, and probably would not look as clear in code. or they could add a prefix to every single function to specify namespace, but that would clutter your code, and create more checks and potential points of failure for developers creating their own widgets.
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.)