Polymer 3 Data Binding not updating on property set - polymer-3.x

I have an Polymer 3 module (simplified/wrong below to explain only);
import {html,PolymerElement} from '#polymer/polymer/polymer-element.js';
class myInput extends PolymerElement {
static get template() {
return html `
<input id="inputBox" value='{{bar::input}}'/><br/>
<a>You have typed [[bar]]!</a>
`;
}
static get properties() {
return {
bar: {
observer: '_dataChanged',
},
}
_dataChanged () {
this.bar = "BAR HAS CHANGED!!"
}
}
[[bar]] is successfully updated & displayed on page.
{{bar::input}} successfully fires _dataChanged.
But [[bar]] does not update & display "BAR HAS CHANGED!!" on page when _dataChanged() is triggered.
Any idea what I have done wrong?
Thanks for your help.

Use one of the polymer button element. Then you can bind the value to the bar property easy. Here the example:
DEMO
import { PolymerElement, html } from '#polymer/polymer';
import '#polymer/paper-button/paper-button.js'
class MyElement extends PolymerElement {
static get properties() {
return {
bar: {
observer: '_dataChanged',
}
}}
static get template() {
return html`
<input id="inputBox" value='{{bar::input}}'/><br/>
<paper-button on-tap="_clickMe">You have typed [[bar]]!</paper-button>
`;
}
_dataChanged(d){console.log(d)}
_clickMe () {
this.bar = "CLICKED!!"
}
}
customElements.define('my-element', MyElement);

Related

How do I sent data to element's grandchildren in LitElement?

How I could efficiently pass value from MyElement to GrandChildrenElement?
index.html
<my-element></my-element>
myElement.ts
#customElement('my-element')
export class MyElement extends LitElement {
value = 'foo';
onChangeValue() {
this.value = 'bar';
}
render() {
return html`
<child-element></child-element>
`;
}
}
childElement.ts
#customElement('child-element')
export class ChildElement extends LitElement {
...
render() {
return html`
<grandchild-element></grandchild-element>
`;
}
}
grandChildElement.ts
#customElement('grandchild-element')
export class GrandChildElement extends LitElement {
#property()
value = '';
render() {
return html`
<p>${value}</p>
`;
}
}
The standard way would be to pass the value down through the child element using properties:
#customElement('my-element')
export class Element extends LitElement {
#state()
value = 'foo';
toggle() {
if (this.value === 'foo') {
this.value = 'bar';
} else {
this.value = 'foo';
}
}
render() {
return html`
<my-child value="${this.value}"></my-child>
<button #click="${this.toggle}">Toggle</button>
`;
}
}
#customElement('my-child')
export class Child extends LitElement {
#property({ type: 'string' })
value;
render() {
return html`<my-grandchild value="${this.value}"></my-grandchild>`;
}
}
#customElement('my-grandchild')
export class GrandChild extends LitElement {
#property({ type: 'string' })
value;
render() {
return html`<div>${this.value}</div>`;
}
}
Playground
But if you're looking for a way to bypass the elements in between, you'll have to get a bit more creative.
One solution could be to have the element collect subscribers and update them when its value changes.
In the example below, the grandchild dispatches a "subscribe" event when it connects, which bubbles up to the element. The element then gets the grandchild (via the event's composedPath), updates its value, and adds it to the set of subscribers. When the element changes its value within toggle(), it updates its subscribers.
import {html, css, LitElement} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
#customElement('my-element')
export class Element extends LitElement {
value = 'foo';
subscribers = new Set();
constructor() {
super();
this.addEventListener('subscribe', (e: CustomEvent) => {
const composedTarget = e.composedPath()[0] as any;
composedTarget.value = this.value;
this.subscribers.add(composedTarget);
});
this.addEventListener('unsubscribe', (e: CustomEvent) => {
const composedTarget = e.composedPath()[0];
this.subscribers.delete(composedTarget);
});
}
toggle() {
if (this.value === 'foo') {
this.value = 'bar';
} else {
this.value = 'foo';
}
for (const subscriber of this.subscribers) {
grandchild.value = this.value;
}
}
render() {
return html`
<my-child></my-child>
<button #click="${this.toggle}">Toggle</button>
`;
}
}
#customElement('my-child')
export class Child extends LitElement {
render() {
return html`<my-grandchild></my-grandchild>`;
}
}
#customElement('my-grandchild')
export class GrandChild extends LitElement {
#property({ type: 'string' })
value;
connectedCallback() {
super.connectedCallback();
this.dispatchEvent(new CustomEvent('subscribe', { bubbles: true, composed: true }));
}
disconnectedCallback() {
super.disconnectedCallback();
this.dispatchEvent(new CustomEvent('unsubscribe', { bubbles: true, composed: true }));
}
render() {
return html`<div>${this.value}</div>`;
}
}
Playground

How can I share data between components through a service?

I am having trouble sharing information between components. What I am trying to do is, show in the home.component.html data that is generated in the about.component.ts.
The operation would be:
By means of a button in the home.component.html, a function in the home.component.ts is called, this function calls a function of the about-service.ts, which collects the data from the about.component.html that obtains the about.component.ts data.
This is my home.component.html:
<button mat-menu-item (click)="goToAbout()">
<mat-icon svgIcon="logoBN"></mat-icon>
Acerca
</button>
This is my home.component.ts:
import { AboutComponent } from '../about/about.component';
export class HomeComponent {
public aboutInfo: AboutService;
goToAbout() {
.subscribe(emitData =>
}
}
From the goToAbout() function of the home.component.ts I need to get the data from the aboutBuild() function of the about.component.ts:
This is my about.component.ts:
import { AboutService } from '';
export class AboutComponent {
ngOnInit() {
}
aboutBuild() {
......code.........
}
}
This is my about.component.html:
<header class="title-color" fxFlex>Build Info</header>
I have created a function in the service to try to communicate both components.
about-service.ts:
observer = new Subject();
public subscriber$ = this.observer.asObservable();
emitData(aboutBuild) {
this.observer.next(aboutBuild);
}
But I can't access the aboutBuild() function of the about.component.ts, what do I need to include in the service to communicate the two components?
The AboutService is fine you have one public subscriber and a function to trigger event.
Your goToAbout() in home.component.ts :
export class HomeComponent {
constructor(public aboutService: AboutService) {}
goToAbout() {
let data = {key: 'value'};
this.aboutService.emitData(data);
}
}
Then your about.component.ts:
export class AboutComponent {
constructor(public aboutService: AboutService) {}
ngOnInit() {
this.aboutService.subscriber$.subscribe(data => {
this.aboutBuild(data);
});
}
aboutBuild(data) {
console.log(data)
}
}

How can I toggle a class in a LitElement Web Component

I am working with precompiled stylesheet (from SASS) and only need to toggle classes.
I have two elements that will be writing to an event. Based on the event being true/false I want to to toggle a class on my component.
Would this work:
import { LitElement, html } from 'lit-element'
/**
*
*
* #export
* #class MenuMainButton
* #extends {LitElement}
*/
export class MenuMainButton extends LitElement {
static get properties() {
return {
name: { type: String },
toggled: { type: String }
}
}
constructor() {
super()
this.name = 'Menu'
this.toggled = ''
this.addEventListener('toggle-main-menu', this.handleEvents)
}
render() {
return html`
<a #click=${this._onClick} class="menu-button wk-app-menu-button app-menu-open ${this.toggled} govuk-body"
>${this.name}</a
>
`
}
handleEvents(event) {
this.toggled = event.toggle ? 'hide-item' : ''
}
_onClick() {
const toggleMainMenu = new CustomEvent('toggle-main-menu', {
toggle: this.toggled === '' ? 1 : 0
})
this.dispatchEvent(toggleMainMenu)
}
}
window.customElements.define('main-menu-button', MenuMainButton)
One way to make styles dynamic is to add bindings to the class or style attributes in your template.
The lit-html library offers two directives, classMap and styleMap, to conveniently apply classes and styles in HTML templates.
Styles - LitElement

LitElement load external script

I tried to simply load it in my rendered HTML but this does not work.
current code:
render() {
return html `<script #onload="${this.mapKitLoadedCallback}" src="https://cdn.apple-mapkit.com/mk/5.x.x/mapkit.js"></script>`;
}
got a solution from polymer slack channel (credit to westbrook):
import { LitElement, html } from 'lit-element';
class MyElement extends LitElement {
script() {
let script = document.createElement('script');
script.onload = this.onLoad.bind(this);
script.src = 'https://cdn.apple-mapkit.com/mk/5.x.x/mapkit.js';
return script;
}
onLoad() {
alert('loaded');
}
render() {
return html`
<p>Hello world! From my-element</p>
${this.script()}
`;
}
}
customElements.define('my-element', MyElement);

Lit-Element - Can't get Id of item from Object

I am learning lit-element and have run into a small problem I am trying to setup the ability to remove an Item from my list but I am unable to get the id of my Item it is coming across as undefined when I test it with console.log. I have three components the add-item.js which adds items to the list that is working fine. app.js is the main component that handles the auto refresh of the page aswell as the main rendering of the page this is where I have the event listeners for the addItem and the removeItem. Then I have todo-item component which is where I have the object that I am trying to get the ID for the remove functionality. Im at a loss at what I am doing wrong here and was hoping some one could take a look and point me in the right direction
here is the code so far .
add-item.js
```
import {LitElement, html} from 'lit-element';
class AddItem extends LitElement{
static get properties(){
return{
todoList: Array,
todoItem: String
}
}
constructor(){
super();
this.todoItem = '';
}
inputKeypress(e){
if(e.keyCode == 13){
e.target.value="";
this.onAddItem();
}else{
this.todoItem = e.target.value;
}
}
onAddItem(){
if(this.todoItem.length > 0){
let storedTodoList = JSON.parse(localStorage.getItem('todo-
list'));
storedTodoList = storedTodoList === null ? [] : storedTodoList;
storedTodoList.push({
id: new Date().valueOf(),
item: this.todoItem,
done: false
});
localStorage.setItem('todo-list',
JSON.stringify(storedTodoList));
this.dispatchEvent(new CustomEvent('addItem',{
bubbles: true,
composed: true,
detail: {
todoList: storedTodoList
}
}));
this.todoItem = '';
}
}
render(){
return html `
<div>
<input value=${this.todoItem}
#keyup="${(e) => this.inputKeypress(e)}">
</input>
<button #click="${() => this.onAddItem()}">Add Item</button>
</div>
`;
}
}
customElements.define('add-item', AddItem)
```
app.js
```
import {LitElement, html} from 'lit-element';
import './add-item';
import './list-items';
class TodoApp extends LitElement{
static get properties(){
return{
todoList: Array
}
}
constructor(){
super();
let list = JSON.parse(localStorage.getItem('todo-list'));
this.todoList = list === null ? [] : list;
}
firstUpdated(){
this.addEventListener('addItem', (e) => {
this.todoList = e.detail.todoList;
});
this.addEventListener('removeItem', (e) => {
let index = this.todoList.map(function(item) {return
item.id}).indexOf(e.detail.itemId);
this.todoList.splice(index, 1);
this.todoList = _.clone(this.todoList);
localStorage.setItem('todo-list', JSON.stringify(this.todoList));
})
}
render(){
return html `
<h1>Hello todo App</h1>
<add-item></add-item>
<list-items .todoList=${this.todoList}></list-items>
`;
}
}
customElements.define('todo-app', TodoApp)
```
todo-item.js
```
import {LitElement, html} from 'lit-element';
class TodoItem extends LitElement{
static get properties(){
return{
todoItem: Object
}
}
constructor(){
super();
this.todoItem = {};
}
onRemove(id){
this.dispatchEvent(new CustomEvent('removeItem',{
bubbles: true,
composed: true,
detail:{
itemId: id
}
}));
}
render(){
console.log(this.todoItem.id);
return html `<li>${this.todoItem}</li>
<button #click="${() =>
this.onRemove(this.todoItem.id)}">Remove</button>`;
}
}
customElements.define('todo-item', TodoItem);
```
I am looking to get the Id of the item so that I can remove it from the list for example if i have 5 items, one, two , three , four, five and I click the button to remove the third Item it should be removed and the list updated with the remaining items .. right now it is deleting the items but it is the last one on the list which is what I do not want to happen.
Looking forward to some help on this so that I can move forward with the project
thank you .
The issues has been resolved,
I was not supplying the entire array, just one element. After fixing the code I was able to get the Id of the object and move forward with my project as expected.

Resources