Lit-Element data loading error in update-element.js - javascript

I have the following element:
import {LitElement, html} from '#polymer/lit-element';
import {SdkArticle} from '../elements/sdk-article/sdk-article.js'
class PraksisView extends LitElement {
static get properties() {
return {
articles: {type: Array},
};
}
constructor() {
super();
this.articles = [];
}
async firstUpdated() {
await fetch(`/testData.json`)
.then(r => r.json())
.then(async data => {
this.articles = data.articles;
});
}
render() {
return html `
<style>
.indent-1 {float: left;}
.indent-1 section {width: 50%; float: left;}
header {
display: block;
height: 50px;
background-color: #215959;
color: white;
}
.center {
margin: auto;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 50px;
}
</style>
<header>
<h3 class="center">Almen praksis</h3>
</header>
<section class="indent-1">
<section>
<div>
<ul>
<li>Patientbehandling</li>
<li>Klinikdrift</li>
<li>Midtkraft</li>
</ul>
</div>
</section>
<section>
${this.articles.map(
article =>
html`
<div>
<sdk-article data=${article}></sdk-article>
</div>
`,
)}
</section>
</section>
`;
}
}
customElements.define('praksis-view', PraksisView);
As you can see here I load some test data in from testData.json.
Now the other sdk-article:
import {LitElement, html} from '#polymer/lit-element';
class SdkArticle extends LitElement {
static get properties() {
return {
data: {type: Object}
};
}
constructor() {
super();
this.data = {};
}
render() {
this.generateHtml();
return html`
`;
}
generateHtml(){
console.log(this.data);
}
}
customElements.define('sdk-article', SdkArticle);
Basically this just checks if the data is there.
When I run this the data is undefined and I get an error:
VM1588:1 Uncaught SyntaxError: Unexpected token o in JSON at position 1
at JSON.parse (<anonymous>)
at fromAttribute (updating-element.js:59)
at Function._propertyValueFromAttribute (updating-element.js:259)
at HTMLElement._attributeToProperty (updating-element.js:387)
at HTMLElement.attributeChangedCallback (updating-element.js:343)
at AttributeCommitter.commit (parts.js:79)
at AttributePart.commit (parts.js:111)
at TemplateInstance.update (template-instance.js:40)
at NodePart.__commitTemplateResult (parts.js:248)
at NodePart.commit (parts.js:189)
Can anyone see what the issue is here?

Solution
if you want to pass the actual property on then you need to use the "dot notation".
<sdk-article .data=${article}></sdk-article>
this is basically the same as doing
document.querySelector('sdk-article').data = article;
Explanation of issue
In your current code you are setting an attribute.
<sdk-article data=${article}></sdk-article>
which is basically this
document.querySelector('sdk-article').setAttribute('data', article);
Attributes however only accept strings.

Related

Cannot render elements from each block

I'm trying to render msg from all_msgs array.
<script>
import { sender_msgs } from "../var_store";
import { receiver_msgs } from "../var_store";
const all_msgs = [1,2,3,4];
// $: msgs = all_msgs;
sender_msgs.subscribe((e) => {
all_msgs.push(`Sender: ${e.slice(-1)[0]}`);
});
receiver_msgs.subscribe((e) => {
all_msgs.push(`Receiver: ${e.slice(-1)[0]}`);
console.log(all_msgs);
});
</script>
<div class="chat-window">
{#each all_msgs as msg}
<div>{msg}</div>
{/each}
</div>
<style>
.chat-window {
width: 500px;
border: 1px solid #000;
}
</style>
I can see the numbers can be rendered as html text. But couldn't render other texts when all_msgs is updated by the two subscribe methods. I can see all_msgs in the console having the texts but can't be seen in html. The o/p of the screen and console.log of all_msgs is also shared.
Solved it: reactivity is assignment based. Below code works:
<script>
import { sender_msgs } from "../var_store";
import { receiver_msgs } from "../var_store";
let all_msgs = [];
// $: msgs = all_msgs;
sender_msgs.subscribe((e) => {
all_msgs.push(`Sender: ${e.slice(-1)[0]}`);
all_msgs = all_msgs;
});
receiver_msgs.subscribe((e) => {
all_msgs.push(`Receiver: ${e.slice(-1)[0]}`);
all_msgs = all_msgs;
});
</script>
<div class="chat-window">
{#each all_msgs as msg}
<div>{msg}</div>
{/each}
</div>
<style>
.chat-window {
width: 500px;
border: 1px solid #000;
}
</style>

I am not able to querySelect elements of shadow dom

I have this parent class, which I will use to crete a set of other layout classes.
They basically create flex based div.
import { LitElement , css, html} from "lit-element";
const _allowedFlexValue = new Set(['flex-start','flex-end', 'center', 'space-around', 'space-between','stretch','base-line']);
export class BaseLayoutEl extends LitElement{
#flexDirection
constructor(flexDirection){
super();
this.ma="flex-start";
this.ca="stretch";
this.#flexDirection = flexDirection;
}
static get properties(){
return {
ma: {type: String},
ca: {type: String}
}
}
check_supplied_values(){
if(!_allowedFlexValue.has(this.ca) || !_allowedFlexValue.has(this.ma)){
console.log("main-axis: " , this.ma, ", cross-axis: ",this.ca);
console.log (`Main-axis/Cross-axis can have only following values: ${[..._allowedFlexValue]}`);
return false;
}
return true;
}
static get styles(){
return css`
.container{
width: 100%;
height: 100%;
overflow: auto;
}
.flex{
display: flex;
flex-wrap: nowrap;
}
`;
}
render(){
if(!this.check_supplied_values()){
return html`<div>Error</div>`;
}
return html`
<style>
.flex{
flex-direction: ${this.#flexDirection};
justify-content: ${this.ma};
align-items: ${this.ca};
}
</style>
<div class="container">
<div class="flex" id="flex">
<slot></slot>
</div>
</div>
`;
}
}
And this one child class, where I am trying to access the parent class shadow dom.
import { BaseLayoutEl } from "./baseLayoutEl.js";
export class CenterThem extends BaseLayoutEl{
constructor(){
super("column");
this.ma="center";
this.ca="center";
}
connectedCallback(){
super.connectedCallback();
console.log(this.shadowRoot.querySelector('.flex'));
}
}
Suprisingy this.shadowRoot.querySelector alwasy retruns null, no matter if I select by class slector '.flex' or by id selector '#flex'.
Can any one let me know how do I select the shadow dom in the child ?
try this
this.parentNode.querySelector('.flex')
or
this.parentNode.parentNode.querySelector('.flex')

How to solve "Cannot read property 'scrollIntoView' of undefined"?

I want to scroll to a particular div on clicking a button in Angular 7, below is the code I am using but it is working in stackblitz but showing error when i use in my project.
"Cannot read property 'scrollIntoView' of undefined".
https://stackblitz.com/edit/angular-scroll-local-variable?file=src%2Fapp%2Fscroll.component.ts
try this link: https://stackblitz.com/edit/angular-scroll-local-variable-ja96uz?file=src%2Fapp%2Fapp.component.html
<button (click)="scroll(target)"></button>
<div #target>Your target</div>
and in component:
scroll(el) {
el.scrollIntoView();
}
Try angular ViewportScroller Service Which provide scrollToAnchor method
scroll.html
<button (click)="scroll('target')">Click to scroll</button>
<div id="target">Your target</div>
scroll.ts
import { Component, Input } from '#angular/core';
import { ViewportScroller } from '#angular/common';
#Component({
selector: 'scroll',
template: `
<button (click)="scroll('target')">Click to scroll</button>
<div id="target">Your target</div>
`,
styles: [`h1 { font-family: Lato; }`, `div { margin-top: 5000px; }`]
})
export class ScrollComponent {
constructor(private vps: ViewportScroller) {
}
scroll(id) {
this.vps.scrollToAnchor(id);
}
}
Example:https://stackblitz.com/edit/angular-scroll-local-variable-99hwvo
Try using ViewChild:
//HTML
<button (click)="scroll()"></button><div #target>Your target</div>
//ts
//Import
import { ..., ViewChild, ElementRef } from '#angular/core';
//Declare
#ViewChild('target') targetEl: ElementRef;
scroll() {
this.targetEl.nativeElement.scrollIntoView();
}
Scroll.html
<button (click)="scroll()">Click to scroll</button>
<div id="target">Your target</div>
componet.ts
getOffset(el) {
el = el.getBoundingClientRect();
return {
left: el.left + window.scrollX,
top: el.top + window.scrollY,
bottom: el.top + window.scrollY
}
}
scroll() {
var scroll_to = document.getElementById('target');
var topHight = this.getOffset(scroll_to).top;
window.scrollTo(0, topHight);
}
The code is not working because of *ngIf condition, when you use show variable is default set to false, hence the div is not rendered on the component.
The code should be used in a related component where you want to scroll to be done, for example:
if you required in the scroll component then
HTML:
<button (click)="scroll(target)">clicking this button</button>
<div style="marging-top: 100px; height: 900px;"></div>
<div #target *ngIf="show" style="border: 1px solid #000; padding: 10px;margin-top: 10px;">
show get screen scrolled to this div
</div>
TS:
scroll(el: HTMLElement) {
if(el){ // If the div is rendered on the HTML then it should be HTML element
el.scrollIntoView();
}
}
StackBlitz

Angular Material stranger render behavior

I'm using the latest version of Angular and Angular Material. I'm having issues with my components. The page Load like this:
Before de click
And the content just appear when I click on the menu.
After clicking
I already tried to uninstall and install all the meterial stuff. And this issue continues. I have a separate module to import and export all the material components. Here is the code of the component that is using the material tags:
profile.component.ts
import { Component, OnInit } from '#angular/core';
import {AuthService} from "../../services/auth.service";
import {User} from "../../model/model.user";
import {Router} from "#angular/router";
import { Expense } from '../../model/model.expense';
import { ReceiptService } from "../../services/receipt.service";
import { ExpenseService } from './../../services/expense.service';
import { Receipt } from './../../model/model.receipt';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
totalReceita = 0;
totalDespesa = 0;
receipts = []
expenses = []
currentUser: User;
constructor(public authService: AuthService, public router: Router, public receiptService: ReceiptService, public expenseService: ExpenseService) {
this.currentUser = JSON.parse(localStorage.getItem('currentUser'));
}
ngOnInit() {
this.receiptService.getReceipts(this.currentUser.id).subscribe(
data => {
console.log(data)
this.receipts = this.retiraArrayRec(data);
this.somaTudoRec();
}
);
this.expenseService.getExpenses(this.currentUser.id).subscribe(
data => {
this.expenses =this.retiraArrayDesp(data);
this.somaTudoDes();
}
);
console.log(this.receipts)
}
retiraArrayRec(data){
let lista = []
data.forEach(element => {
let receita : Receipt = new Receipt;
receita.name = element[0];
receita.value = element[1]
lista.push(receita);
});
return lista;
}
retiraArrayDesp(data){
let lista = []
data.forEach(element => {
let despesa : Expense = new Expense;
despesa.name = element[0];
despesa.value = element[1]
lista.push(despesa);
});
return lista;
}
somaTudoRec(){
this.receipts.forEach(element => {
this.totalReceita += element.value;
});
}
somaTudoDes(){
this.expenses.forEach(element => {
this.totalDespesa += element.value;
});
}
// login out from the app
logOut() {
this.authService.logOut()
.subscribe(
data => {
this.router.navigate(['/login']);
},
error => {
});
}
}
profile.component.html
<mat-sidenav-container fullscreen class="menu-container">
<mat-sidenav #sidenav>
<mat-nav-list>
<a mat-list-item routerLink="/home" routerLinkActive="active-list-item">
<h2 matLine>Home</h2>
<mat-icon matListIcon>home</mat-icon>
</a>
<a mat-list-item routerLink="/account" routerLinkActive="active-list-item">
<h2 matLine>Receitas</h2>
<mat-icon matListIcon>local_atm</mat-icon>
</a>
<a mat-list-item routerLink="/settings" routerLinkActive="active-list-item">
<h2 matLine>Despesas</h2>
<mat-icon matListIcon>show_chart</mat-icon>
</a>
<a mat-list-item routerLink="/settings" routerLinkActive="active-list-item">
<h2 matLine>Notificações</h2>
<mat-icon matListIcon>notification_important</mat-icon>
</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content fxFlexFill>
<mat-toolbar>
<button class="hamburger mat-button" mat-icon-button (click)="sidenav.toggle()">
<mat-icon>menu</mat-icon>
<span>Menu</span>
</button>
<span>Bem vindo ao CPF, Pedro</span>
<button mat-icon-button [mat-menu-trigger-for]="menu">
<mat-icon>more_vert</mat-icon>
</button>
</mat-toolbar>
<mat-menu x-position="before" #menu="matMenu">
<button mat-menu-item>
<mat-icon>person</mat-icon>
<span>Perfil</span>
</button>
<button mat-menu-item>
<mat-icon>money_off</mat-icon>
<span>Sair</span>
</button>
</mat-menu>
</mat-sidenav-content>
</mat-sidenav-container>
profile.component.css
mat-toolbar {
background-image: linear-gradient(to bottom, #00b4db, #0083b0);
color: #fff;
justify-content: space-between;
box-shadow: 0 2px 5px 0 rgba(0,0,0,.3);
}
span {
font-size: 16px;
font-weight: 700;
}
.hamburger {
height: 100%;
font-size: 18px;
}
.mat-sidenav-container {
min-width: 400px;
max-width: 100%;
}
.mat-sidenav {
flex: 0 1 auto;
}
.menu-spacer {
flex: 1;
}
.mat-list-item-content {
padding: 0 25px;
}
.menu-container {
min-width: 200px;
max-width: 100%;
}
First, please consider that you need to insert style into the project:
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
If it did not solve the problem, I guess it's because you are using another font and also added !important for the font, please try to add the code below in your style.css (or style.scss) which is the general/public CSS file which effects on your whole project:
mat-icon{
font-family: 'Material Icons' !important;
}

React app refreshing page for each item deletion

I have a React app here that works in many browsers:
<!DOCTYPE html>
<html>
  
<head>
  <title>React! React! React!</title>
  <script src="https://unpkg.com/react#15.3.2/dist/react.js"></script>
  <script src="https://unpkg.com/react-dom#15.3.2/dist/react-dom.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  
<style>
body {
padding: 50px;
background-color: #66CCFF;
font-family: sans-serif;
}
.todoListMain .header input {
padding: 10px;
font-size: 16px;
border: 2px solid #FFF;
}
.todoListMain .header button {
padding: 10px;
font-size: 16px;
margin: 10px;
background-color: #0066FF;
color: #FFF;
border: 2px solid #0066FF;
}
.todoListMain .header button:hover {
background-color: #003399;
border: 2px solid #003399;
cursor: pointer;
}
.todoListMain .theList {
list-style: none;
padding-left: 0;
width: 255px;
}
.todoListMain .theList li {
color: #333;
background-color: rgba(255,255,255,.5);
padding: 15px;
margin-bottom: 15px;
border-radius: 5px;
}
  </style>
</head>
  
<body>
  
  <div id="container">
  
  </div>
  
  <script type="text/babel">
    var destination = document.querySelector("#container");
// es6 is working in the browser :)
let y = [1, 3, 6, 15, 39, 88].find(x => x > 39 && x < 90)
var TodoItems = React.createClass({
render: function(){
var todoEntries = this.props.entries;
function createTask(item){
return (
<li key={item.key}>
<span>{item.text}</span>
<a href="" data-id="{item.id}"
className="remove-filter"
onClick={this.props.remove.bind(item)}
>
remove
</a>
</li>
)
}
// var listItems = todoEntries.map(createTask.bind(this));
return (
<ul className="theList">
{this.props.entries.map(createTask.bind(this))}
</ul>
);
}
});
var TodoList = React.createClass({
getInitialState: function(){
return {
items: []
};
},
addItem: function(e) {
var itemArray = this.state.items;
itemArray.push(
{
text: this._inputElement.value,
key: this.state.items.length
}
);
this.setState({
items: itemArray
})
this._inputElement.value = "";
e.preventDefault();
},
// removing items from a list
// https://stackoverflow.com/questions/27817241/how-to-remove-an-item-from-a-list-with-a-click-event-in-reactjs
removeItem: function(item, event){
event.preventDefault();
var items = this.state.items.filter(function(itm){
return item.id !== itm.id;
});
this.setState({ items: items });
},
render: function() {
return (
<div className="todoListMain">
<div className="header">
<form onSubmit={this.addItem}>
<input ref={(a) => this._inputElement = a}
placeholder="enter task" />
<button type="submit">add</button>
</form>
</div>
<TodoItems remove={this.removeItem} entries={this.state.items} />
</div>
);
}
});
    ReactDOM.render(
      <div>
        <TodoList/>
      </div>,
      destination
    );
  </script>
</body>
  
</html>
I have followed how to remove an item from a list with a click event in ReactJS? and it seems to be working, with a few issues.
First, the example references <a href data-..., but this did not work and redirected me to file:///Users/cchilders/tutorials/javascript/react/todo-list/true, where it got true from something it evaluated (true should be the index.html)
Deletion works using href="", but it flashes the page in an ugly manner, and the usual suspects to make an href do nothing don't work...
...if I try href="#" or href="javascript:;" and similar I get
embedded:60 Uncaught TypeError: Cannot read property 'preventDefault' of undefined
Second, I am getting warning
react.js:20478 Warning: bind(): React component methods may only be bound to the component instance. See TodoList
no matter what, for each thing I try.
Third, it is deleting all items in the list on remove, not just 1 item.
How can I make React do this deletion onclick without refreshing the page, and delete 1 item at a time?
There are few things that u need to change, check the jsfiddle for working example, do the changes in ur code accordingly.
*Don't write like this: {this.props.entries.map(createTask.bind(this))}
instead of that just call a method {this.createTask()} from render, that function will return the complete list, n define createTask outside of the render method. like this:
createTask: function(){
return this.props.entries.map(item=>{
return (
<li key={item.key}>
<span>{item.text}</span>
<a href="#" data-id="{item.id}"
className="remove-filter"
onClick={()=>this.props.remove(item)}
>
remove
</a>
</li>
)})
},
*U forgot to give the dead link to href, don't leave it empty define it like this: href="#".
*Don't bind the props remove method with onClick, use it like normal method calling, like this: onClick={()=>this.props.remove(item)}.
Check jsfiddle: https://jsfiddle.net/79eax14s/
Let me know if u need any help in this.

Categories

Resources