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')
Related
I am working on a oop project that I want to use after for my self and add to my portfolio. I'm also using it as a learning project as I'm new to oop.
In this project I want to be able to edit the added td fields that are submitted to the bottom table.
when I try to create input element and then append the input to the submitted items it just creates input boxes next to the tds.
(Update I have now got it to edit the first td item I'm just now working on updating the edit after clicking enter or adding a ok button to the app.)
This is my html css and js:
// this is the es5 way to do it with constructors and prototypes
// keyword constructor
function Keyword(sitename, keyword, keywordpost) {
this.sitename = sitename;
this.keyword = keyword;
this.keywordpost = keywordpost;
}
// UI Constructor is the things assosiated to the ui different prototypes and methods
function UI() {
}
// this is the UI prototype for the above UI constructor
UI.prototype.addKeywordToList = function(keywordNames) {
const list = document.getElementById('keyword-list');
// create tr element
const row = document.createElement('tr');
// insert cols
row.innerHTML = `
<td>${keywordNames.sitename}</td>
<td>${keywordNames.keyword}</td>
<td>${keywordNames.keywordpost}</td>
<td>X</td>
<td>edit</td>
`;
console.log(row);
console.log(list);
list.appendChild(row);
}
// start here on monday or tuesday ------------------------------------------------------
// show alert
UI.prototype.showAlert = function(message, className) {
//create div
const div = document.createElement('div');
// add class
div.className = `alert ${className}`;
// add text
div.appendChild(document.createTextNode(message));
// Get parent
const container = document.querySelector('.input-container');
const form = document.querySelector('#keyword-form');
//insert alert box/message above the form
container.insertBefore(div, form);
// Timeout after 3 secs
setTimeout(function() {
document.querySelector('.alert').remove();
}, 3000);
}
// clear fields of there input
UI.prototype.clearFields = function() {
document.getElementById('website-name').value = '';
document.getElementById('keyword-name').value = '';
document.getElementById('keyword-post').value = '';
}
//prototype to delete item
UI.prototype.deleteBook = function(target) {
if (target.className === 'delete') {
target.parentElement.parentElement.remove();
}
}
//prototype to edit book -------- working on this here updated now edits element but just need save the update
UI.prototype.editBook = function(target) {
if(target.className === 'edit'){
const firstItem = document.querySelector('td');
const input = document.createElement('input');
firstItem.innerHTML = '';
input.type = 'text';
firstItem.appendChild(input);
console.log(firstItem);
}
}
// Event Listeners
document.getElementById('keyword-form').addEventListener('submit', function(e) {
// get form values
const siteName = document.getElementById('website-name').value
const keywordName = document.getElementById('keyword-name').value
const keywordPost = document.getElementById('keyword-post').value
//instantiate a book creating a new one based of the Keyword object constructor ---------------------------
const keywordNames = new Keyword(siteName, keywordName, keywordPost);
//instantiate UI object ----------------------------------------------------
const ui = new UI();
//create validation to stpp crosses being submited on a line when there is nothing to submit
if (siteName === '' || keywordName === '' || keywordPost === '') {
ui.showAlert('please fill in all fields', 'error');
} else {
ui.addKeywordToList(keywordNames);
ui.clearFields();
ui.showAlert('Your item has been added', 'success');
}
console.log('test');
e.preventDefault();
});
//event listener for delete
document.getElementById('keyword-list').addEventListener('click', function(e) {
console.log(123);
//instatiate ui again beceuse we outside of above function
const ui = new UI();
ui.deleteBook(e.target);
ui.showAlert('Your item has been removed', 'success');
e.preventDefault();
});
//event listener for editing the table items --------- working on this here
document.getElementById('keyword-list').addEventListener('click', function(e) {
const ui = new UI();
ui.editBook(e.target);
e.preventDefault();
});
* {
margin: 0;
box-sizing: border-box;
}
.main-container {
width: 100%;
height: 100vh;
}
.input-container {
display: flex;
height: 50%;
width: 90%;
margin: auto;
flex-direction: column;
justify-content: space-around;
}
.inputContainer {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
flex-wrap: wrap;
}
#keyword-form {
display: flex;
flex-direction: column;
align-content: space-between;
justify-content: space-evenly;
flex-wrap: wrap;
width: 100%;
/* padding-top: 6px; */
height: 90%;
}
.input {
padding: 10px;
}
.submit-button {
padding: 10px;
width: 120px;
}
.output-table {
width: 100%;
}
tr {
text-align: center;
}
.success,
.error {
color: white;
padding: 5px;
margin: 5px 0 15px 0;
}
.success {
background: green;
}
.error {
background: red;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<div class="main-container">
<div class="input-container">
<h1>Seo Keyword Tracker</h1>
<form id="keyword-form">
<div class="inputContainer">
<label for="Website-Name">Website Name</label>
<input class="input one" id="website-name" type="text" placeholder="keyword">
</div>
<div class="inputContainer">
<label for="Keyword-Name">Keyword Name</label>
<input class="input two" id="keyword-name" type="text" placeholder="keyword">
</div>
<div class="inputContainer">
<label for="Keyword-Site-Number">Keyword Post Title</label>
<input class="input three" id="keyword-post" type="text" placeholder="keyword">
</div>
<div class="inputContainer">
<input class="submit-button" id="Keyword-Site-Name" type="submit" placeholder="keyword">
</div>
</form>
</div>
<hr>
<table class="output-table">
<thead>
<tr>
<th>Website Name</th>
<th>Keyword Name</th>
<th>Keyword Post Title</th>
<th></th>
</tr>
</thead>
<tbody id="keyword-list"></tbody>
</table>
</div>
<script src="app.js"></script>
<script src="appes6.js"></script>
</body>
</html>
Ok so if you run snippet you will see the input boxes that I mentioned also when I inspect element they seem to me creating a new tr and then the inputs outputting within that.
I'm not sure were I'm going wrong i thought maybe i could grab the td by class or id but they do not have class and ids as there created dynamically.
I thought maybe grabbing the innerHTML fields used in the (add) prototype one and add that into the (edit) prototype one.
(Update i have now got it to edit the first td item I'm just now working on updating the edit after clicking enter or adding a ok button to the app.)
So if anyone could help it would be greatly appreciated based on my update this post.
Many Thanks
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.
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
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;
}
so here is my script code
class Pop extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
let currDoc = document.currentScript.ownerDocument;
let template = currDoc.querySelector('#pop-up');
let tmplNode = document.importNode(template.content, true);
let shadowRoot = this.createShadowRoot();
shadowRoot.appendChild(tmplNode);
shadowRoot.querySelector("#content").innerHTML = this.innerHTML;
}
}
window.customElements.define("pop-up", Pop);
and here is my template
<template id="pop-up">
<style>
* {
padding: 0;
margin: 0;
}
.btn {
//styling
}
.btn:hover {
//styling
}
#box{
//styling
display: none;
}
#box h1{
//styling
}
</style>
<div id="box">
<h1> Your Shopping Cart</h1>
<text id="content"> </text>
<button class="btn" onclick="close()"> Close </button>
</div>
</template>
and in my index file i have this
<button class="btn" onclick="pop()">Show Pop</button>
<pop-up id="pop"> pop-up box</pop-up>
<script>
function pop(){
document.getElementById("pop").style.display = "block";
}
</script>
I am trying to do a pop-up box. And when i click on the "Show Pop" button i want to change the display property from style to "block" from "none". But for some reason it doesn't work. Im new to this shadow dom elemets and i can't really figure it out.
It is hard to explain everything in this answer, but the following code will give you an overview of how the solution might look like.
MDN, as usual, has the perfect intro for web components and shadow doms here
class Pop extends HTMLElement {
constructor() {
// Always call super first in constructor
super();
let template = document.getElementById('pop-up');
let templateContent = template.content;
// Create a shadow root
const shadowRoot = this.attachShadow({ mode: 'open' })
.appendChild(templateContent.cloneNode(true));
// attach close listener
this.shadowRoot.querySelector('.btn').addEventListener('click', this.close.bind(this));
}
// close pop-up
close() {
this.style.display = 'none';
}
// open pop-up
open() {
this.style.display = 'block';
}
}
window.customElements.define("pop-up", Pop);
function pop() {
// notice we are using the open method here
document.getElementById("pop").open();
}
<template id="pop-up">
<style>
:host {
display: none;
}
* {
padding: 0;
margin: 0;
}
.btn {
//styling
}
.btn:hover {
//styling
}
#box {
//styling
display: none;
}
#box h1 {
//styling
}
</style>
<div id="box">
<h1> Your Shopping Cart</h1>
<!-- Notice we are using slots -->
<slot> </slot>
<button class="btn"> Close </button>
</div>
</template>
<button class="btn" onclick="pop()">Show Pop</button>
<pop-up id="pop"> pop-up box </pop-up>