Lit-element checkbox not updated after reset - web-component

I'm new with Lit-element and I'm trying to modify an exsiting library. This library creates a table of checkboxes based on three arrays. Arrays AAA and BBB are used as indexes, and array CCC is used to set the "checked" property.
These arrays can be realoaded via ajax, so the initial data is reset.
The problem is, if I manually check/uncheck one checkbox an reset the data, the checkbox is not updated and the manual changes persist.
I've tried to add all three arrays to the "properties" section. Also I've created getters and setters. And finally I've tried to do a full requestUpdate", but nothing works.
Here is a piece of code (EDITED: replaced ?checked by .checked):
render(){
return html`
...
${this.AAA.map(A => html`
<tr>
...
${this.BBB.map(B => html`
<td>
...
<input type="checkbox" .checked=${this.CCC[A].includes(B)} id="${A}:${B}"/>
...
</td>
`)}
</tr>
`)}
...
`
}
static get properties() {
return {
//(added by me) Same for BBB and CCC
AAA: { type: Array, hasChanged: (newVal, oldVal) => {
return true;
}},
...
};
}
//(added by me) Same for BBB and CCC
set AAA(newAAA){
const oldAAA = this.AAA;
this._AAA = newAAA;
this.requestUpdate('AAA', oldAAA);
}
get AAA(){
return this._AAA
}
Ajax Data might look like this:
{
"AAA": [
"xxx",
"yyy",
"zzz"
],
"BBB": [
"111",
"222",
"333"
]
"CCC": {
"xxx": [
"111"
],
"yyy": [
"222",
"333"
],
"zzz": [
"222"
]
}
}
Here, a little example where you can try the problem: https://stackblitz.com/edit/js-fadxme
Some advise?
Thanks.

I've managed to do it modifying the shadowRoot/renderRoot in the "updated" method.
Input now is declared as:
<input type="checkbox" data-aaa="${A}" data-bbb="${B}" id="${A}:${B}"/>
instead of:
<input type="checkbox" .checked=${this.CCC[A].includes(B)} id="${A}:${B}"/>
And here the updated method:
updated(changedProperties) {
var inputs = this.shadowRoot.querySelectorAll("input[type='checkbox']");
inputs.forEach(elem => {
let A = elem.getAttribute('data-aaa');
let B = elem.getAttribute('data-bbb');
if(A && B){
elem.checked = this.CCC[A] ? this.CCC[A].includes(B) : false;
}
})
super.updated(changedProperties);
}
Here the working example: https://stackblitz.com/edit/js-fadxme
Anyway... what is preferible to use, shadowRoot or renderRoot?

Related

React-Table display contents of array as individual elements inside a cell

I am trying to use react-table to visualise some data structured as below:
{
companyName: "Barclays Bank Plc",
tenancies: ["G.01 # Newbuilding"],
status: "Active",
accountNumber: "EX209302",
logo: "companylogo1",
},
{
companyName: "Boeing AerospaceLtd",
tenancies: ["G.01 # Newbuilding", "G.02 # Newbuilding"],
status: "Active",
accountNumber: "EX209302",
logo: "companylogo1",
},
My column definition (which doesn't work) looks something like this:
{
Header: "Tenancies",
accessor: "tenancies",
Cell: (tableProps) => (
<div>{tableProps.cell.value.forEach((item) => item)}</div>
),
},
What I am trying to do is display each array item within it's own HTML tags (such as a div) so I can visually style and seperate them within the table cell.
Can anyone point me in the right direction as to the best way to achieve this?
According to the API document, the Cell receives the table Instance and returns a valid JSX.
{
...
Cell: (tableInstance) => JSX
}
Since the tenancies array are available from tableInstance.row.original.tenancies, so you can change your tenancies column definition as follow:
{
Header: "Tenancies",
Cell: ({ row }) => {
return (
<div>
{row.original.tenancies.map((tenancy, i) => (
<div key={i}>{tenancy}</div>
))}
</div>
);
}
}
Here is the example:

Vue-Select: How to save all entries as lower case items?

I am using the vue-select library. How can I force input entries to make all characters lower case? Right now, when I type the word "Baseball" into the input field, the tag is saved as "Baseball". I would like all tags to only keep a lower case version of the entry such as "baseball".
I have a sandbox here: https://stackblitz.com/edit/vue-ranqmt?file=src/App.vue
<template>
<div id="app">
<h3>Vue Select</h3>
<v-select
v-model="selected"
taggable
multiple
:options="options"
#input="setSelected"
></v-select>
<br /><br />
selected: <br />
<pre>{{ selected }}</pre>
</div>
</template>
<script>
import vSelect from 'vue-select';
import 'vue-select/dist/vue-select.css';
export default {
name: 'App',
components: {
'v-select': vSelect,
},
data() {
return {
selected: null,
options: [],
};
},
methods: {
setSelected(value) {
console.log(value);
},
},
};
</script>
a - You could add a computed property which returns the lowercased version to use in whichever part of your app you need it to be.
computed: {
selectedLowerCase() {
return this.selected.toLowerCase();
}
}
b - if you are using this for something like an API call, then you can turn the variable(s) into lowercase before submitting.
c - if you want the variable to appear as lowercase even in the input field you need to add the #input action to your input field and point it to a function to lowercase your input
methods: {
setSelected(value) {
this.selected = this.selected.toLowerCase();
console.log(value);
},
},

Wordpress Gutenberg autocomplete - saving attributes

I want to develop a custom block that will let the user pick some information from autocomplete. I manage to create the autocomplete component on edit function.
A user can select an item but i don't know how to handle the attribute save.
I'm trying to save the selected item as attribute package_name. I created the onChange function on Autocomplete component but event.target.value is undefined.
Here is my code from block.js
const { __ } = wp.i18n; // Import __() from wp.i18n
const { AlignmentToolbar,
BlockControls,
registerBlockType } = wp.blocks;
const { RichText } = wp.editor;
const { Autocomplete, } =wp.components;
const MyAutocomplete = () => {
const autocompleters = [
{
name: 'fruit',
triggerPrefix: '#',
options: [
{ name: 'Apple', id: 1 },
{ name: 'Orange', id: 2 },
{ name: 'Grapes', id: 3 },
{ name: 'test', id: 4 },
],
getOptionLabel: option => (
<span>
{ option.name }
</span>
),
getOptionKeywords: option => [ option.name ],
isOptionDisabled: option => option.name === 'Grapes',
getOptionCompletion: option => (
<abbr title={ option.name }>{ option.name }</abbr>
),
}
];
function onChangeAuto(newContent){
console.log('autocompletexx '+newContent);
}
function onSelectAuto(event){
console.log(event.target);
console.log( event.target.value);
}
return (
<div>
<Autocomplete completers={ autocompleters }>
{ ( { isExpanded, listBoxId, activeId } ) => (
<div
contentEditable
suppressContentEditableWarning
aria-autocomplete="list"
aria-expanded={ isExpanded }
aria-owns={ listBoxId }
aria-activedescendant={ activeId }
onChange={onChangeAuto }
onSelect={onSelectAuto}
>
</div>
) }
</Autocomplete>
<p class="autocomplete_p">Type # for triggering the autocomplete.</p>
</div>
);
};
registerBlockType( 'residence-gutenberg-block/membership-package-settings', {
title: __( 'Residence - Membership Package Settings' ), // Block title.
icon: 'shield',
category: 'common',
keywords: [
__( 'membership-package-settings' ),
],
attributes:{
package_id:{
type:'string',
select:'p'
},
package_name:{
type:'string',
},
},
edit: function( props ) {
const { attributes: {package_name}, className,setAttributes,isSelected } = props;
return (
<div className={ props.className }>
<form>
<label className="wpresidence_editor_label">Current Package: {package_name}</label>
<MyAutocomplete></MyAutocomplete>
</form>
</div>
);
},
save: function( props ) {
// Rendering in PHP
return null;
},
} );
Passing down onChange, onSelect to the div element won't work, because these attributes are only applicable to the form field elements (as input, select, etc.).
I checked the documentation and the source code and didn't find any details or official approaches for dealing with the case.
However, I'm seeing two possible approaches for getting the selected value:
1. Using Autocomplete onReplace prop
Looking into the Autocomplete's source code, I noticed that onSelect callback invokes onReplace prop with the selected option as array. It may not fit all the cases, but you can give it a try! It may be enough for your case! You can try to add your handler to the onReplace as follows:
<Autocomplete
onReplace={ option => { console.log(option) } }
completers={ autocompleters }>
{ ( { isExpanded, listBoxId, activeId } ) => (
<div
contentEditable
suppressContentEditableWarning
aria-autocomplete="list"
aria-expanded={ isExpanded }
aria-owns={ listBoxId }
aria-activedescendant={ activeId }
/>
) }
</Autocomplete>
2. Listen for <div /> changes manually
You can add onInput, onBlur listeners to the <div />, having an uncontrolled react div component and when the div's value is changed then we can keep the changed value in your parent component's state.
Here's a great discussion, which describes these technique: React.js: onChange event for contentEditable
The good think is that there's already a plugin (based on this discussion) that can do it for you: react-contenteditable.
Firstly you have to convert your <MyAutocomplete /> component to be a statefull (not functional) and then:
import ContentEditable from 'react-contenteditable'
// Boilerplate ...
<Autocomplete completers={ autocompleters }>
{ ( { isExpanded, listBoxId, activeId } ) => (
<ContentEditable
html={this.state.html}
onChange={this.handleChange}
contentEditable
suppressContentEditableWarning
aria-autocomplete="list"
aria-expanded={ isExpanded }
aria-owns={ listBoxId }
aria-activedescendant={ activeId }
/>
) }
</Autocomplete>
Conclusion
I'm surprised that in the Autocomplete's documentation there aren't any details for this case. I guess it's because of the following statement (27.08.2018):
Gutenberg is being developed on GitHub, and you can try an early beta
version today from the plugin repository. Though keep in mind it’s not
fully functional, feature complete, or production ready.
However, one of both mentioned approaches above will help you, until they provide a complete API to work with their components.
I would suggest you to keep wrapping the Wordpress's <Autocomplete /> with your own component - in order to easily refactor your code later, when they release the complete API.
If you have questions, feel free to comment below.

Wordpress returning HTML symbol codes in GraphQL query in Gatsby website

First, I'm not sure if this is a Wordpress issue, a Gatsby issue, or a GraphQL issue (or something else).
I'm building a Gatsby site using gatsby-source-wordpress to pull content from a self-hosted wordpress site.
I'm building a homepage that queries for the most recent posts and displays the titles along with some other info. If the title has a special character(-, &, etc.) then it returns the HTML code for that character in the title ('&' instead of '&' for instance). Then it gets displayed as the code, which just looks bad. See below for some of my code.
What are my options in terms of getting it to return without the HTML codes or for converting them back to the symbols?
This is the relevant part of the query
export const homepageQuery = graphql`
query homepageQuery {
mainSection: allWordpressPost(limit: 3) {
edges {
node {
id
title
date (formatString: "MMM DD, YYYY")
featured_media {
source_url
alt_text
}
slug
categories {
id
name
link
}
}
}
}
}
This is and example of a single post returned
{
"data": {
"mainSection": {
"edges": [
{
"node": {
"id": "d1e9b5e0-bb8a-5e73-a89a-ba73aae92e0d",
"title": "Stories from the Bus Station Bistro & Creamery",
"date": "Jul 15, 2018",
"featured_media": {
"source_url": "https://www.storynosh.com/wp-content/uploads/2018/07/IMG_9962_3.jpg",
"alt_text": ""
},
"slug": "stories-bus-station-bistro-creamery",
"categories": [
{
"id": "3bceb083-de92-5eff-90f3-e2da524f3c2a",
"name": "Stories",
"link": "https://www.storynosh.com/category/stories/"
}
]
}
}
This is the component the data ultimately is passed to which renders the data.
class MostRecent extends Component {
render(){
const bgimage = `url(${this.props.data.featured_media.source_url})`;
return (
<div className={`${styles.featured} ${styles['most-recent']}`} style={{backgroundImage: bgimage}} >
<div className={styles['article-info-container']} >
<h1 className={styles['featured-header']} >{this.props.data.title}</h1>
<div>
<Link to={this.props.data.categories[0].link} className={styles['category-box-link']}>
<span className={styles['category-box']}>{this.props.data.categories[0].name}</span>
</Link>
<span className={styles['featured-date']}>{this.props.data.date}</span>
</div>
</div>
</div>
)
}
}
What the result looks like
After a bunch of searching around I did come across this post.
Unescape HTML entities in Javascript?
It became the base of my solution.
I added the function in this solution before the return in my component. Then created a variable that ran the function and returned the encoded html.
The following works.
class SecondMostRecent extends Component {
render() {
const bgimage = `url(${this.props.data.featured_media.source_url})`
function htmlDecode(input){
var e = document.createElement('div');
e.innerHTML = input;
// handle case of empty input
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}
const title = htmlDecode(this.props.data.title);
return (
<div className={`${styles.featured} ${styles['second-most-recent']}`} style={{backgroundImage: bgimage}} >
<div className={styles['article-info-container']} >
<h3 className={styles['featured-header']} >{title}</h3>
<div>
<Link to={this.props.data.categories[0].link} className={styles['category-box-link']}>
<span className={styles['category-box']}>{this.props.data.categories[0].name}</span>
</Link>
<span className={styles['featured-date']}>{this.props.data.date}</span>
</div>
</div>
</div>
)
}
}
Using dangerouslySetInnerHTML solved this issue for me:
Replace: <h1>{this.props.data.title}</h1>
With: <h1 dangerouslySetInnerHTML={{ __html: this.props.data.title }} />

Selected ListItem value using SelectableContainerEnhance in Material-UI

I'm really new to ReactJS and trying to work with Material-UI components on a new Meteor app I'm working with. A classic use case has come to my needs: a list of items changes the UI when the user selects or not some ListItem. Surprisingly, I found that React isn't easy with parent-child component relations like that.
I tried to follow the Material-UI Docs, implementing SelectableList component like the docs suggests using the SelectableContainerEnhance class. Then I went this way:
const {ListItem, Avatar, Divider} = mui;
App = React.createClass({
mixins: [ReactMeteorData],
getMeteorData() {
return {
players: Players.find({}, { sort: { score: -1 } }).fetch()
}
},
render() {
return (
<SelectableList subheader="Players list">
{this.data.players.map((player) => {
return (
React.Children.toArray([
<Divider />,
<ListItem
value={player._id}
primaryText={player.name}
secondaryText={player.score}
leftAvatar={<Avatar>{player.name}</Avatar>} />
])
);
})}
</SelectableList>
<Divider />
{ true /* What to do now? */ ?
(<span>Thanks!</span>) :
(<span>Click a player to select</span>)}
);
}
});
Ok, the list items has become selectable. But how to know if any ListItem is selected? And how to get the value and adjust the UI according to it?
They talk about setting up a valueLink in the documentation.
<SelectableList
subheader="Players List"
valueLink={{
value: this.state.selectedIndex,
requestChange: this.handleUpdateSelectedIndex
}}>
And then define a handleUpdateSelectedIndex to set the state:
getInitialState() {
return {selectedIndex: 1};
},
handleUpdateSelectedIndex(e, index) {
this.setState({
selectedIndex: index,
});
},
This will give you this.state.selectedIndex on your App component that you can do whatever you need to do with it.

Resources