All:
I wonder if it is possible that binding multiple event handlers to same event?
For example:
var LikeToggleButton = React.createClass({
render: function(){
(function toggle(){
this.setState({liked:!like});
}).bind(this);
return (
<div onClick={toggle}>TOGGLE LIKE</div>
);
}
});
Until this point everything seems normal, but I want to add another feature to that button, which is decide by other option:
For example, I have another switch component(could be anything like checkbox or radio button etc.) called "count toggle", which when enabled, the LikeToggleButton's button will be added another onClick handler which is start counting times of button clicked, I know it could be predesignd into the toggle function, but I just wonder if there is a way to append this part to onClick handler?
Thanks
If you want to have multiple callbacks executed when onClick is triggered, you can have them passed from outside, so you'll have access to them in the props object. Then execute them all (note: code not tested):
var LikeToggleButton = React.createClass({
toggle: function() {
this.setState({liked:!like});
},
handleClick: function(e) {
e.preventDefault();
this.toggle();
for (var i=0, l<this.props.callbacks.length; i<l; i++) {
this.props.callbacks[i].call();
}
},
render: function() {
return (
<div onClick={this.handleClick}>TOGGLE LIKE</div>
);
}
});
BUT, if you want to have components connected between them, you should not do that by calling methods inside handlers. Instead you should use an architectural pattern, where Flux is the obvious choice (but there are lots more).
Take a look to Flux, and here you have more choices.
For an extensible way that does't require the component to know about components that use it - save the onClick event before changing it.
This is highlights extracted from the actual working code:
button.jsx
class Button extends React.Component {
constructor(props) {
super(props);
this.state= { callback: false};
}
click(){
//do stuff here
if(this.state.callback) { this.state.callback.call(); }
}
render () {
this.state.callback = this.props.onClick; // save the onClick of previous handler
return (
<button { ...this.props } type={ this.props.type || "button" } onClick={ this.click.bind(this) } className = this.props.className } >
{ this.props.children }
</button>
);
}
}
export default Button;
Then in another component you can use the button and it can have it's own onClick handler:
class ItemButtons extends React.Component {
itemClick () {
//do something here;
}
render () {
const buttons = [
(
<Button onClick={ this.itemClick.bind(this) } className="item-button">
<span>Item-Button</span>
</Button>
)
];
return (<section>{ buttons }</section>);
}
export default ItemButtons;
To group multiple actions on an event
onMouseDown={(e) => { e.stopPropagation(); alert('hello'); }}
Maybe you can set multiple click event handlers on the same one target as described here: https://gist.github.com/xgqfrms-GitHub/a36b56ac3c0b4a7fe948f2defccf95ea#gistcomment-2136607
Code (copied from linke above):
<div style={{ display: 'flex' }}>
<div style={{
width: '270px',
background: '#f0f0f0',
borderRight: "30px solid red",
minHeight: ' 500px',
maxHeight: '700px',
overflowX: 'hidden',
overflowY: 'scroll',
}}
onClick={this.state.ClickHandler}
onClick={this.stateHandleClick}
className="sidebar-btn"
>
<button onClick={this.props.ClickHandler}>props</button>
<button onClick={(e) => this.props.ClickHandler}>props</button>
<button onClick={this.props.ClickHandler}>props</button>
<button onClick={this.state.ClickHandler}>state</button>
//...
</div>
Related
When I attach the '#change' property to a form input, e.g.
<select v-model="data" #change="handler">
<option>1</option>
<option>2</option>
<option>3</option>
</select>
It is only triggered when a different option is selected. How do I trigger the handler function even when the same option is chosen again?
The event that's triggered on a <select> when selecting the already selected <option> is mouseup.
Therefore, you'll need an additional prop to store the select's state (isOpen). You'll call a helper method (let's name it mouseUp) and only call handler() when the select is closing.
For all the other input cases (keypress and whatnot), rely on #input.
I added another helper prop to avoid triggering handler() twice:
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
data: () => ({
data: 1,
isOpen: false,
changed: false
}),
methods: {
mouseUp(e) {
if (this.isOpen) {
this.$nextTick().then(() => {
if (!this.changed){
this.handler(e);
}
});
this.isOpen = false;
} else {
this.changed = false;
this.isOpen = true;
}
},
handler(e) {
this.changed = true;
console.log(`handler(${e.target.value})`);
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<select v-model="data" #mouseup="mouseUp" #input="handler">
<option>1</option>
<option>2</option>
<option>3</option>
</select>
</div>
The logic might seem cumbersome but, because #input is triggered before #mouseup, I'm only calling handler() from mouseUp() if #input has not been triggered and the <select> is closing.
The only case of <select> closing and handler() not being fired is pressing ESC key when select is open. But I'm not sure you should be calling it in that case (user wants to cancel any change). If you want to call it in that case as well, it's quite easy to listen to that specific case.
I am trying to reset my form. What am I doing wrong? What is best-practice?
Here is my Plunk demo.
My problem on the demo is that connectedCallback() appears to fire continually (not just on initial load), thereby losing the value of savedItem by updating it to newItem on each update.
Here is the same issue on Github.
https://plnkr.co/edit/wRdXXws2UXl3VXrycqua?p=preview
my-demo.html
<base href="https://polygit.org/polymer+v2.0.0/shadycss+webcomponents+1.0.0/components/">
<link rel="import" href="polymer/polymer-element.html">
<link rel="import" href="paper-toggle-button/paper-toggle-button.html">
<dom-module id="my-demo">
<template>
<style>
:host > * {
margin-top: 40px;
font-size: 18px;
}
button.save {
color: white;
background-color: blue;
}
</style>
<paper-toggle-button checked="{{item.alice}}">Alice</paper-toggle-button>
<paper-toggle-button checked="{{item.bob}}">Bob</paper-toggle-button>
<paper-toggle-button checked="{{item.charlie}}">Charlie</paper-toggle-button>
<paper-toggle-button checked="{{item.dave}}">Dave</paper-toggle-button>
<button>Reset</button>
<button class="save" on-tap="_reset">Save</button>
</template>
<script>
class MyDemo extends Polymer.Element {
static get is() {
return 'my-demo';
}
static get properties() {
return {
item: {
type: Object,
notify: true,
value: () => {
return {
alice: false,
bob: false,
charlie: false,
dave: true,
};
},
},
savedItem: {
type: Object,
notify: true,
},
};
}
connectedCallback() {
super.connectedCallback();
this.set('savedItem', this.item);
}
static get observers() {
return [
'_itemChanged(item.*)',
];
}
_itemChanged(newItem) {
console.log('saved-item', this.savedItem);
console.log('new-item', newItem);
}
_reset() {
this.set('item', this.savedItem);
}
}
window.customElements.define(MyDemo.is, MyDemo);
</script>
</dom-module>
Edit
Steps to recreate the problem
Open the demo here.
Open your console.
Navigate in the Plunker to my-demo.html
Click one of the toggle switches.
Notice in the console, the savedItem property updates to the current item property.
Notice, this appears to be the result of the following code block.
connectedCallback() {
super.connectedCallback();
this.set('savedItem', this.item);
}
But how can this be? Because I thought connectedCallback() only fired once at initialization time?
tldr; The connectedCallback() isn't actually being called more than once in this case. savedItem and item are always the same object in your code because JavaScript passes objects by reference.
Object references
In the following:
connectedCallback() {
this.set('savedItem', this.item);
}
_reset() {
this.set('item', this.savedItem);
}
savedItem and item are both references to the same object. Calling this.set() does not automatically clone the operand (nor does the = operator).
One solution is to clone the object before assignment (using ES2017 object-spread operator):
connectedCallback() {
this.savedItem = {...this.item};
}
_reset() {
this.item = {...this.savedItem};
}
updated plunker
Best practice (or simpler reset method)
A simpler way to reset the form is to let iron-form handle the form's reset event, where it resets the form's named inputs to their initial values. This saves you from having to declare savedItem and no extra JavaScript to manage it.
To accomplish this, wrap the <paper-toggle-button>'s in an <iron-form>, and add name attributes to them. Then, insert an <input type="reset"> in the form, which serves as the reset button.
<iron-form>
<form>
<paper-toggle-button name="alice" checked="{{item.alice}}">Alice</paper-toggle-button>
<paper-toggle-button name="bob" checked="{{item.bob}}">Bob</paper-toggle-button>
<paper-toggle-button name="charlie" checked="{{item.charlie}}">Charlie</paper-toggle-button>
<paper-toggle-button name="dave" checked="{{item.dave}}">Dave</paper-toggle-button>
<input type="reset" class="save">
</form>
</iron-form>
demo
I am making a file manager app based on react-redux, and I meet problem with input.
For example, my code:
PathForm.js:
export default class PathForm extends Component {
render() {
const { currentPath, handleSubmit } = this.props;
console.log('PathFormPathFormPathForm', this.props)
return (
<div className="path-box">
<form onSubmit={handleSubmit}>
<div>
<input type="text" className="current-path-input" placeholder="input path" value={currentPath} />
</div>
<button className="go-btn" type="submit">Go</button>
</form>
</div>
);
}
}
Explorer.js:
class Explorer extends Component {
goPath(e) {
e.preventDefault()
// fake function here, because I have to solve the input problem first
console.log('PathForm goPath:',this.props)
let {targetPath , actions} = this.props
swal(targetPath)
}
render() {
const { node, currentPath , actions} = this.props
console.log('Explorer.render:',this.props)
return (
<div className='explorer-container'>
<PathForm currentPath={currentPath} handleSubmit={this.goPath.bind(this)}/>
<FileListOperator />
<FileListView fileList={node && node.childNodes} actions ={actions} />
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
node: state.tree[state.tree.currentPath],
currentPath: state.tree.currentPath
};
}
function mapDispatchToProps(dispatch) {
console.log('mapDispatchToProps')
return {
actions: bindActionCreators(NodeActions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Explorer);
Feature I want:
I have a PathForm, it need show path from two way:
user click a file path from left tree view, Explorer get this path as currentPath, then pass to PathForm, and show currentPath in input
user directly type a path to the PathForm's input, PathForm call handleSubmit(Explorer's function) to change the currentPath
Additional:I want to keep PathForm as a stateless component
The problem:
I'd like use PathForm as a stateless form, so I don't want connect it to store, but I need it change input by currentPath. But if I set value={currentPath}, user can not type anything else.
change to <input type="text" onChange={this.changeValue} value={this.getValue()}/> allow user type string in this input, but can not use props currentPath passed by Explorer
The only way I can imagine is connect this form to store which I don't want. I'd like Explorer to dispatch all actions and pass props.
Tried with some package
I found the input not act as my thought, so I tried the two popular package:
redux-form
It create a form need so much code, and official doc not say how to render this form with parent props,
I try to pass props and handleSubmit to it, not work. After I see
React + Redux - What's the best way to handle CRUD in a form component?
and How to wire up redux-form bindings to the form's inputs
I found I can't do that, it define some function overwrite mine, this behave is not good for me(I have to change the handlerSubmit function name, but it still not work), and it connect to the store. So I turn to formsy-react
formsy-react
It still need so much code, though it provide some mixin, but I still have to write a custom text input with changeValue function myself(changeValue is no need in most situation when writing normal html jquery app).Then I found the problem that PathForm can not use props currentPath passed by Explorer...
Probably Worked solution(but I don't tend to use):
connect PathForm to store, add another state inputPathValue for this input. Use inputPathValue interact with currentPath
After above, I found use input/form is super in-convenient in react....
Does it mean I have to connect PathForm to stroe?
Any other way to solve my problem?
There are uncontrolled(not set value) and controlled(set value) input in reactjs.
controlled not allow user input, but uncontrolled does.
Solution:
Need use uncontrolled input(no value attribute).
Select input element and set the value when currentPath change.
Bad way:
code:
export default class PathForm extends Component {
changeCurrentPath(path) {
const pathInput = document.querySelector('.current-path-input')
if (pathInput){
pathInput.value = path
this.lastPath = path
}
}
render() {
const { currentPath, handleSubmit } = this.props;
console.log('PathFormPathFormPathForm', this.props)
this.changeCurrentPath(currentPath)
return (
<div className="path-box">
<form onSubmit={handleSubmit}>
<div>
<input type="text" className="current-path-input" placeholder="input path" />
</div>
<button className="go-btn" type="submit">Go</button>
</form>
</div>
);
}
}
Good way:
use componentWillReceiveProps to set props and rel to select element
1.use form submit
export default class PathForm extends Component {
constructor(props) {
super(props)
// can not find `this` if not bind
this.handleSubmit = this.handleSubmit.bind(this)
}
componentWillReceiveProps(nextProps) {
if (nextProps.currentPath !== this.props.currentPath) {
this.setInputValue(nextProps.currentPath)
}
}
getInputValue() {
return this.refs.pathInput.value
}
setInputValue(val) {
this.refs.pathInput.value = val
}
handleSubmit(e){
e.preventDefault()
this.props.handleSubmit(this.getInputValue())
}
render() {
return (
<div className="path-box">
<form onSubmit={this.handleSubmit}>
<input className="current-path-input"
defaultValue={this.props.currentPath}
ref="pathInput" />
<button className="waves-effect waves-light btn" type="submit">Go</button>
</form>
</div>
);
}
}
2.use button click
export default class PathForm extends Component {
constructor(props) {
super(props)
// can not find `this` if not bind
this.handleGoClick = this.handleGoClick.bind(this)
this.handleKeyUp = this.handleKeyUp.bind(this)
}
componentWillReceiveProps(nextProps) {
if (nextProps.currentPath !== this.props.currentPath) {
this.setInputValue(nextProps.currentPath)
}
}
getInputValue() {
return this.refs.pathInput.value
}
setInputValue(val) {
this.refs.pathInput.value = val
}
handleKeyUp(e) {
if (e.keyCode === 13) {
this.handleGoClick()
}
}
handleGoClick(e) {
e.preventDefault()
this.props.handleSubmit(this.getInputValue())
}
render() {
return (
<div className="path-box">
<form >
<input className="current-path-input"
defaultValue={this.props.currentPath}
onKeyUp={this.handleKeyUp}
ref="pathInput" />
<button className="waves-effect waves-light btn" type="submit" onClick={this.handleGoClick}>Go</button>
</form>
</div>
);
}
}
If you really don't want the state in Redux, you can instead store the state on the component with setState. Directly accessing the input is strongly discouraged. You should track the state of the input on the component. Add an onChange handler to the input, store the state and handle componentWillReceiveProps where you decide what to do with new incoming props.
I'm trying to wrap Semantic UI Modal component using portal approach described here
Here is my take at it http://jsfiddle.net/mike_123/2wvfjpy9/
I'm running into issue though, when obtaining a DOM reference and Rendering new markup into it there seem to be old reference still maintained.
render: function() {
return <div className="ui modal"/>; <-- the idea at first was to only return <div/>
},
...
React.render(<div > <----------- originally this element had className="ui modal", but this.node doesn't seem to overtake the original node reference
<i className="close icon"></i>
<div className="header">test</div>
<div className="content">
{props.children}
</div>
</div>, <-----------
this.node);
Any pointers how fix this test case http://jsfiddle.net/mike_123/2wvfjpy9/
You will lose correct vertical positioning and probably animations with approaches mentioned above.
Instead, you can just place your modal's component inside your app's root component and call .modal() with detachable: false. With this option, semantic wouldn't make any DOM manipulations and you won't lose your React DOM event listeners.
Example using Webpack/Babel:
import React, { Component } from 'react'
import $ from 'jquery'
if (typeof window !== 'undefined') {
window.jQuery = $
require('semantic-ui/dist/semantic.js')
}
class App extends Component {
state = {
showModal: false
}
_toggleModal = (e) => {
e.preventDefault()
this.toggleModalState()
}
toggleModalState = () => {
this.setState({ showModal: !this.state.showModal })
}
render() {
return (
<div>
<a href="" onClick={this._toggleModal}></a>
{this.state.showModal
? <Modal toggleModalState={this.toggleModalState}/>
: ''}
</div>
)
}
}
class Modal extends Component {
componentDidMount() {
$(this.modal)
.modal({ detachable: false })
.modal('show')
}
componentWillUnmount() {
$(this.modal).modal('hide')
}
_close = (e) {
e.preventDefault()
alert("Clicked")
this.props.toggleModalState()
}
render() {
return (
<div ref={(n) => this.modal = n} className="ui modal">
<div class="content">
<a onClick={this._close} href="">Click Me</a>
</div>
</div>
)
}
}
When you call this.$modal.modal('show'), it will actually restructure your DOM, and React will not be happy about it. Plus, if you try to put control in your modal, the control will not work.
What you should do is to React.render an already shown modal, i.e. a modal with markup as if $('.ui.modal').modal('show') has been called.
Here is my attempt using "React-Portal" to help with rendering a react component at body level. You can still use your method if you prefer.
// modal.jsx
import React, { Component } from 'react';
import Portal from 'react-portal';
class InnerModal extends Component {
constructor(props) {
super(props);
this.state = { modalHeight: 0 };
}
componentDidMount() {
let modalHeight = window.$('#reactInnerModal').outerHeight();
this.setState({modalHeight: modalHeight});
}
render() {
return (
<div id='reactInnerModal' className='ui standard test modal transition visible active' style={{'margin-top': - this.state.modalHeight / 2}}>
<i className='close icon' onClick={this.props.closePortal}></i>
{this.props.children}
</div>
);
}
}
class Modal extends Component {
render() {
let triggerButton = <button className='ui button'>Open Modal</button>;
return (
<Portal className='ui dimmer modals visible active page transition' openByClickOn={triggerButton} closeOnEsc={true} closeOnOutsideClick={true}>
<InnerModal>
{this.props.children}
</InnerModal>
</Portal>
);
}
}
export default Modal;
Notice that my modal has already been rendered in the markup.
You can then consume the modal as below:
// index.jsx
import React, { Component } from 'react';
import Modal from './modal';
class ModalDemo extends Component {
render() {
return (
<Modal>
<div className='header'>
Profile Picture
</div>
<div className='image content'>
<div className='ui medium image'>
<img src='http://semantic-ui.com/images/avatar2/large/rachel.png' />
</div>
<div className='description'>
<div className="ui header">We've auto-chosen a profile image for you.</div>
<p>We've grabbed the following image from the <a href='https://www.gravatar.com' target='_blank'>gravatar</a> image associated with your registered e-mail address.</p>
<p>Is it okay to use this photo?</p>
</div>
</div>
<div className='actions'>
<div className='ui black deny button'>
Nope
</div>
<div className='ui positive right labeled icon button'>
Yep, that's me
<i className='checkmark icon'></i>
</div>
</div>
</Modal>
);
}
}
React.render(<ModalDemo />, document.getElementById('content'));
With this you don't have to hack your way into DOM manipulation with jQuery, and the control in the modal (button, link, etc, to invoke functions) still works.
Hope this help!
Khanetor answered this question thoroughly and correctly, I just want to contribute one additional tidbit about how to position the Modal. It would be best as a comment, but unfortunately, I don't have the reputation to do so.
Anyways, the first child element of the Portal element needs to be positioned absolutely in order to make the dimmer and resulting modal sit on top of the page content rather than get put beneath it.
First, add style={position:'absolute'} to the Portal declaration in Modal's render method so the dimmer gets set at the top of the page. You end up with:
<Portal className='ui dimmer modals visible active page transition' openByClickOn={triggerButton} closeOnEsc={true} closeOnOutsideClick={true} style={position:'absolute'}>
<InnerModal>
{this.props.children}
</InnerModal>
</Portal>
Next, set the InnerModal's position to relative and decide on a distance from the top of the screen. I used an eighth (or 0.125) of the browser's viewport and got:
class InnerModal extends Component {
constructor(props){
super(props);
this.state = {
modalId : _.uniqueId('modal_'),
style: {}
}
}
componentDidMount() {
this.setState({
style : {
position : 'relative',
top : $(window).height() * 0.125 + 'px'
}
});
}
render(){
return (
<div id={this.state.modalId} className='ui standard modal transition visible active'
style={this.state.style}>
<i className='close icon' onClick={this.props.closePortal}></i>
{ this.props.children }
</div>
);
}
}
With those edits made, I've finally got some working modals in React! Hope this is helpful to someone else running into some of the same issues I've been.
I am trying to create a web-page where some elements (forms and buttons) become visible or are being hidden when some other elements (buttons) are clicked.
I try to find a way to manage this, that is re-usable, and easy to maintain.
My current solution is shown below, but I hope someone has a more elegant solution.
The problem with my own solution is that it will become difficult to read when the number of dependencies increase. It will then also require a lot of editing when I add another button and form.
My current solution is to use an observable to manage the state of the forms, like this:
HTML:
<button id="button-A">Show form A, hide button A and B</button>
<button id="button-B">Show form B, hide button A and B</button>
<form id="form-A">
...this form is initially hidden
...some form elements
<button id="cancel-A">Hide form A, show button A and B</button>
</form>
<form id="form-B">
...this form is initially hidden
...some form elements
<button id="cancel-B">Hide form B, show button A and B</button>
</form>
Dart:
import 'dart:html';
import 'package:observe/observe.dart';
final $ = querySelector;
final $$ = querySelectorAll;
Map<String, bool> toBeObserved = {
"showFormA" : false,
"showFormB" : false
};
// make an observable map
ObservableMap observeThis = toObservable(toBeObserved);
// start managing dependencies
main() {
// add click event to buttons
$('#button-A')
..onClick.listen((E) => observeThis["showFormA"] = true);
$('#button-B')
..onClick.listen((E) => observeThis["showFormB"] = true);
// add click events to form buttons
$('#cancel-A')
..onClick.listen((E) {
E.preventDefault();
E.stopPropagation();
observeThis["showFormA"] = false;
});
$('#cancel-B')
..onClick.listen((E) {
E.preventDefault();
E.stopPropagation();
observeThis["showFormB"] = false;
});
// listen for changes
observeThis.changes.listen((L) {
L.where((E) => E.key == 'showFormA').forEach((R) {
$('#form-A').style.display = (R.newValue) ? 'block' : 'none';
$('#button-A').style.display = (R.newValue || observeThis['showFormB']) ? 'none' : 'inline-block';
$('#button-B').style.display = (R.newValue || observeThis['showFormB']) ? 'none' : 'inline-block';
});
L.where((E) => E.key == 'showFormB').forEach((R) {
$('#form-B').style.display = (R.newValue) ? 'block' : 'none';
$('#button-A').style.display = (R.newValue || observeThis['showFormA']) ? 'none' : 'inline-block';
$('#button-B').style.display = (R.newValue || observeThis['showFormA']) ? 'none' : 'inline-block';
});
});
}
You can use basic CSS to show/hide the elements.
HTML
<div id="container" class="show-buttons">
<button id="button-A" class="btn" data-group="a">...</button>
<button id="button-B" class="btn" data-group="b">...</button>
<form id="form-A" class="form group-a">...</button>
<form id="form-B" class="form group-b">...</button>
</div>
CSS
.btn, .form {
display: none;
}
.show-buttons .btn,
.show-a .form.group-a,
.show-b .form.group-b {
display: block;
}
In Dart just get the data-group (or whatever you want to call this) attribute from the button. Toggle the CSS classes (show-buttons, show-a and show-b) on the container element to switch between the buttons and the specific forms.
This solution is very easy to extend on.
You can use something like this to handle all the elements in a generic way :
final Iterable<ButtonElement> buttons = querySelectorAll('button')
.where((ButtonElement b) => b.id.startsWith('button-'));
final Iterable<ButtonElement> cancels = querySelectorAll('button')
.where((ButtonElement b) => b.id.startsWith('cancel-'));
final Iterable<FormElement> forms = querySelectorAll('form')
.where((FormElement b) => b.id.startsWith('form-'));
buttons.forEach((b) {
b.onClick.listen((e) {
// name of clicked button
final name = b.id.substring(b.id.indexOf('-') + 1);
// hide all buttons
buttons.forEach((b) => b.hidden = true)
// show the good form
querySelector('#form-$name').hidden = false;
});
});
cancels.forEach((b) {
b.onClick.listen((e) {
// show all buttons
buttons.forEach((b) => b.hidden = false);
// hide all forms
forms.forEach((b) => b.hidden = true);
// prevent default
e.preventDefault();
});
});
// hide all form at startup
forms.forEach((f) => f.hidden = true);
You could use polymer's template functionality like
<template if="showA">...
This should work without embedding your elements within Polymer elements too.
This discussion provides some information how to use <template> without Polymer elements.
Using Polymer elements could also be useful.
It all depends on your requirements/preferences.
Angular.dart is also useful for such view manipulation.
If you want to use plain Dart/HMTL I don't have ideas how to simplify your code.