I defined an angular.dart component like this:
@NgComponent(
selector: 'dartcomp',
templateUrl: 'dartComp.html',
publishAs: 'ctrl',
map: const
{
'val' : '@val'
}
)
class DartComp
{
String val;
calc(a,b) =>a+b;
}
the usage in the HTML is:
<dartcomp id="dc"></dartcomp>
How to access angular.dart component´s attribute val or method calc() from the main dart
a call to the component like
querySelector("#dc").val = "Frank";
throw:
Class 'UnknownElement' has no instance setter 'val='.
NoSuchMethodError : method not found: 'val='
Receiver: Instance of 'UnknownElement'
Arguments: ["Frank"]
What´s the mistake?
Accessing attributes like class properties works in Polymer but not in Angular.Dart.
You have to use the Elements default method .attributes['attrName'] = value
// this doesn't work with Angular
// querySelector("#dc").attributes['val'] = "Frank";
// I found an AngularDart function that may help but it's comment says this is for debugging only
// this example calls the method 'add' on MyComponent
var mc = ngProbe(dom.querySelector('my-component')).directives.firstWhere((d) => d is MyComponent);
mc.add('someValue');
Maybe there is a better way I'm not aware of, but this way should work at any rate.
workaround for calling methods on Angular components/controllers
Example is also available here: GitHub BWU-Dart Playground
This is the best idea I come up with.
Maybe someone of the AngularDart creators knows a better solution.
(I also couldn't find a straight forward solution for AngularJS)
EventBus
/**
* Derived from https://github.com/marcojakob/dart-event-bus
*/
library eventbus;
import "dart:async";
import "dart:html";
import "package:logging/logging.dart";
/**
* [EventBus] is a central event hub.
* In addition it automatically updates the page navigation history ([History]
* by calling [window.pushState] for fired events which indicate, that they
* are supposed to update the [History] and refiring events which led to
* [window.pushState] in the case of a [window.onPopState] event.
*/
class EventBus extends Object {
final _logger = new Logger("EventBusModel");
/**
* A [StreamController] is maintained for each event type.
*/
Map<EventType, StreamController> streamControllers = new Map<EventType, StreamController>();
bool isSync = true;
/**
* Constructs an [EventBus] and allows to specify if the events should be
* send synchroniously or asynchroniously by setting [isSync].
*/
EventBus({this.isSync : false});
/**
* [on] allows to access an stream for the specified [eventType].
*/
Stream/*<T>*/ on(EventType/*<T>*/ eventType) {
_logger.finest('on');
return streamControllers.putIfAbsent(eventType, () {
return new StreamController.broadcast(sync: isSync);
}
).stream;
}
/**
* [fire] broadcasts an event of a type [eventType] to all subscribers.
*/
void fire(EventType/*<T>*/ eventType, /*<T>*/ data) {
_logger.finest('event fired: ${eventType.name}');
if (data != null && !eventType.isTypeT(data)) {
throw new ArgumentError('Provided data is not of same type as T of EventType.');
}
var controller = streamControllers[eventType];
if (controller != null) {
controller.add(data);
}
}
}
/**
* Type class used to publish events with an [EventBus].
* [T] is the type of data that is provided when an event is fired.
*/
class EventType<T> {
String name;
/**
* Constructor with an optional [name] for logging purposes.
*/
EventType(this.name);
/**
* Returns true if the provided data is of type [T].
*
* This method is needed to provide type safety to the [EventBus] as long as
* Dart does not support generic types for methods.
*/
bool isTypeT(data) => data is T;
}
index.dart with MyComponent and main()
library main;
import 'dart:html' as dom;
import 'dart:async';
import 'package:angular/angular.dart';
import 'package:di/di.dart';
import 'event_bus.dart';
class Item {
String name;
Item(this.name);
}
@NgComponent(
selector: 'my-component',
publishAs: 'ctrl',
applyAuthorStyles: true,
template: '''<div ng-repeat="value in ctrl.values"><span>{{value.name}}</span> - <content><content></div>'''
)
class MyComponent {
List<Item> values = [new Item('1'), new Item('2'), new Item('3'), new Item('4')];
void add(String value) {
values.add(new Item(value));
}
EventBus _eb;
MyComponent(this._eb) {
print('MyComponent');
_eb.on(Events.someEvent).listen((e) => add(e));
_eb.on(Events.someEventWithData).listen((SomeEventData ed) {
print('Event received from ${ed.senderId}');
ed.respond(this);
});
}
}
typedef MyComponentEventResponse (MyComponent component);
class SomeEventData {
SomeEventData(this.respond, this.someOtherData, [this.senderId]);
MyComponentEventResponse respond;
var someOtherData;
String senderId;
}
class Events {
static final EventType<String> someEvent = new EventType<String>("someEvent");
static final EventType<SomeEventData> someEventWithData = new EventType<SomeEventData>("someEventWithData");
}
class MyAppModule extends Module {
MyAppModule() {
type(MyComponent);
value(EventBus, new EventBus());
}
}
void main() {
Injector inj = ngBootstrap(module: new MyAppModule());
EventBus eb = inj.get(EventBus);
eb.fire(Events.someEvent, "17");
new Timer(new Duration(milliseconds: 1000), () => eb.fire(Events.someEvent, "33"));
new Timer(new Duration(milliseconds: 2000), () => eb.fire(Events.someEventWithData, new SomeEventData(respond, 'blabla', 'main105')));
}
void respond(MyComponent c) {
dom.window.alert('Demonstrate access to event receiver: Name of 2nd item: ${c.values[1].name}');
}
index.html
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8">
</head>
<body>
<h3>Repeat</h3>
<my-component>
<div>some provided content to repeat</div>
</my-component>
<script type="application/dart" src="index.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
I tried a different approach.
In a static map in the component I store the instances of the components, so that I can access the component later in the main.dart
@NgComponent(
selector: 'dartcomp',
templateUrl: 'dartComp.html',
publishAs: 'ctrl',
map: const
{ 'val' : '@val',
'id' : '@id'
}
)
class DartComp implements NgAttachAware
{ static Map<String,DartComp> comp = new Map();
String id;
String val;
void attach()
{ comp[id]=this; }
}
the components html:
<span> Component val:{{ctrl.val}}: </span>
In the index.html I put 2 of the components
<dartcomp id="dc1"></dartcomp>
<dartcomp id="dc2" val="max"></dartcomp>
In the main.dart I try to change the val attribute of component dc1
DartComp.comp['dc1'].val="frank";
but in the browser I can't see change.
Is there a way to solve the problem like this?