I want to wait until my component is fully loaded. The current approach would be to implement the ShadowRootAware interface. However this does not work, if the component disables the use of shadow dom:
#Component(
selector: 'travel-step',
templateUrl: 'packages/TravelPlanner/travelstep/travel_step_component.html',
useShadowDom: false,
publishAs: 'cmp')
class TravelStepComponent extends AttachAware{
I need to disable the usage of ShadowDom, because I want to use styles from my parent object (e.g. Bootstrap). Is there another way to wait for the dom to be ready?
I want to reference a file upload input. At the moment (angular v.012) there seems to be no other way to upload a file.
You can implement ShadowRootAware interface. For example:
class NgFreeTree implements ShadowRootAware {
void onShadowRoot(ShadowRoot shadowRoot) { ... }
}
It should work regardless of useShadowDom attribute.
It does not give you the error message if you use the following signature:
void onShadowRoot(Node n) {
HtmlElement element = n;
...
}
Related
I'm trying to create a central notification service for the app, to report simple errors via the same "pipeline". It may be the wrong approach, but I need a material-popup stuck to the main HTML body, displaying on-demand as required by various components.
import 'package:angular/angular.dart';
import 'package:angular_components/angular_components.dart';
import 'package:angular_components/laminate/overlay/zindexer.dart';
#Component(
selector: 'hv-alerts',
templateUrl: 'alert_service.html',
directives: [
MaterialPopupComponent,
PopupSourceDirective
],
providers: [
ClassProvider(ZIndexer),
materialProviders,
popupBindings
]
)
class AlertService {
RelativePosition get popupPosition => RelativePosition.AdjacentTop;
bool popupVisible = false;
void setVisible(bool flag) {
popupVisible = flag;
}
PopupSizeProvider popupSize = FixedPopupSizeProvider(
minWidth: 400,
minHeight: 75,
maxWidth: 600,
maxHeight: 75
);
static final AlertService _instance = AlertService();
}
Is there a way to pass in the handler to this material-popup via a singleton or Factory or whatever, and allow other services to call an AlertService.show()?
This is what I would do:
Create an Alert Service (not a component, a regular class)
Inject this alert service into your AppComponent
Use this service to pass around the view that controls the popup and manipulate it accordingly
It is quite easy to pull-off.
The component's lifecycle is dependant on the element in the templates itself. Such if you had two elements with the same selector you would have two components. You can't have them by singletons themselves.
That said how I would do this is abstract the service part of it, and have it be a separate class that would be injected into the popup component and any usage. The popup would listen to a stream know when to show the popup, and the clients would send an event on a StreamController to tell the popup when to show. If you wanted to be a bit safer you could provide two different interfaces the stream, and the stream controller which were backed by the same entity. This would allow you more quickly see who was consuming alerts, and who was producing them.
Is there a way to wait for the component to be initialized?
#Component(
selector: "my-component",
templateUrl: 'component/my.html',
useShadowDom: false,
publishAs: "ctrl"
)
class MyComponent {
#NgAttr('foo')
String foo;
}
#NgAttr('bar')
String bar;
}
MyComponent(){
print(foo);
print(bar);
}
}
Usage:
<my-component foo="bar" bar="baz"></my-component>
When i use the component like this, the constructor prints: null, null
I could write a setter for foo and bar and check on every set if they are both set. But if no value is provided.. my init is never fired.
Do i have to implement a interface which provides a init method or something?
It's the same as shown here Connecting 2 controllers and have access to the first controllers propertie in the second controller
Implement the AttachAware interface. The values of the instance can't be set before the element is constructed therefore there is no chance to have the fields set from the outside when the constructor is executed.
As Günter said implement either AttacheAware or!! ShadowRootAware. onShadowRoot comes after onAttachAware. The Param it gives you (ShadowRoot) is your custom element. The name is misleading - it also works if useShadowDom is false.
I have a custom component and try to test for the emitted events
My component looks like this:
#Component(
selector: "my-component",
templateUrl: 'component/my-component.html',
useShadowDom: false,
publishAs: "ctrl"
)
class MyComponent implements ScopeAware {
Scope scope;
String value = "foo"
void onButtonClick(){
scope.emit('on-select', value);
}
}
The component emits a 'on-select' event when a button is clicked. Works like expected but since i'm a huge fan of testing i want to test for that event.
Is this possible in angular 0.14.0?
I write my tests like the tests in angulardart-sample-app
The whole component testing has really a lot of boilerplate and is everything else then fun to write. Are there simpler ways for component testing in 0.14.0 or the future?
I would like to do some mapping after the members have been set by angular dart:
#Component(
selector: 'travel-step',
templateUrl: 'packages/TravelPlanner/travelstep/travel_step_component.html',
useShadowDom: false,
publishAs: 'cmp')
class TravelStepComponent {
// Deprecated but impossible to replace, since the new syntax is not ready
#NgTwoWay('step')
TravelStep step;
TravelStepComponent() {
// step is null at the moment
}
}
I'm using angular v. 0.12. When the constructor is called, step is still null.
I could do it with a watch expression but I only want to do it once, so this workaround is not how I want to do it.
You can implement AttachAware and put your code into the attach() method.
Similar behavior can be achieved by implementing ShadowRootAware and onShadowRoot().
You need to give Angular some time to evaluate the bindings and assign the values. Use one of these methods according to your requirements.
Sometimes it might help to (additionally) wrap your code into a
new Future(() {
your code here
});
to delay the execution of your code.
Another approach is to implement a setter and execute your logic there
#NgTwoWay('step')
TravelStep _step;
TravelStep get step => _step;
set step(TravelStep s) {
// your code here
_step = s;
// or here
}
I've tried to bind a custom event handler to a WebComponent that has an EventStreamProvider exposed via a getter, but it comes back with "Class 'DivElement' has no instance getter 'onMainAction'.".
Trimmed down component .dart code...
import 'dart:html';
import 'dart:async';
import 'package:web_ui/web_ui.dart';
class SegmentedButtonsListComponent extends WebComponent {
static const EventStreamProvider<CustomEvent> mainActionEvent = const EventStreamProvider<CustomEvent>("MainActionEvent");
Stream<CustomEvent> get onMainAction => mainActionEvent.forTarget(this);
}
Trimmed usage of component…
<x-segmented-buttons-list id="segmented-buttons-list" on-main-action="eventHandler($event)"></x-segmented-buttons-list>
Trimmed code from main.dart…
import 'dart:html';
import 'dart:async';
import 'package:web_ui/web_ui.dart';
const EventStreamProvider<CustomEvent> mainActionEvent = const EventStreamProvider<CustomEvent>("MainActionEvent");
void eventHandler(CustomEvent event) {
print("""
Yabba Dabba Doo!
Type: ${event.type}
Detail: ${event.detail}
""");
}
void main() {
mainActionEvent.forTarget(query('#segmented-buttons-list')).listen(eventHandler);
}
The "MainActionEvent" custom events are being dispatched by components instantiated within this "list" component.
As you can see from the above example I can catch the events if I create an EventStreamProvider in main.dart and target the component, that works fine (but by-passes the Stream getter in the component).
It would be great though if I could dispense with the EventStreamProvider in main.dart and simply bind to the onMainEvent getter on the component.
Is that possible?
Update 2013-05-05:
Siggi explains below that at present it is not possible to do this, but there is a way to reference the component's CustomEventProvider's getter via the element's xtag.
I found that I had to use a Timer to query the DOM after main() has completed because xtags aren't populated until the main() event loop has finished.
void postMainSetup() {
query('#segmented-buttons-list').xtag.onMainAction.listen(eventHandler);
}
void main() {
Timer.run(postMainSetup);
}
With the above setup a new CustomEventProvider isn't needed to monitor the component.
Good question!
I see a couple parts to this question:
using custom events directly on a component: Currently web_ui uses different objects to represent your component and the actual dom element it represents. In the future, we plan to extend directly from "DivElement" instead of "WebComponent" and that will allow you to do what you wrote.
Meanwhile, you'll have to be more explicit when you want to use the host or shadow root of your component. In your example, it seems like you want to attach the event to the host, so you would need to write something more like this:
Stream<CustomEvent> get onMainAction => mainActionEvent.forTarget(this.host);
using 'on-someting-foo' syntax in a component: you probably found a bug/missing feature =). Currently we treat attributes in a special way and bind their values to fields of a component if we identify that the target was corresponds to a component. We do this for value bindings, but not yet for binding custom events. A workaround before this feature is added, would be to query for your element and attach the event by hand:
query('#host-node').xtag.onMainAction.listen(...);