LitElement load external script - web-component

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);

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

store is undefined in vue 3 class component unit testing

I am new to unit testing in vue 3 class component. I getting error says store is undefined. Here is my code in Home.vue file.
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import HelloWorld from "#/components/HelloWorld.vue";
import { useStore } from "vuex";
import { key } from "#/store";
import { ProductType } from "#/types/commonTypeDefinitions";
#Options({
components: {
HelloWorld,
},
})
export default class Home extends Vue {
store = useStore(key);
get getAllProducts() {
return this.store.state.products ? this.store.state.products.groups : null;
}
mounted() {
if (!this.store.state.products) {
this.store.dispatch("getJsonData");
}
}
goToDetail(item: ProductType) {
localStorage.setItem("productDetail", JSON.stringify(item));
this.store.commit("setProductDetail", item);
const name = item.name.replaceAll(" ", "-");
console.log(name);
this.$router.push("/detail/" + name);
}
}
</script>
I am using jest for testing. Please help me with this.Thanks.

How to disable scripts based on pathname in NEXT JS

I want to disable a chat widget on my page /embed/. The widget is loaded in _document.js. I am familiar useRoute or withRouter to disable a component. Example
import React, { Component } from "react";
import Link from "next/link";
import { withRouter } from "next/router";
class Footer extends Component {
render() {
const pathname = this.props.router.pathname;
if (pathname.startsWith("/embed/")) {
return null;
} else {
return (
<div className='footer container'>
My footer displayed on all pages expect on /embed/
</div>
);
}
}
}
export default withRouter(Footer);
But unfortunately this doesn't work in _document.js and keep getting internal error 500.
import Document, { Head, Main, NextScript } from "next/document";
import { GA_TRACKING_ID } from "../lib/gtag";
import WidgetChat from "../lib/WidgetChat";
import { withRouter } from "next/router";
class MyDocument extends Document {
render() {
const pathname = this.props.router.pathname;
if (pathname.startsWith("/embed/")) {
return null;
} else {
return (
<html>
<Head>
{/* Global Site Tag (gtag.js) - Google Analytics */}
<script
async
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
</Head>
<body>
<Main />
<NextScript />
<WidgetChat />
</body>
</html>
);
}
}
}
export default withRouter(MyDocument);
Any advice?
Many thanks
Frido

Polymer 3 Data Binding not updating on property set

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);

Decorators in Meteor 1.4

I am trying to understand how decorators work with Meteor 1.4. From what I read, this feature is supported.
Now, I am unsure how to actually implement it. From this blog, to decorate a class, I would require this code
export const TestDecorator = (target) => {
let _componentWillMount = target.componentWillMount;
target.componentWillMount = function () {
console.log("*** COMPONENT WILL MOUNT");
_componentWillMount.call(this, ...arguments);
}
return target;
}
Then use it as
import React, { Component } from 'react';
import { TestDecorator } from 'path/to/decorator.js';
#TestDecorator
export default class FooWidget extends Component {
//...
}
The code compiles, but nothing gets output when the component is being rendered.
What am I missing? How do I implement a decorator in Meteor? Is this the proper solution? What is the alternative?
Edit
I have tried this, and it still does not work
export const TestDecorator = (target) => {
console.log("*** THIS IS NOT EVEN DISPLAYED! ***");
target.prototype.componentWillMount = function () {
// ...
};
}
You are assigning your componentWillMount function to the class FooWidget instead of its prototype. Change that to target.prototype.componentWillMount = …. Besides, storing the previous componentWillMount is unnecessary in this case because it is undefined anyway.
Here is a full working example:
main.html
<head>
<title>decorators</title>
</head>
<body>
<div id="root"></div>
</body>
decorator.js
export const TestDecorator = (target) => {
console.log('Decorating…');
target.prototype.componentWillMount = function() {
console.log('Component will mount');
};
};
main.jsx
import React, { Component } from 'react';
import { render } from 'react-dom';
import { TestDecorator } from '/imports/decorator.js';
import './main.html';
#TestDecorator
class FooWidget extends Component {
render() {
return <h1>FooWidget</h1>;
}
}
Meteor.startup(function() {
render(<FooWidget/>, document.getElementById('root'));
});
.babelrc
{
"plugins": ["transform-decorators-legacy"]
}

Resources