removing polymer elements in dart - dart

i am adding polymer elements. i want to remove the element(self) on click on its(own) image. as per encapsulation i will have to make the parent delete the child. but this requires requires generating polymer elements for the parent too( am i right here??). children.add not defined for polymer elements.how do i do this, i plan to do some checks before adding the polymer element to the form .
Already read:
How to remove a child component with a delete button in the child itself
How do I fire a custom event from Polymer Dart?
How do you dispatch and listen for custom events in Polymer?
what do i pass through dispatch event??
init-item.html
<polymer-element name="init-item" >
<template>
<input type="image" src="button_minus_red.gif" on-click="{{remove}}" width = "15" height ="15">
{{Name}}<br>
</template>
<script type="application/dart" src="init-item.dart"></script>
</polymer-element>
init-item.dart
#HtmlImport('init-item.html')
import 'dart:html';
import 'dart:async';
import 'package:polymer/polymer.dart';
#CustomTag('init-item')
class PItem extends PolymerElement{
#observable String Name='hello';
void remove(){
Name='';
}
so presently on clicking the image the name attribute gets reset. but this happens only on a subsequent click event generated from main.dart. This is due to data binding. how do i initiate two way data binding.??
main.html (polymer tags not added inspite of suggestion. couldnt figure it out)
...
<form id="youritems">
</form>
...
Polymer items added to above tag.
main.dart
...
main() async{
.. initPolymer().then((_) {});
}
..
...{..
var input = new InputElement(type:"image");
input.onClick.listen((p){
p.preventDefault();
populateYourPlayers(teams[i]);
});
}
void populateItems(String name){
Polymer.onReady.then((_) {
var pl = new Element.tag('player-item');
pl.playerName = name;
yourPlayer.children.add(pl);
});
}
..

If you rename the click handler from remove to for example removeItem then you can just call remove() within the handler and it will work.
<input type="image" src="button_minus_red.gif" on-click="{{removeItem}}" width = "15" height ="15">
void removeItem(){
Name='';
remove(); // removes the init-item (self)
}
remove is an existing method on all elements. If you name your own method remove you override the default remove method and the original can't be called anymore (except from within your element with super.remove();)

Related

Vaadin Designer Tabs not correctly adding children "tab" to tabs object

I'm using Vaadin Deisgner 14.6.1 to create some super simple tabs. However, when I try to do some simple operations in the java class (eg selecting a tab), it throws an error which indicates that the "Tabs" object does not have the proper children "tab" components. Here's a simple test case below. (I discovered the issue when I was trying to add a addSelectedChangeListener() to the tabs class and discovered that it would never fire, presumably since the "tabs" class never properly had any children.) I tried a bunch of hacks, but nothing worked. (I have in the past gotten tabs to work if I stuck purely to a programmatic approach, but I really really really like using Designer, since it saves me tonnes of times and keeps the code quite modular and clean....when it works....)
import {html, PolymerElement} from '#polymer/polymer/polymer-element.js';
import '#vaadin/vaadin-ordered-layout/src/vaadin-vertical-layout.js';
import '#vaadin/vaadin-tabs/src/vaadin-tabs.js';
import '#vaadin/vaadin-tabs/src/vaadin-tab.js';
class MyTabtest extends PolymerElement {
static get template() {
return html`
<style include="shared-styles">
:host {
display: block;
height: 100%;
}
</style>
<vaadin-vertical-layout theme="spacing" style="width: 100%; height: 100%;">
<vaadin-tabs theme="equal-width-tabs" id="tabs" orientation="horizontal" selected="0">
<vaadin-tab id="tab1" selected>
Tab one
</vaadin-tab>
<vaadin-tab id="tab2">
Tab two with a longer title
</vaadin-tab>
<vaadin-tab id="tab3">
Tab three
</vaadin-tab>
</vaadin-tabs>
<label id="lbl1">page1</label>
<label id="lbl2">page2</label>
<label id="lbl3">page3</label>
</vaadin-vertical-layout>
`;
}
static get is() {
return 'my-tabtest';
}
static get properties() {
return {
// Declare your properties here.
};
}
}
customElements.define(MyTabtest.is, MyTabtest);
and
package com.deepsearch.fe.tab2vizdb.fpsgraphicaldetails.spectratab.hslspectrachartandalts;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.polymertemplate.Id;
import com.vaadin.flow.component.tabs.Tab;
import com.vaadin.flow.component.tabs.Tabs;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.templatemodel.TemplateModel;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.polymertemplate.PolymerTemplate;
/**
* A Designer generated component for the my-tabtest template.
*
* Designer will add and remove fields with #Id mappings but
* does not overwrite or otherwise change this file.
*/
#Route("tabtest")
#Tag("my-tabtest")
#JsModule("./src/my-tabtest.js")
public class MyTabtest extends PolymerTemplate<MyTabtest.MyTabtestModel> {
#Id("tabs")
private Tabs tabs;
#Id("tab1")
private Tab tab1;
#Id("tab2")
private Tab tab2;
#Id("tab3")
private Tab tab3;
#Id("lbl1")
private Label lbl1;
#Id("lbl2")
private Label lbl2;
#Id("lbl3")
private Label lbl3;
/**
* Creates a new MyTabtest.
*/
public MyTabtest() {
// tabs.setSelectedTab(tab2); //throws error!
tabs.addSelectedChangeListener(e -> {
System.out.println("A tab got selected!"); //this never fires!!!!
});
}
/**
* This model binds properties between MyTabtest and my-tabtest
*/
public interface MyTabtestModel extends TemplateModel {
// Add setters and getters for template properties here.
}
}
Ultimately, I'm trying to capture a tab select event -- but it doens't seem to work when the tabs are created in Designer....is this true on Vaadin's side too? (ie is this reproducible?)
This is an unfortunate limitation of the component mapping to elements defined in a template. When mapping to Java, the parent-child relationships are not preserved and thus the tabs component does not realize that the tab is one of its child components.
See https://github.com/vaadin/flow/issues/7622
The way to make it work would be to create the Tabs and Tab instances in Java and the rest in Designer.

open NgbTypeahead dropdown when click event on button

I need to simulate focus event on input to open NgbTypeahead dropdown , is it possible? Because i cant do it like:
.ts:
#ViewChild('inputExecutor') inputExecutor: ElementRef
focus$ = new Subject<string>();
click$ = new Subject<string>();
focused(){
this.executorForm.controls['executor'].setValue('');
this.focus$.next(this.executorForm.value.executor);
}
inputFocus(){
this.inputExecutor.nativeElement.focus();
}
html:
<input
class="choose-executor-input"
type="text"
name="executor"
formControlName="executor"
[ngbTypeahead]="searchExecutors"
(focus)="focus$.next($event.target.value); focused()"
(click)="click$.next($event.target.value);"
(selectItem)="itemSelected($event)"
[resultFormatter]="formatExecutors"
[inputFormatter]="formatExecutors"
#instance="ngbTypeahead"
#inputExecutor/>
<button
click)="inputFocus()"
class="btn btn-executor-open"></button>
</button>
So how i can focus on input to open dropdown? Any issues?
To accomplish this, you can fire an input event on the input element the NgbTypeahead is bound to, then call the focus method so that the input has focus and you can start typing immediately.
I started with the Open on focus demo from the ng-bootstrap website, and made the following changes:
Declare a new template variable elem so the DOM element is accessible inside the component TS file (note you can't use the existing instance as that is an NgbTypeahead not an HTML element):
Component HTML:
<input
id="typeahead-focus"
...
#instance="ngbTypeahead"
#elem
/>
Component TS:
#ViewChild('elem') elem: ElementRef;
Add a button to the template which will call a focus function - this is the button that when clicked will open the typeahead and focus on it:
<button (click)="openTypeahead()">Open Typeahead</button>
Add an openTypeahead function in the component TS file as follows:
public openTypeahead(): void {
// Dispatch event on input element that NgbTypeahead is bound to
this.elem.nativeElement.dispatchEvent(new Event('input'));
// Ensure input has focus so the user can start typing
this.elem.nativeElement.focus();
}
Please see this Stackblitz for a demo
I use
HTML:
<input #instance="ngbTypeahead" .../>
<button (click)="openTypeahead(instance)">Open Typeahead</button>
TS:
openTypeahead(inp) {
inp._elementRef.nativeElement.value = '';
inp._elementRef.nativeElement.dispatchEvent(new Event('input'));
inp._elementRef.nativeElement.focus();
}

Use DartAngular with dart:html

Is it possible to use default dart library html with angular dart?
ie:
class Test1Component implements OnInit{
#override
void ngOnInit() {
ButtonElement button = querySelector('button');
//Broken code, avoid button to be null.
button.onClick.listen(onClick);
}
void onClick(Event e){
print('Button clicked');
}
}
How can I avoid to get a 'null' button without the using any timers?
Basically I'm using only angular just for the Routes and but I'd like to stick with dart:html to control the DOM and events.
Yes, you can do that, but it's usually not a good idea.
Use instead #ViewChild(...) or similar Angular methods to get references to elements in a components view.
<button #myButton>click me</button>
#ViewChildren('myButton')
set myButton(List<Element> value) {
if(value.isNotEmpty) {
print(value.first);
}
}
If you want to just add a click handler using
<button (click)="onClick">click me</button>
would be the better way but it sounds you are somehow adding the button dynamically and adding a click handler declaratively might not work in this case (would need more info)
EDIT:
If someone like me want to use dart:html instead angular ng code, it's possible to use it
import 'package:angular/angular.dart';
import 'dart:html';
// AngularDart info: https://webdev.dartlang.org/angular
// Components info: https://webdev.dartlang.org/components
#Component(
selector: 'container',
template: '<h1>Test 1</h1><button #test1>Bottone test 1</button>',
)
class Test1Component implements OnInit{
#ViewChild('test1')
ButtonElement button;
#override
void ngOnInit() {
//Verified that button is initialized
print(button);
//Initialize click
button.onClick.listen((e) => print("Clicked"));
}
}

How to use jQuery UI with React JS

How can I use jQuery UI with React? I have seen a couple examples by Googling, but all of them seem to be outdated.
If you really need to do that, here is an approach I am using.
The plan: Create a component to manage the jQuery plugin. This component will provide a React-centric view of the jQuery component. Moreover, it will:
Use React lifecycle methods to initialize and tear down the jQuery plugin;
Use React props as plugin configuration options and hook up to plugin's methods events;
Destroy the plugin when component unmounts.
Let's explore a practical example how to do that with the jQuery UI Sortable plugin.
TLDR: The Final Version
If you just want to grab the final version of the wrapped jQuery UI Sortable example:
here is a GIST I made with full annotated comments;
and here's a jsfiddle DEMO, full annotated comments too;
... plus, below is the shortened from the longer comments code snippet:
class Sortable extends React.Component {
componentDidMount() {
this.$node = $(this.refs.sortable);
this.$node.sortable({
opacity: this.props.opacity,
change: (event, ui) => this.props.onChange(event, ui)
});
}
shouldComponentUpdate() { return false; }
componentWillReceiveProps(nextProps) {
if (nextProps.enable !== this.props.enable)
this.$node.sortable(nextProps.enable ? 'enable' : 'disable');
}
renderItems() {
return this.props.data.map( (item, i) =>
<li key={i} className="ui-state-default">
<span className="ui-icon ui-icon-arrowthick-2-n-s"></span>
{ item }
</li>
);
}
render() {
return (
<ul ref="sortable">
{ this.renderItems() }
</ul>
);
}
componentWillUnmount() {
this.$node.sortable('destroy');
}
};
Optionally, you can set default props (in the case of none are passed) and the prop types:
Sortable.defaultProps = {
opacity: 1,
enable: true
};
Sortable.propTypes = {
opacity: React.PropTypes.number,
enable: React.PropTypes.bool,
onChange: React.PropTypes.func.isRequired
};
... and here's how to use the <Sortable /> component:
class MyComponent extends React.Component {
constructor(props) {
super(props);
// Use this flag to disable/enable the <Sortable />
this.state = { isEnabled: true };
this.toggleEnableability = this.toggleEnableability.bind(this);
}
toggleEnableability() {
this.setState({ isEnabled: ! this.state.isEnabled });
}
handleOnChange(event, ui) {
console.log('DOM changed!', event, ui);
}
render() {
const list = ['ReactJS', 'JSX', 'JavaScript', 'jQuery', 'jQuery UI'];
return (
<div>
<button type="button"
onClick={this.toggleEnableability}>
Toggle enable/disable
</button>
<Sortable
opacity={0.8}
data={list}
enable={this.state.isEnabled}
onChange={this.handleOnChange} />
</div>
);
}
}
ReactDOM.render(<MyComponent />, document.getElementById('app'));
The Full Explanation
For those of you, who want to understand why and how. Here's a step by step guide:
Step 1: Create a component.
Our component will accept an array (list) of items (strings) as data prop.
class Sortable extends React.Component {
componentDidMount() {
// Every React component has a function that exposes the
// underlying DOM node that it is wrapping. We can use that
// DOM node, pass it to jQuery and initialize the plugin.
// You'll find that many jQuery plugins follow this same pattern
// and you'll be able to pass the component DOM node to jQuery
// and call the plugin function.
// Get the DOM node and store the jQuery element reference
this.$node = $(this.refs.sortable);
// Initialize the jQuery UI functionality you need
// in this case, the Sortable: https://jqueryui.com/sortable/
this.$node.sortable();
}
// jQuery UI sortable expects a <ul> list with <li>s.
renderItems() {
return this.props.data.map( (item, i) =>
<li key={i} className="ui-state-default">
<span className="ui-icon ui-icon-arrowthick-2-n-s"></span>
{ item }
</li>
);
}
render() {
return (
<ul ref="sortable">
{ this.renderItems() }
</ul>
);
}
};
Step 2: Pass configuration options via props
Let's say we want to configure the opacity of the helper while sorting. We'll use the opacity option in the plugin configuration, that takes values from 0.01 to 1.
class Sortable extends React.Component {
// ... omitted for brevity
componentDidMount() {
this.$node = $(this.refs.sortable);
this.$node.sortable({
// Get the incoming `opacity` prop and use it in the plugin configuration
opacity: this.props.opacity,
});
}
// ... omitted for brevity
};
// Optional: set the default props, in case none are passed
Sortable.defaultProps = {
opacity: 1
};
And here's how we can use the component in our code now:
<Sortable opacity={0.8} />
The same way, we can map any of the jQUery UI Sortable options.
Step 3: Hook-up functions on plugin events.
You will most probably need to hook-up on some of the plugin methods, in order to perform some React logic, for example, manipulate the state let's day.
Here's how to do that:
class Sortable extends React.Component {
// ... omitted for brevity
componentDidMount() {
this.$node = $(this.refs.sortable);
this.$node.sortable({
opacity: this.props.opacity,
// Get the incoming onChange function
// and invoke it on the Sortable `change` event
change: (event, ui) => this.props.onChange(event, ui)
});
}
// ... omitted for brevity
};
// Optional: set the prop types
Sortable.propTypes = {
onChange: React.PropTypes.func.isRequired
};
And here's how to use it:
<Sortable
opacity={0.8}
onChange={ (event, ui) => console.log('DOM changed!', event, ui) } />
Step 4: Pass the future updates control to jQuery
Right after ReactJS adds the element in the actual DOM, we need to pass the future control to jQuery. Otherwise, ReactJS will never re-render our component, but we don't want that. We want jQuery to be responsible for all updates.
React lifecycle methods comes to the rescue!
Use shouldComponentUpdate() to let React know if a component's output is not affected by the current change in state or props. The default behavior is to re-render on every state change, and in the vast majority, but we don't want this behavior!
shouldComponentUpdate() is invoked before rendering when new props or state are being received. If shouldComponentUpdate() returns false, then componentWillUpdate(), render(), and componentDidUpdate() will not be invoked.
Then, we use componentWillReceiveProps(), we compare this.props with nextProps and call jQuery UI sortable updates only when necessary. For this example, we will implement the enable/disable option of the jQuery UI Sortable.
class Sortable extends React.Component {
// Force a single-render of the component,
// by returning false from shouldComponentUpdate ReactJS lifecycle hook.
// Right after ReactJS adds the element in the actual DOM,
// we need to pass the future control to jQuery.
// This way, ReactJS will never re-render our component,
// and jQuery will be responsible for all updates.
shouldComponentUpdate() {
return false;
}
componentWillReceiveProps(nextProps) {
// Each time when component receives new props,
// we should trigger refresh or perform anything else we need.
// For this example, we'll update only the enable/disable option,
// as soon as we receive a different value for this.props.enable
if (nextProps.enable !== this.props.enable) {
this.$node.sortable(nextProps.enable ? 'enable' : 'disable');
}
}
// ... omitted for brevity
};
// Optional: set the default props, in case none are passed
Sortable.defaultProps = {
enable: true
};
// Optional: set the prop types
Sortable.propTypes = {
enable: React.PropTypes.bool
};
Step 5: Clean up the mess.
Many jQuery plugins provide a mechanism for cleaning up after themselves when they are no longer needed. jQuery UI Sortable provides an event that we can trigger to tell the plugin to unbind its DOM events and destroy. React lifecycle methods comes to the rescue again and provides a mechanism to hook into when the component is being unmounted.
class Sortable extends React.Component {
// ... omitted for brevity
componentWillUnmount() {
// Clean up the mess when the component unmounts
this.$node.sortable('destroy');
}
// ... omitted for brevity
};
Conclusion
Wrapping jQuery plugins with React is not always the best choice. However, it is nice to know that it is an option and how you can implement a solution. It is a viable option if you are migrating a legacy jQuery application to React or maybe you just can't find a React plugin that suits your needs in your case.
In the case that a library modifies the DOM, we try to keep React out of its way. React works best when it has full control of the DOM. In these cases, React components are more of wrappers for the 3rd party libraries. Mostly by using the componentDidMount/componentWillUnmount to initialize/destroy the third party library. And props as a way of giving the parent a way of customizing the behavior of the third party library that the child wraps and to hook-up on plugin events.
You can use this approach to integrate almost any jQuery plugin!
React doesn't play well with libraries that do direct DOM mutations. If something else mutates the DOM where React is attempting to render, it will throw errors. If you had to make this work, your best compromise is to have different parts of your page which are managed by different things, for example a div which houses your jquery component(s), and then some other div which contains your React component(s). Communicating between these disparate (jquery and react) components will be difficult however and honestly it's probably better to just choose one or the other.
While technically faultless, Kayolan's answer has a fatal flaw, IMHO: in passing the responsibility for future UI updates from React to jQuery, he's rather negated the point of React being there in the first place! React controls the initial render of the sortable list, but after that React's state data will become outdated as soon as the user does the first jQueryUI drag/sort operations. And the whole point of React is to represent your state data at the view level.
So, I took the reverse approach when I approached this problem: I tried to ensure that React was in control as much as possible. I don't let the jQueryUI Sortable control change the DOM at all.
How's that possible? Well jQuery-ui's sortable() method has a cancel call that sets the UI back to how it was before you started dragging and dropping stuff around. The trick is to read the state of the sortable control before you issue that cancel call. That way, we can pick up what the user's intentions were, before the cancel call sets the DOM back the way it was. Once we have those intentions, we can pass them back to React, and manipulate the state data to be in the new order that the user wanted. Finally, call a setState() on that data to have React render the new order.
Here's how I do that:
Attach the jquery-ui.sortable() method to a list of line items (generated by React of course!)
Let the user drag and drop those line items around the DOM.
When the user starts dragging, we read the index of the line item that user's dragging from.
When the user drops the line item, we:
Read from jQuery-ui.sortable() the new index position for the line item, i.e. where in the list user dropped it.
Pass a cancel call to jQuery-ui.sortable() so that the list goes backs to its original position, and the DOM is unchanged.
Pass the old and new indexes of the dragged line item as parameters to a JavaScript function in a React module.
Have that function reorder the list's back-end state data to be in the new order that the user dragged and dropped it into.
Make a React setState() call.
The list in the UI will now reflect the new order of our state data; this is standard React functionality.
So, we get to use jQueryUI Sortable's drag and drop functionality, but without it changing the DOM at all. React's happy, because it's in control of the DOM (where it should be).
Github repository example at https://github.com/brownieboy/react-dragdrop-test-simple. This includes a link to a live demo.
I could not get the jquery-ui npm package to work. What has worked for me is to use jquery-ui-bundle:
import $ from 'jquery';
import 'jquery-ui-bundle';
import 'jquery-ui-bundle/jquery-ui.min.css';
Concerning to Kaloyan Kosev's long answer, i must create a component for every jQueryUi feature that i want to use? No thanks! Why not simply update your state when you change the DOM? Followig works for me:
export default class Editor extends React.Component {
// ... constructor etc.
componentDidMount() {
this.initializeSortable();
}
initializeSortable() {
const that = this;
$('ul.sortable').sortable({
stop: function (event, ui) {
const usedListItem = ui.item;
const list = usedListItem.parent().children();
const orderedIds = [];
$.each(list, function () {
orderedIds.push($(this).attr('id'));
})
that.orderSortableListsInState(orderedIds);
}
});
}
orderSortableListsInState(orderedIds) {
// ... here you can sort the state of any list in your state tree
const orderedDetachedAttributes = this.orderListByIds(orderedIds, this.state.detachedAttributes);
if (orderedDetachedAttributes.length) {
this.state.detachedAttributes = orderedDetachedAttributes;
}
this.setState(this.state);
}
orderListByIds(ids, list) {
let orderedList = [];
for (let i = 0; i < ids.length; i++) {
let item = this.getItemById(ids[i], list);
if (typeof item === 'undefined') {
continue;
}
orderedList.push(item);
}
return orderedList;
}
getItemById(id, items) {
return items.find(item => (item.id === id));
}
// ... render etc.
}
The list element just needs an additional attribute for let jQuery select the element.
import React from 'react';
export default class Attributes extends React.Component {
render() {
const attributes = this.props.attributes.map((attribute, i) => {
return (<li key={attribute.id} id={attribute.id}>{attribute.name}</li>);
});
return (
<ul className="sortable">
{attributes}
</ul>
);
}
}
For ids i use UUID's so i havent conflicts when matching them in orderSortableListsInState().
You can use either useRef or the component id or class as usual...
import { useRef, useEffect } from 'react';
import $ from 'jquery';
import "jquery-ui-dist/jquery-ui"
export default function YourComponent() {
const ref = useRef()
useEffect(() => {
$(ref.current).sortable({
items: '>li',
});
}, []);
return (
<ul ref={ref}>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
)
}

Trigger a click on a HTML element (in dart)

I'm trying to trigger a click on html element (in dart).
In other words, how can I execute the function that is normally executed when the element is clicked ?
Here is an example:
import 'dart:html';
import 'dart:math';
main() {
querySelector("#first").onClick.listen((e) {
r() => new Random().nextInt(256);
querySelector("#first").style.color =
"rgb(${r()},${r()},${r()})";
});
querySelector("#second").onClick.listen((e) {
// pretend the first paragraph is clicked
});
}
see also: https://dartpad.dartlang.org/456f1ec945536caf310c
This should work to produce a mouse event
querySelector("#second").dispatchEvent(new MouseEvent(...))

Resources