How to pass an object from ng-repeat to component using AngularDart? - dart

I have a component called
<checkbox_component ng-repeat="ccc in cmp.teachers1"
label="{{ccc.name}}" objectValue="{{ccc}}"></checkbox_component>
Should I declare objectValue as a NgAttr? but that doesn't work I think it only works with strings.
Here I want to complain, I find it relatively difficult to communication between components. Lets say I have a component which is a page and then it has some components of mine on that page, its slightly hard to communication from the child components to the parent components and back again, either due to my lack of knowledge or some limitation.

Either use the #NgTwoWay annotation or the NgModel Decorator:
<checkbox_component ng-repeat="ccc in cmp.teachers1" ng-model="ccc"></checkbox_component>
#NgComponent(
selector: 'checkbox_component[ng-model]',
...
)
class CheckboxComponent {
NgModel _ngModel;
CheckboxComponent(this._ngModel);
...
}
Take a look at how NgModel is used at class InputCheckbox here.

You are right. #NgAttr can only work with literal values but you can use #NgTwoWay

Related

Writing extensible components in Angular 2

I'm currently using Angular2 (Dart) to create a reusable search box component, which includes a suggestion box.
The trouble I'm running into is that I'm not sure how to make the suggestion box extensible by supporting richer suggestion types, such as suggestions with icons, suggestions with multiple lines of text, varying styles on the text, animations, etc.
Currently the suggestion box HTML (stripped down) looks a bit like this:
<div class="suggestion-box-container">
<div class="suggestion" *ng-for="#suggestion of model.suggestions">
{{ suggestion.text }}
</div>
</div>
And the Dart code looks sort of like this:
#Component(
selector: 'suggestion-box'
)
#View(
templateUrl: 'suggestion_box.html',
directives: const [NgFor]
)
class SuggestionBox {
final SuggestionModel model;
// ...
}
class SuggestionModel {
List<Suggestion> suggestions = [];
// ...
}
class Suggestion {
String text;
// ...
}
I'm wondering how I can modify this design so that a user can extend Suggestion and write their own corresponding suggestion component.
I considered something like
<basic-suggestion *ng-if="suggestion.type == 'basic'"></basic-suggestion>
<icon-suggestion *ng-if="suggestion.type == 'icon'"></icon-suggestion>
...
But of course that would get out of hand quickly as more suggestion types are supported, and would also require duplicating the (^hover) and (^click) handlers to each type of suggestion. Worse yet, it would require modifying the original library rather than just using it in a modular way.
Is there an elegant solution to this sort of thing? Or would users simply be better off writing their own suggestion box entirely if they wanted richer suggestions? I'm hoping to avoid that since I want to bake in a bunch of accessibility functionality into the search box/suggestion box that the user shouldn't need to rewrite.
EDIT: based on Gunter's answer I've managed to improve this by a little bit:
<suggestion-box>
<div *ng-for="#suggestion in suggestions">
<suggestion suggestion-instance="suggestion">
<!-- Custom suggestion code goes here.
The following is an example -->
<img src="{{suggestion.iconSrc}}"></img> {{suggestion.text}}
</suggestion>
</div>
</suggestion>
I have a new suggestion component that looks a bit like this:
#Component(
selector: 'suggestion',
properties: const ['suggestion: suggestion-instance']
)
#View(
template: '''
<div (^click)="handleClick(suggestion)"
(hover)="handleHover(suggestion)">
<content></content>
</div>
'''
)
class SuggestionComponent {
final SuggestionModel model;
final Suggestion suggestion;
// ...
}
This is a bit nicer now because the user now gets accessibility/keyboard control/mouse control out of the box. But this still isn't ideal. I'd somehow like the template to automatically determine the appropriate component to use based on the actual subclass of the suggestion. I suppose I could define a method in each component like getTemplate() but that probably wouldn't be a very Angular-esque way of doing it.
I haven't really used Angular2 but I think there are at least two ways to do it
- You should be able to extend a component (just create a derived class and specialize the behavior
- Use composition with the <content> tag of the shadow DOM. I found some docs here https://www.airpair.com/angularjs/posts/creating-components-p3-angular2-directives
- Or a combination of the two
update
Support for extending components is coming with version 3.2.
Annotations like #HostBinding(), #HostListener(), #ViewChild(), #ContentChild() will be recognised on subclasses, so will be interfaces for OnInit and other lifecycle callbacks
There are different ways beside inheritanc to make a component customizable.
You can pass component types to be instantiated by your search box using DynamicComponentLoader,
pass children to be displayed at <ng-content> locations,
or pass <template> content to be added dynamically by your search component, and probably others.

Notify Observable-Watchers programmatically in Dart

Once again, a Dart/Polymer related question.
I wanted to use the Parse.com JavaScript library, but since it's not available in Dart I've written Wrapper classes which store a JsObject and delegate all calls from Dart to the corresponding JavaScript object. Basically it's like a proxy.
Guess what, it works pretty great.
However, my observables don't. To understand this, you have to take a look at the following structure of one of my "proxy"-classes.
class ParseObject extends Observable {
JsObject _jsDelegate = new JsObject(context['Parse']['ParseObject']);
void set(String key, dynamic value) {
_jsDelegate.callMethod('set', [key, jsify(value)];
}
dynamic get(String key) {
return dartify(_jsDelegate.callMethod('get', [key]));
}
}
The HTML code of my Polymer Element looks like this:
<div>Name: {{project.get('name')}}</div>
Since the data binding is only evaluate in case the parameter of the method changed, it will never be updated and thus even though the name is changed, the old one will stay in place.
The solution I came up with is to store all the values the user is setting in the ParseObject#set(String, dynamic) method into a Map which is observable. This works but I think it's quiete dirty since I have to make sure that both Maps, the one in Dart and the one in the ParseObject's JavaScript representation equal.
Thus I am looking for a better solution and I think of some kind of method to tell Polymer to reevaluate it's data bindings.
Does such a method exist or are there any other possibilities to address this problem?
Extending observable by itself does nothing yet.
You need to annotate the getters with #observable (and if you are not using Polymer, you also need to add the observable transformer to pubspec.yaml). You can't make functions observable (this works in Polymer elements but not in Observable model classes. For more details about observable see for example Implement an Observer pattern in Dart or Dart: #observable of getters

Using angular-dart components, supply own instances instead of calling the constructor

In angular-dart it is possible to create your own components as can be seen here. If you use custom tags in the html like this:
<rating></rating>
angular will create a component by calling the constructor of the class associated with rating, in this case new RatingComponent() (if i'm not mistaken).
I know you can add attributes for having some control over it, but i was wondering if it is possible to supply your own instances, instead of angular calling the constructor. What if i have a list of buttons in the main controller, how to achieve something like this:
<div ng-repeat='b in ctrl.buttonList'>
<fancy-button instance='b'></fancy-button>
</div>
I have the feeling i'm missing something obvious, but i did search around and couldn't find the answer.
edit (for extra clarification): I think it boils down to if you can or can not influence/bypass the call of the constructor by angular. If it was just about generating the html, its easy to not use the component and just generate the html using the main-controller (like below), but if possible i would like to use a component since it also has shadow-dom for sandboxing the css.
<div ng-repeat='b in ctrl.buttonList'>
<input type='button' class='fancy' value='{{b.label}}'></input>
</div>
You want something like
<div ng-bind-html='ctrl.html'></div>
This way you can provide the html by the controller.
You would have to create your own implementation of NgBindHtmlDirective (just a few lines) though, because the used NodeValidator of ng-bind-html doesn't allow custom tags (yet).
Maybe there are more sophisticated ways but I don't know Angular very well yet.
=== Edit ===
I think what you try to do is not Angularish.
You can ng-repeat over a model that provides values for the attributes of the fancy-button or input.
In the components class you have access to these attributes and can make the behavior and appearance depending on that attributes.
Using ng-bind-html you can create the markup using code if you don't know at development time what kind of tag you want to use.

Access Dart / Polymer WebComponent from main

Think of the following:
I've got a table data grid webcomponent and another component providing a data feed. In the applications main acting as kind of controller, i'd like to wire and setup those two components. Therefore i need a reference to underlying table grid instance Dart class and call methods on the "Component API' (to provide the table grid's tablemodel with data).
How do I access the Dart Class instance of a webcomponent instance from outside ?
Probably I missed something fundamental or are polymer webcomponents meant to interact only using databinding and stringy attributes stuff ?
Follow up: Found it !!
RLTable table = querySelector("#apptable").xtag;
does the job
As zoechi pointed out, xtag is not necessary.
var component = $['myComp'];
var componentXtag = $['myComp'].xtag;
print(component == componentXtag);
prints true. Therefore both
component.method()
componentXtag.method()
work fine
You don't need xtag. Some weeks/months ago this was a workaround until the final solution was landed.

Grails internals : Auto mapping and Domain object creation

I am trying to make a taglib to represent an object (to read and display at the UI). When creating an object (save method in the controller), I see the domain class and association are created by the auto assignment of parameter
def Book = new Book(params)
It also maps complex types (for eg: joda time). I wonder about the naming convention necessary to facilitate this mapping. Out of curiosity, can someone also point where in the grails source code I could see how grails handles this mapping. I'm still learning Spring and probably this would be a good exercise.
Thanks,
Babu.
AFAIK the naming conventions are rather straightforward. If there's a field params.foo and the object you are binding to has a field foo, it will bind the value, assuming the type conversion works properly. If there's a params.bar.id set with an Long value and your object has a complex property of type Bar, it will lookup this instance and inject it.
If you need more control over the binding process, you might want to use bindData.
If you are interested into the details of the binding process, have a look at Java's PropertyEditor as this is what is being used in the background. I wrote a blog post on how to create and register PropertyEditors a while ago, maybe it helps you getting started with that stuff.

Resources