From Polymer3 to lit-element and material components: replacement for paper-tabs and iron-pages - polymer

I am porting a Polymer 3 app to lit-element stepwise and also want to replace the paper and iron elements by material web components. I very often am using the combination of paper-tabs and iron-pages to show property pages/dialogs.
What would be the replacement for paper-tabs/iron-pages in the material web components world?
I have found mwc-tab-bar but there is no example for actually displaying contents according to the selected tab.
Has anyone an example for how to build what sometimes is called a page-control (tabs plus contents)?

There are several options: (I would prefer 1 & 3)
You could just create a condition to render and eventually lazy load a certain page.
Use something like lion-steps (they also provide tabs)
Use a router like simple-wc-router
class MyElement extends LitElement {
static get properties() {
return {
page: String,
}
}
get _oneTemplate() {
return html`Page one`;
}
get _twoTemplate() {
return html`Page two`;
}
constructor() {
super();
this.page = 'one';
setTimeout(() => (this.page = 'two'), 5000);
}
render() {
return this.page === 'one' ? this._oneTemplate : this._twoTemplate;
}
}

Related

How to get inner content as a variable without rendering in LitElements

I am using my lit element like this
<my-header>MyHeading</my-header>
And I have my lit element's render method:
render() {
return html`
<h3><slot></slot></h3>
`;
}
which is working perfectly. Now I want the inner content i.e. "MyHeading" in my lit element's class as a value(not to render). Is there any way to get that innerHTML or as a text?
Note: my use case can be to set another property of rendered content like
render() {
return html`
<h3 id="${//How to get that 'MyHeading' here??}"><slot></slot></h3>
`;
}
Is it possible to get inner content as a value?
This is what you get when you learn new stuff starting with a Library or Framework;
You learn the Tool, not the Technology.
The child-elements of your customElement are not available yet
when the connectedCallback fires
so you wait till the EventLoop is empty (and thus know all children are parsed)
Or use any of the Library methods that (usually) fire even later than a setTimeout
Or, even more blunty, like many blogs show, execute the script that creates your Element
after the whole DOM is parsed by marking it a type="module" or async or defer
<script>
customElements.define("my-element", class extends HTMLElement {
constructor(){
super().attachShadow({mode:"open"}).innerHTML = `<h3><slot></slot></h3>`
}
connectedCallback() {
setTimeout(() => { // wait till innerHTML is parsed
let title = this.innerText;
console.log("Reflected from lightDOM:" , title);
this.shadowRoot.querySelector("h3").id = title;
})
}
})
</script>
<my-element>
Hello Web Components!
</my-element>

Iron-signal alternate in Polymer 2?

I have been using iron-signals almost everywhere in my application.
Now I was upgrading my polymer 1 application to polymer 2 and I found that <iron-signals> is not used anymore.
What is the alternate path to achieve the same. I basically want to pass data between different pages in my web app.
You should be able to simply dispatch events on window from one element and listen for them in other elements.
Example:
// Element 1
class FooElement extends Polymer.Element {
connectedCallback() {
super.connectedCallback()
}
ready() {
super.ready()
window.addEventListener('bar-was-called', e => {
console.log(e.detail) // logs 'hello-bar'
})
}
}
// Element 2
class BarElement extends Polymer.Element {
connectedCallback() {
super.connectedCallback()
}
ready() {
super.ready()
}
doBar() {
window.dispatchEvent(new CustomEvent('bar-was-called', {
detail: 'hello-bar'
}))
}
}
Side note
Keep in mind that iron-signals was removed for a reason. AFAIK it's that it promotes a hard-to-debug communications architecture.
From <iron-signals> README:
Note: avoid using iron-signals whenever you can use a controller (parent element) to mediate communication instead.
Iron Signals have been deprecated in polymer 1.
Replace uses of iron-signals with iron-meta.

How to represent web component tags in Kotlin html frameworks?

Kotlin has frameworks to represent html, such as kotlinx. How can I represent web component tags in such frameworks? For instance, if I want to use Polymer components, do I have to extend these frameworks to include every Polymer component?
You can Customize Kotlinx (To create a Custom Tag). For a tag called dicom-editor to be used inside divs:
class DicomEditor(consumer: TagConsumer<*>) :
HTMLTag("dicom-editor", consumer, emptyMap(),
inlineTag = true,
emptyTag = false),
HtmlInlineTag {}
fun DIV.dicom_editor(block: DicomEditor.() -> Unit = {}) {
DicomEditor(consumer).visit(block)
}
...
div{
dicom_editor {
onMouseDownFunction = {_ ->
window.alert("Dicom Editor")
}
}
}
In the example above, the dicom_editor call includes a callback for the mouse down event. You can also add atributes: attributes["data-toggle"] = "dropdown"
You can add attributes as fields:
class DicomEditor(consumer: TagConsumer<*>) :
HTMLTag("dicom-editor", consumer, emptyMap(),
inlineTag = true,
emptyTag = false),
HtmlInlineTag {
var data_toggle: String = ""
set(x) {attributes["data-toggle"] = x}
}
fun DIV.dicom_editor(block: DicomEditor.() -> Unit = {}) {
DicomEditor(consumer).visit(block)
}
...
div{
dicom_editor {
data_toggle = "dropdown"
}
}
In the Kotlin code, you have to use _ in the place of - or you get an error.
It is possible to use Vaadin 10 to control (Polymer-based) web components from server-side Java. Events are fully supported, please read the docs on how to wrap Web Component in a Java API.
There is a Kotlin integration with Vaadin 10 ready:
The Vaadin-on-Kotlin (VoK) framework; there are nice guides for Vaadin 10 to get you started there.
An example project built on VoK: Vaadin-Kotlin-PWA. This project demoes usage of AppLayout Web Component: AppLayout.kt.
The example code which uses the AppLayout layout and shows the basic page:
#BodySize(width = "100vw", height = "100vh")
#HtmlImport("frontend://styles.html")
#Viewport("width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes")
#Theme(Lumo::class)
class MainLayout : AppHeaderLayout(), RouterLayout {
private val content: Div
init {
appHeader {
appToolbar {
title.text = "Vaadin Kotlin PWA Demo"
paperIconButton(VaadinIcons.FILE_REMOVE) {
addClickListener {
Notification.show("A toast!", 3000, Notification.Position.BOTTOM_CENTER)
}
}
}
}
appDrawer {
navMenuItem(VaadinIcons.LIST, "Task List")
navMenuItem(VaadinIcons.COG, "Settings")
navMenuItem(VaadinIcons.QUESTION, "About")
}
content = div {
setSizeFull(); classNames.add("app-content")
}
}
override fun showRouterLayoutContent(content: HasElement) {
this.content.element.appendChild(content.element)
}
}
Disclaimer: I'm the author of VoK
You can create XSD file for your components and generate them automatically. It's exactly how kotlinx do it.
Generator is part of the project. Check it out here: https://github.com/Kotlin/kotlinx.html/tree/master/generate
There is also the source XSD file for HTML5 in resource folder:
https://github.com/Kotlin/kotlinx.html/blob/master/generate/src/main/resources/html_5.xsd

how to force a Polymer.Element extended class to execute its lifecycle without attaching it to the dom?

Consider this element (minimal for the purpose of the question) :
class MyCountDown extends Polymer.Element
{
static get is () { return 'my-count-down'; }
static get properties ()
{
return {
time: { /* time in seconds */
type: Number,
observer: '_startCountDown'
},
remains: Number
}
}
_startCountDown ()
{
this.remains = this.time;
this.tickInterval = window.setInterval(() => {
this.remains--;
if (this.remains == 0) {
console.log('countdown!');
this._stopCountDown();
}
}, 1000);
}
_stopCountDown () {
if (this.tickInterval) {
window.clearInterval(this.tickInterval);
}
}
}
customElements.define(MyCountDown.is, MyCountDown);
If I get one instance and set the property time,
let MyCountDown = customElements.get('my-count-down');
let cd = new MyCountDown();
cd.time = 5;
the property time changes but the observer and the _startCountDown() function is not called. I believe Polymer is waiting for the Instance to be attached to the DOM because in fact when I appendChild() this element to the document the count down starts and after 5 seconds the console logs 'countdown!' as expected.
My goal is to execute this lifecycle without attaching anything to the document because the instances of MyCountDown are not always attached to the view but/and they need to be live-code between the different components of my web application.
One solution is to attach the new MyCountDown instances to an hidden element of the dom to force the Polymer lifecycle but I think this is not so intuitive.
I don't know the exact place to call, but the problem you have is that the property assessors are not in place.
I think you might get a clue from this talk https://www.youtube.com/watch?v=assSM3rlvZ8 at google i/o
call this._enableProperties() in a constructor callback?

How can I reset redux state when hitting go back in a web browser?

This is my use case:
In the WelcomeScreen I have code like this:
class WelcomeScreen extends Component {
render() {
const {
checkoutState,
} = this.props;
if (checkoutState.status === TRYING_TO_BUY) {
return this.renderPurchaseForm(plan);
}
return this.renderWelcome();
}
When the user visit /welcome, he can hit a purchase button, which will dispatch an action and set the checkoutState.status to TRYING_TO_BUY. The Welcome will be rerendered by calling renderPurchaseForm
Within renderPurchaseForm, it will render a ArticlePurchaseBlock
renderPurchaseForm() {
const { articleId } = this.props;
return (
<ArticlePurchaseBlock
articleId={articleId}
/>
)
and in the block, the class will try to update the url to reflect that it is in an input form
import { withRouter } from 'react-router';
class ArticlePurchaseBlock extends Component {
componentWillMount() {
const { history } = this.props;
history.push(URL_BUY_ARTICLE);
}
render() {
// render a redux-form
}
}
export default withRouter(ArticlePurchaseBlock);
You can see the history.push(URL_BUY_ARTICLE); is called in componentWillMount.
Now the problem is: when the user in the purchase form, if a user wants to go back to previous url (/welcome) , he can't. It is because the state of checkoutState.status is still TRYING_TO_BUY. The welcome is always rendered to the form.
Is there any where within the ArticlePurchaseBlock I can monitor the go back event and unset the state? I do not plan to use redux-saga-router yet because of time constraint.
I am using react-router v4
I designed a router for this exact problem. It's excessively difficult with react-router. https://github.com/cellog/react-redux-saga-router. For your code:
https://gist.github.com/cellog/0731f7e1ba8f9009f6b208c2bd15aa16
The entire thing can be done in 1 line of code, and your routes look almost identical to react-router, with 1 additional line for mapping param or url change to action.