I am trying to create a custom 'radial-selector' element using Polymer in Dart.
My current goal is to make a 'children' field for the RadialSelector class that is observable, so that I can do a template loop through the children and filter through those that are only of a certain element type.
It is not clear how I can make the 'children' field observable without creating another variable that does not conflict with it in terms of namespace. For example, I could just create a "childs" field, make it observable, and then set it equal to this.children. But this seems awkward and boilerplate.
Is there a way for my RadialSelector class, which extends the PolymerElement class, to make its superclass 'children' field observable? Currently when I just write #observable children, Dart (understandably) thinks I'm just creating a new 'children' variable.
Thank you and let me know if there is something I could make clearer! :)
Here is the Dart Code:
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag('radial-selector')
class RadialSelector extends PolymerElement {
#published String img;
#observable children;
RadialSelector.created() : super.created() {
print("CONSTRUCTING RADIAL SELECTOR");
print(children);
}
filterByType(Type t, List<Element> elems) {
print("FILTER BY TYPE CALLED");
List<Element> newElems = new List<Element>();
for (var e in elems) {
if (e.runtimeType == t)
newElems.add(e);
}
return newElems;
}
}
Here is the corresponding html:
<link rel="import" href="packages/polymer_ui_elements/polymer_ui_icon_button/polymer_ui_icon_button.html">
<polymer-element name="radial-selector">
<template>
<style>
:host {
display: block;
margin: 100px
}
</style>
<script>console.log("HEY");</script>
<div id="stem">
<img src = "{{img}}" alt ="Radial stem"/>
<template repeat = "{{child in children | filterByType(ImageElement)}}">
<polymer-ui-icon-button src = {{child.src}}></polymer-ui-icon-button>
</template>
</div>
</template>
<script type="application/dart" src="radialselector.dart"></script>
</polymer-element>
There is no way you can do this for children.
You could make an #observable getter/setter that redirects to the super class' children
#observable
NodeList get children => super.children;
set children(NodeList val) => super.children = val; // won't work
This has two flaws.
the children list is final (haven't tried but I'm sure you can't assign a new list.
it doesn't help you at all to have an #observable children.
You would only be notified when another list gets assigned (which is not possible because the field is final)
What you need is an observable list to get notified about list element changes (add/remove/replace).
For this you would need to replace children by toObservable(children) which is not possible because you can't replace the children collection.
What you can do is to use a MutationObserver. see Angular Dart - execute a function right after ng-repeat has finished to get notified about child node changes.
You can add a new field
#observable List myChildren;
and use the mutation observer to update this field whenever the children change.
void mutationCallback(...) {
...
myChildren = children.toList();
}
This way each time your children change myChildren get's an entire new list assigned and the view gets notified about this change due to the #observable annotation.
You don't need to make the list observable because there are no single add/remove/replace operations to observe.
Related
I have been looking for information about variable binning. The problem is that I don't want to use those variables inside my HTML render, but in the component logic (i want to fetch some data). How do I declare it?
constructor(){
super();
this.url = "https://....";
}
render() {
return html`
<parent-component .url ="${this.url}"></parent-component>
`;
child component:
constructor() {
super();
fetch(this.url)
.then(response => response.json())
.then(data =>{this.ArrayData = data.results
this.requestUpdate()
})
}
Thank you very much
To bind data, declare a reactive property for properties that can trigger the reactive update cycle when changed, re-rendering the component.
From your example I'm not exactly sure when you want to trigger the fetch in the child component. I'll assume you want to fetch the data once with this.url passed from the parent as a property binding.
The issue I see in the code sample is that this.url will be undefined in the constructor of the child component, as the outer element needs to render and set the property .url on the child. Moving the logic in the constructor to the firstUpdated lifecycle callback should fix the issue, as this.url will now be defined.
An additional change I included in the code sample, is making this.ArrayData a reactive property, allowing the removal of this.requestUpdate.
Minimal example of fix in the Lit Playground.
In a cshtml page I'm able to select a class in function of a database value. Example:
<div class="alert #item.State" role="alert">#item.State</div>
"State" is an enum of my table defined as follow:
public enum States
{
Ready,
Setup,
Pause,
Error,
Maintenance,
Emergency,
Disconnected
}
the Razor syntax above will add a class to my alert in function of the item value. Good!
But now I need to "translate" this class to Bootstrap's one. Example:
Ready: alert-primary
Pause or Disconnected: alert-secondary
Error: alert-danger
etc...
I'm able to this manually with Razor, but I wonder if there's a convenient way to this directly in css, something like (metacode):
.Ready {
return alert-primary
}
.Pause, .Disconnect {
return alert-secondary
}
You can declare an array of strings which will contain the class names:
string[] ClassList = new string[]
{
"ReadyClass", "SetupClass", "PauseClass", "ErrorClass", "MaintenanceClass", "EmergencyClass", "DisconnectedClass"
};
Now you can display the equivalent class name using the following line
#ClassList[(int)item.State]
I did not test it but it should work.
I need some clarification on binding between service and component properties and data binding in angular2
assume i have a service(singleton) and a component
export class Service {
name = "Luke";
object = {id:1};
getName(){return this.name};
getObject(){return this.object};
}
export class Component implements OnInit{
name:string;
object:any;
constructor(private _service:Service){}
ngOnInit():any{
//Is this 2 way binding?
this.name = this._service.name;
this.object = this._service.object;
//Is this copying?
this.name = this._service.getName();
this.object = this._service.getObject();
}
}
If you update elements by reference (if you update something into the object property), you will see the updates in the view:
export class Service {
(...)
updateObject() {
this.object.id = 2;
}
}
If you update elements by value (if you update something into the name property), you won't see the updates in the view:
export class Service {
(...)
updateName() {
this.name = 'Luke1';
}
}
See this plunkr: https://plnkr.co/edit/w7bS0fAVjOc3utnpD39b?p=preview.
Angular binding only works for bindings declared in the view (HTML).
If you want properties in your component being updated when values in a service change, you need to take care of it yourself.
Observables make this easy. See detect change of nested property for component input for an example.
If you want properties in a component updates as soon as a value in change in a service changes:
Import DoCheck from #angular/core and your service into the
component.
Call the service functions affecting the component property in ngDoCheck(){...}
The component view will be updated automatically as soon as any changes
Something like this in your component:
ngDoCheck() {
this.qty = this.cartService.getTotalQtyInCart();
}
I have GWT CellList and after adding items via a DataProvider I use the following code to add styling to each item.
members... we can styling if a matched item is also in members
matched... passed in as a MetaJsArray<Contact>
CellList<Contact> list = getView().getResults();
for (int i=0; i<matched.length(); i++) {
if (members.isExistingEntry(matched.get(i))) {
list.getRowElement(i).addClassName("RED");
}
}
This code works until... I click items in the list.
onCellPreview() is called for each item clicked, but the previously clicked item loses its "RED" styling.
Do I need to add styling differently? Or how do I stop the loss of "RED"?
My guess its something to do the way GWT generates the javascript. When you manually set the cell on load its all good. When you select it, the javascript changes the object to use the selected CSS and when you un select it, the CSS changes to the default GWT CSS style for the cell.
Only way I can think of is to have a handler on select. When you select an item:
selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
public void onSelectionChange(SelectionChangeEvent event) {
// get item last selected
// check if needs re styling
// restyle
// do things with the new selected object
}
});
Add another check through the cell list and mark the ones that got unmarked.
This way might be inefficient, but its one way of avoiding your problem that I can think of. hope it helps.
After trying various approaches the only want that works, without hacks, is to define the style at the point of rendering.
With my own ContactCell extending AbstractCell the render() function can pass in a styling value into the contactcell.ui.xml file.
#Override
public void render(Context context, Contact value, SafeHtmlBuilder sb) {
if (value == null) {
return;
}
String styling = value.getStyling();
uiRenderer.render(sb, styling);
}
and then in contactcell.ui.xml file
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'>
<ui:with field='styling' type='java.lang.String'/>
<div class="{styling}"> ... </div>
GWT will mangle the style name so define your own CssResource class to access the class name thru so that the class name is mangled throughout the app.
I'm using a tree control that I want to customize. The data items in the tree's dataProvider have a property name that should be used for labeling the node, and a property type that should be used to select one of several embedded images for use as an icon. The simplest way to do this is by using the labelField and iconFunction properties.
However, I wanted to get started with item renderers and open the door for adding more complex customization later, so I tried making my own item renderer. I extended the TreeItemRenderer class as follows and used it in my tree control:
class DirectoryItemRenderer extends TreeItemRenderer
{
[Embed("assets/directory/DefaultIcon.png")]
private static var _DEFAULT_ICON:Class;
// ... some more icons ...
override public function set data(value:Object):void
{
super.data = value; // let the base class take care of everything I didn't think of
if (value is Node) { // only handle the data if it's our own node class
switch ((value as Node).type) {
// ... some case clauses ...
default:
this._vSetIcon(_DEFAULT_ICON);
}
this.label.text = (value as Node).name;
}
}
private function _vSetIcon(icon:Class):void
{
if (null != this.icon && this.contains(this.icon)) {
this.removeChild(this.icon);
}
this.icon = new icon();
this.addChild(this.icon);
this.invalidateDisplayList();
}
}
This code has no effect whatsoever, icon and label in the tree control remain at their defaults. Using trace(), I verified that my code is actually executed. What did I do wrong?
Looking at the base mx.controls.treeClasses.TreeItemRenderer class, I see that in the updateDisplayList function the renderer gets it's icon and disclosureIcon classes from _listData:TeeListData. Instead of overriding the updateDisplayList function, try modifying the icon and disclosureIcon classes of the renderer's private _listData instance in your _vSetIcon method using the public accessors, like so:
private function _vSetIcon(icon:Class, disclosureIcon:Class = null):void
{
var tmpListData:TreeListData;
if (disclosureIcon == null) disclosureIcon = icon;
tmpListData = this.listData;
tmpListData.icon = icon;
tmpListData.disclosureIcon = disclosureIcon;
this.listData = tmpListData;
}
EDIT
Here is some clarification on the difference between data and listData. You'll have to excuse my omission of package names but I'm editing from my phone so its tough to look them up and I don't know the package names off the top of my head. data is defined in the context of a TreeItemRenderer in the IDataRenderer interface. You create a data renderer by implementing this interface and defining a public property data, which in this case is set by the parent control and contains some data and meta-data from the dataProvider to be rendered by the data renderer class.
listData is defined in the IDropInListItemRenderer interface as a property of type BaseListData and is realized in the TreeItemRenderer class as a property TreeListData. It differs from the data property in that it contains meta-data that describes the TreeListRenderer itself (icon, indent, open) as well as (I believe, I'll have to double check this later) a reference to the data item being rendered. I gather that It's used by the the TreeItemRenderer and I would imagine the parent list control for display update and sizing purposes. Someone is free to correct or add onto that if I'm incorrect or missed something, I'm going of what I remember drom the code.
In this case, you wanted to use meta-data from the data set from the data provider to modify data that determines the display of the renderer, so you would need to modify both.
I think the real confusion here however came from the fact that you extended the TreeItemRenderer class then tried to override functionality on the component in a manner the original developer didn't intend for someone to do, hence the unexpected results. If your goal is education and not ease of implementation you would probably be better served by extending the UIComponent class and using the TreeItemRenderer code as a reference to create a class that implements the same interfaces. That would be a real dive into the pool of custom component development.
I'd probably try something simple, as in this example from the Adobe Cookbooks. I notice that they override updateDisplayList, which may have something to do with your problems.
There's another example (for Flex 2, but looks applicable to Flex 3) that shows how to manage the default icons. It looks like you'll want to manage the icon yourself, setting the default icon styles to null, instead of trying to manipulate the superclass's icon property.
Update -- Looking at the source for TreeItemRenderer, commitProperties has the following before checking the data and setting up the icon and label:
if (icon)
{
removeChild(DisplayObject(icon));
icon = null;
}
Also, it looks like the setter for data calls invalidateProperties. Hence, your icon is wiped out when the framework gets around to calling commitProperties.