Make base class which operates on a child class - javascript

I know I'm conceptually wrong somewhere here, but how would you arrange this type of situation? I think my confusion would be clear from the code below:
class Base {
constructor() {
this._defineProperties( this.properties );
}
_defineProperties(properties) {
properties.forEach((value, key) => {
Object.defineProperties(this, key, {
value: value,
enumerable: true
})
});
}
}
class Child extends Base {
properties: {
'title': '',
'text': ''
}
}
const obj = new Child();
child.title
child.text

Related

LitElement remove item from list

When the page first loads, the delete buttons generated by the code below work as expected. However, if you alter the text in one of the <textarea> elements, the delete button no longer works correctly. How can I fix this?
import { LitElement, html } from 'lit-element';
class MyElement extends LitElement {
static get properties() {
return {
list: { type: Array },
};
}
constructor() {
super();
this.list = [
{ id: "1", text: "hello" },
{ id: "2", text: "hi" },
{ id: "3", text: "cool" },
];
}
render() {
return html`${this.list.map(item =>
html`<textarea>${item.text}</textarea><button id="${item.id}" #click="${this.delete}">X</button>`
)}`;
}
delete(event) {
const id = event.target.id;
this.list = this.list.filter(item => item.id !== id);
}
}
customElements.define("my-element", MyElement);
I'm not sure of the exact cause, but I think it has to do with the way lit-html decides which DOM elements to remove when rendering a list with fewer items than the previous render. The solution is to use the repeat directive. It takes as its second argument a function that helps lit-html identify which DOM elements correspond to which items in the array:
import { repeat } from 'lit-html/directives/repeat.js'
// ...
render(){
return html`
${repeat(this.list, item => item.id,
item => html`<textarea>${item.text}</textarea><button id="${item.id}" #click="${this.delete}">X</button><br>`
)}
`;
}

Update ngrx selector inside ngOnChanges

I have a parent component (B) that is getting data from it's parent input (A)
(C) have is (B) child component.
Inside (B) I'm having a selector that gets data from the store.
export class BComponent implements OnChanges {
#Input() branchId;
ngOnChanges() {
this.selectedDataByBranch$ = this.store.pipe(
select(selectBranchDirections, { branchId: this.branchId, dir: this.selectedDirection })
);
this.selectedDataByBranch$.subscribe(selectedDataByBranch => {
this.trainsDatasets = this.getDatasets(selectedDataByBranch);
this.lineChart.data.datasets = this.trainsDatasets ? this.trainsDatasets : [];
this.lineChart.update();
});
directionChanged(event) {
this.selectedDirection = event;
this.selectedDataByBranch$ = this.store.pipe(
select(selectBranchDirections, { branchId: this.branchId, dir: this.selectedDirection })
);
}
}
directionChanged is the Output event that I get from (C)
The issue this that selectedDataByBranch subscription is not getting the new data update triggered inside selectedDataByBranch$
I have also tried this way
directionChanged(event) {
this.selectedDirection = event;
select(selectBranchDirections, { branchId: this.branchId, dir: this.selectedDirection });
}
What i could suggest is. Turn your parameters into a Subject then merge with the store selection, in your directionChanged(event) method provide value to subject.
So your final code will be something like this:
export class BComponent implements OnChanges {
#Input() branchId;
criterias$= new Subject<{branchId:number,dir:number}>;
ngOnChanges() {
this.selectedDataByBranch$ = this.criterias$.pipe(mergeMap(criteria=> this.store.pipe(
select(selectBranchDirections, { branchId: criteria.branchId, dir: this.searchDirection})
)));
this.selectedDataByBranch$.subscribe(selectedDataByBranch => {
this.trainsDatasets = this.getDatasets(selectedDataByBranch);
this.lineChart.data.datasets = this.trainsDatasets ? this.trainsDatasets : [];
this.lineChart.update();
});
this.criterias$.next({branchId:this.branchId,dir:this.sortDirection}); // init first call
}
directionChanged(event) {
this.selectedDirection = event;
this.criterias$.next({ branchId: criteria.branchId, dir: this.searchDirection}});
);
}
}
This stackblitz tries to materialize what i say.

How to notify array mutation to parent component in polymer 3

I have 2 components using same array binding for example:
{
title: "food",
data : ["data1", "data2", "data3"]
}
title is on parent component and data is binded from parent to child component to child works with the array.
how can i do to when i remove a array data element notify to parent component?
Here the example.
In the example i have a children with binded array and one method to remove array elements and notify. And parent compontent has a observer named arrayChanges.
if code works parent component has to know about child array length, but it doesnt works.
<script type='module'>
import {PolymerElement, html} from 'https://unpkg.com/#polymer/polymer/polymer-element.js?module';
import {} from 'https://unpkg.com/#polymer/polymer#3.1.0/lib/elements/dom-repeat.js?module';
class ParentComp extends PolymerElement {
static get properties() {
return {
myArr: {
type: Array,
observer: "arrayChanges"
},
changes: {
type: String
}
};
}
static get template() {
return html`
<div>[[myArr.title]]</div>
<children-comp data='[[myArr.data]]'></children-comp>
<div>[[changes]]</div>
`;
}
ready(){
super.ready();
this.myArr = {
title : "My component",
data : [
{titulo: "titulo1", comment : "im comment number 1"},
{titulo: "titulo2", comment : "im comment number 2"}
]
}
}
arrayChanges(){
this.changes = "Array length : "+this.myArr.data.length;
console.log("the Array has been changed");
}
}
class ChildrenComp extends PolymerElement {
static get properties() {
return {
data: {
type: Array,
notify: true
}
};
}
static get template() {
return html`
<ul>
<dom-repeat items='[[data]]' >
<template>
<li>
[[index]] )
[[item.titulo]]
[[item.comment]]
<button data-index$='[[index]]' on-click='handle_button'>Borrar</button>
<hr>
</li>
</template>
</dom-repeat>
</ul>
`;
}
handle_button(e){
var index = e.currentTarget.dataset.index;
this.notifyPath("data");
this.splice("data", index, 1);
}
}
customElements.define('children-comp', ChildrenComp);
customElements.define('parent-comp', ParentComp);
</script>
<parent-comp></parent-comp>
The parent component would only handle change-notifications in two-way bindings (using curly brackets). Your data binding incorrectly uses one-way binding (square brackets).
<children-comp data='[[myArr.data]]'></children-comp>
^^ ^^ square brackets: one-way binding
<children-comp data='{{myArr.data}}'></children-comp>
^^ ^^ curly brackets: two-way binding
Also note the simple observer (specified in myArr-property declaration) does not detect array mutations. You should use a complex observer instead. You could observe data/length changes and/or array mutations:
static get properties() {
return {
myArr: {
// type: Array, // DON'T DO THIS (myArr is actually an object)
type: Object,
// observer: 'arrayChanges' // DON'T DO THIS (doesn't detect array splices)
}
}
}
static get observers() {
return [
'arrayChanges(myArr.data, myArr.data.length)', // observe data/length changes
'arraySplices(myArr.data.splices)', // observe array mutations
]
}
arrayChanges(myArrData, myArrDataLength) {
console.log({
myArrData,
myArrDataLength
})
}
arraySplices(change) {
if (change) {
for (const s of change.indexSplices) {
console.log({
sliceIndex: s.index,
removedItems: s.removed,
addedItems: s.addedCount && s.object.slice(s.index, s.index + s.addedCount)
})
}
}
}
<html>
<head>
<script src="https://unpkg.com/#webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
</head>
<body>
<script type='module'>
import {PolymerElement, html} from 'https://unpkg.com/#polymer/polymer/polymer-element.js?module';
import {} from 'https://unpkg.com/#polymer/polymer#3.1.0/lib/elements/dom-repeat.js?module';
class ParentComp extends PolymerElement {
static get properties() {
return {
myArr: {
type: Array,
},
changes: {
type: String
}
};
}
static get observers() {
return [
'arrayChanges(myArr.data, myArr.data.length)',
'arraySplices(myArr.data.splices)',
]
}
static get template() {
return html`
<div>[[myArr.title]]</div>
<children-comp data='{{myArr.data}}'></children-comp>
<div>{{changes}}</div>
`;
}
ready(){
super.ready();
this.myArr = {
title : "My component",
data : [
{titulo: "titulo1", comment : "im comment number 1"},
{titulo: "titulo2", comment : "im comment number 2"}
]
}
}
arrayChanges(myArr, myArrLength){
this.changes = "Array length : " + myArrLength;
console.log("the Array has been changed", myArr);
}
arraySplices(change) {
if (change) {
for (const s of change.indexSplices) {
console.log({
sliceIndex: s.index,
removedItems: s.removed,
addedItems: s.addedCount && s.object.slice(s.index, s.index + s.addedCount)
})
}
}
}
}
class ChildrenComp extends PolymerElement {
static get properties() {
return {
data: {
type: Array,
notify: true
}
};
}
static get template() {
return html`
<ul>
<dom-repeat items='[[data]]' >
<template>
<li>
[[index]] )
[[item.titulo]]
[[item.comment]]
<button data-index$='[[index]]' on-click='handle_button'>Borrar</button>
<hr>
</li>
</template>
</dom-repeat>
</ul>
`;
}
handle_button(e){
var index = e.currentTarget.dataset.index;
this.notifyPath("data");
this.splice("data", index, 1);
}
}
customElements.define('children-comp', ChildrenComp);
customElements.define('parent-comp', ParentComp);
</script>
<parent-comp></parent-comp>
</body>
</html>

How would I change the background color of a list item upon click? (ES6 & Polymer)

I've cloned a repository which focuses on creating a To-Do application using ES6 and Polymer 3. I'm trying to implement a button which turns the background color containing a string green upon click. I've tried doing this, but I keep failing to get the desired result.
Example code:
static get properties() {
return {
list: {type: Array},
todo: {type: String},
};
}
constructor() {
super();
this.list = [
this.todoItem('buy cereal'),
this.todoItem('buy milk')
];
this.todo = '';
this.createNewToDoItem = this.createNewToDoItem.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
this.handleInput = this.handleInput.bind(this);
}
todoItem(todo) {
return {todo}
}
createNewToDoItem() {
this.list = [
...this.list,
this.todoItem(this.todo)
];
this.todo = '';
}
//Right here is where I tried to implement the background color change.
checkItem() {
checkItem = document.getElementById('checkItem'),
checkItem.addEventListener('click', () => {
this.list = this.list.filter(this.todo)
document.body.style.backgroundColor = 'green';
});
}
deleteItem(indexToDelete) {
this.list = this.list.filter((toDo, index) => index !== indexToDelete);
}
render() {
return html`
${style}
<div class="ToDo">
<h1>Grocery List</h1>
<h1 class="ToDo-Header">What do I need to buy today?</h1>
<div class="ToDo-Container">
<div class="ToDo-Content">
${repeat(
this.list,
(item, key) => {
return html`
<to-do-item
item=${item.todo}
.deleteItem=${this.deleteItem.bind(this, key)}
></to-do-item>
`;
}
)}
</div>
I'd be eternally thankful if someone helped me out. I've created two JSFiddle links which show the code I've worked on thus far:
Link 1: https://jsfiddle.net/r2mxzp1c/ (Check line 42-49)
Link 2: https://jsfiddle.net/zt0x5u94/ (Check line 13 & 22-24)
I'm not sure about the approach. But this link might help you
https://stackblitz.com/edit/web-components-zero-to-hero-part-one?file=to-do-app.js
from this guy: https://stackblitz.com/#thepassle
You should try to make the reactive templating work for you by defining presentation details in terms of your element's properties.
For example, this is a stripped-down approach to the same problem:
class TestElement extends LitElement{
static get properties() {
return {
'items': { 'type': Array }
};
}
constructor() {
super();
// set up a data structure I can use to selectively color items
this.items = [ 'a', 'b', 'c' ].map((name) =>
({ name, 'highlight': false }));
}
render() {
return html`<ol>${
this.items.map((item, idx) =>
html`<li
#click="${ () => this.toggle(idx) }"
style="background: ${ item.highlight ? '#0f0' : '#fff' }">
${ item.name }
</li>`)
}</ol>`;
}
toggle(idx) {
// rendering won't trigger unless you replace the whole array or object
// when using properties of those types. alternatively, mutate with the
// usual .push(), .splice(), etc methods and then call `this.requestUpdate()`
this.items = this.items.map((item, jdx) =>
jdx === idx ? { ...item, 'highlight': !item.highlight } : item
);
}
}
https://jsfiddle.net/rzhofu81/305/
I define the template such that the elements are colored the way I want depending on an aspect of their state (the "highlight" attribute of each entry in the list), and then I focus the interaction on updating the state to reflect what the user is doing.

Reactjs: How to update component when a new prop comes up?

I want to update/re-render the component after a new update comes up. All I am doing is:
I have a list of dealers for a casino game, what I want is to add a new dealer, and once the new dealer is added then display it in the view. It is actually happening, but in order for me to see the new dealer, I have to reload the page.
I am not updating the state, I am working with this.props. Look at my code
#connectToStores
export default class Dealers extends Component {
constructor (props) {
super(props);
this.state = {}
}
componentWillMount () {
GetDealersActions.getDealers();
}
static getStores () {
return [ GetDealersStore, CreateDealersStore ];
}
static getPropsFromStores () {
return {
...GetDealersStore.getState(),
...CreateDealersStore.getState(),
}
}
render () {
return (
<div>
{!!this.props.dealerData ?
this.props.dealerData.dealersData.map((dealer) => {
return (here I am rendering what I need);
: <p>Loading . . .</p>
</div>
}
_addDealer = () => {
CreateDealersActions.createDealer({
DealerName : this.refs.DealerName.getValue(),
CardId : this.refs.CardId.getValue(),
NickName : this.refs.NickName.getValue(),
});
}
}
as you see the component above in the code is doing the initial rendering properly, the problem comes up when you hit _addDealer(), which is not updating the component, you should reload the page in order to see the new item in the view.
If you do a console.log(this.props); within _addDealer(), you will get something like this
{params: Object, query: Object, dealerData: Object, newDealerData: null}
where dealerData holds the full data of the dealers in the view but you can't see there the new dealer created. And newDealerData remains null
so, what do you think I should do in order to update the component everytime a new prop/dealer comes up ? or how do I update the props? which is the proper method in this situation ?
here is the full code for stores and actions just in case
action
#createActions(flux)
class CreateDealersActions {
constructor () {
this.generateActions('createDealerSuccess', 'createDealerFail');
}
createDealer (data) {
const that = this;
that.dispatch();
axios.post(`${API_ENDPOINT}/create-dealer/create-dealer`, data)
.then(function success (data) {
that.actions.createDealerSuccess({data});
})
}
};
store
#createStore(flux)
class CreateDealersStore {
constructor () {
this.state = {
newDealerData : null,
};
}
#bind(CreateDealersActions.createDealerSuccess)
createDealerSuccess (data) {
this.setState({
newDealerData : data.response.config.data,
});
}
}
the Dealers component is within a tab named management, which is this one:
const menuItems = [
{ route : 'dealers', text : 'Dealers' },
{ route : 'game-info', text : 'Game Info' },
{ route : 'player-info', text : 'Players Info' },
{ route : 'money', text : 'Money' }
];
export default class Management extends React.Component {
static propTypes = {
getActivePage : React.PropTypes.func,
menuItems : React.PropTypes.arrayOf(React.PropTypes.object),
}
static contextTypes = {
router : React.PropTypes.func,
}
render () {
return (
<div>
<TabsMainMenu menuItems={menuItems} getActivePage={this._getActivePage} />
<RouteHandler />
</div>
);
}
_getActivePage = () => {
for (const i in menuItems) {
if (this.context.router.isActive(menuItems[i].route)) return parseInt(i, 10);
}
}
}

Categories

Resources