How to extend Polymer.AppNetworkStatusBehavior into custom Mixin in Polymer2? - polymer-2.x

In Polymer 1.0, I have the following code snippet:-
CustomBehaviorImpl = {
properties: {
// Define property here...
// ...
},
// Other custom behaviors definition
// ...
};
CustomBehavior = [
Polymer.AppNetworkStatusBehavior,
CustomBehaviorImpl,
];
How would I do it in Polymer 2.0 to create a CustomMixin class.

If you separate the mixins into its own file you can reference them as an Html Import dependency whenever composing an element that uses mixins.
Just extend your Polymer class with your custom behaviours.
I think this question has been answered already here on stackoverflow.
Take a look at this one:
Applying Behaviors with JS Mixins in Polymer 2
Example
This is my CustomMixin.html
<script>
// CustomMixin provides the _customMixin function
var CustomMixin = function(superClass) {
return class extends superClass {
_customMixin() {
}
}
}
</script>
This is my Polymer Element where I make use of the CustomMixin.
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../CustomMixin/CustomMixin.html">
<dom-module id="my-element">
<template>
<style>
:host {
display: block;
}
</style>
</template>
<script>
class MyElement extends CustomMixin(Polymer.Element) {
static get is() { return 'my-element'; }
static get properties() {
return {
};
}
ready() {
super.ready();
}
}
window.customElements.define(MyElement.is, MyElement);
</script>
</dom-module>
I actually never tested that code but I believe this should be the way to implement a custom mixin.

Related

vue3 devtools: How to access component methods using Options API?

In devtools with Vue2 I can access my components methods by selecting a component in vue devtools and then type $vm0.myMethod() in the console.
export default {
// ...
methods: {
myMethod() {
console.log('hello');
},
}
// ...
}
Now I'm using Vue3 with options API. How can I still access my component methods?
Given that the methods are specified in methods for options API: {
<script>
export default {
expose: ['publicMethod'],
methods: {
privateMethod() {...}
publicMethod() {...}
},
}
</script>
For composition API, it's presumed that component methods are returned from setup function:
<script>
export default {
setup(props, ctx) {
function justLocalFunction() {...}
function privateMethod() {...}
function publicMethod() {...}
ctx.expose({ publicMethod })
return { privateMethod, publicMethod };
}
}
</script>
This is implicitly done with script setup syntax:
<script setup>
export default {
setup(props, ctx) {
{
// Block scope isn't magically transformed
function justLocalFunction() {...}
}
function privateMethod() {...}
function publicMethod() {...}
defineExpose({ publicMethod })
}
}
</script>
Here only publicMethod is available on component ref. While privateMethod and publicMethod are exposed on internal component instance, which can be accessed as getCurrentInstance().proxy.privateMethod, etc inside setup block, and as devtools $vm.proxy via Vue devtools.
If there's a chance that justLocalFunction needs to be accessed later, returning it from setup function will make it easier for testing and debugging.

Vue3 custom element into Vue2 app using external framework

I have an application written in Vue2 which is not really ready to be upgraded to Vue3. But, I would like to start writing a component library in Vue3 and import the components back in Vue2 to eventually make the upgrade once it's ready.
Vue 3.2+ introduced defineCustomElement which works nicely but once I use a framework in the Vue3 environment (for example Quasar) that attaches to the Vue instance, it starts throwing errors in the Vue2 app, possibly because the result of defineCustomElement(SomeComponent) tries to use something from the framework that should be attached to the app.
I've thought about extending the HTMLElement and mounting the app on connectedCallback but then I lose the reactivity and have to manually handle all props/emits/.. like so:
class TestQuasarComponentCE extends HTMLElement {
// get init props
const prop1 = this.getAttribute('prop1')
// handle changes
// Mutation observer here probably...
const app = createApp(TestQuasarComponent, { prop1 }).use(Quasar)
app.mount(this)
}
customElements.define('test-quasar-component-ce', TestQuasarComponentCE);
So finally the question is - is it possible to somehow combine the defineCustomElement with a framework that attaches to the app?
So, after a bit of digging, I came up with the following.
First, let's create a component that uses our external library (Quasar in my case)
// SomeComponent.vue (Vue3 project)
<template>
<div class="container">
// This is the quasar component, it should get included in the build automatically if you use Vite/Vue-cli
<q-input
:model-value="message"
filled
rounded
#update:model-value="$emit('update:message', $event)"
/>
</div>
</template>
<script setup lang="ts>
defineProps({
message: { type: String }
})
defineEmits<{
(e: 'update:message', payload: string | number | null): void
}>()
</script>
Then we prepare the component to be built (this is where the magic happens)
// build.ts
import SomeComponent from 'path/to/SomeComponent.vue'
import { reactive } from 'vue'
import { Quasar } from 'quasar' // or any other external lib
const createCustomEvent = (name: string, args: any = []) => {
return new CustomEvent(name, {
bubbles: false,
composed: true,
cancelable: false,
detail: !args.length
? self
: args.length === 1
? args[0]
: args
});
};
class VueCustomComponent extends HTMLElement {
private _def: any;
private _props = reactive<Record<string, any>>({});
private _numberProps: string[];
constructor() {
super()
this._numberProps = [];
this._def = SomeComponent;
}
// Helper function to set the props based on the element's attributes (for primitive values) or properties (for arrays & objects)
private setAttr(attrName: string) {
// #ts-ignore
let val: string | number | null = this[attrName] || this.getAttribute(attrName);
if (val !== undefined && this._numberProps.includes(attrName)) {
val = Number(val);
}
this._props[attrName] = val;
}
// Mutation observer to handle attribute changes, basically two-way binding
private connectObserver() {
return new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (mutation.type === "attributes") {
const attrName = mutation.attributeName as string;
this.setAttr(attrName);
}
});
});
}
// Make emits available at the parent element
private createEventProxies() {
const eventNames = this._def.emits as string[];
if (eventNames) {
eventNames.forEach(evName => {
const handlerName = `on${evName[0].toUpperCase()}${evName.substring(1)}`;
this._props[handlerName] = (...args: any[]) => {
this.dispatchEvent(createCustomEvent(evName, args));
};
});
}
}
// Create the application instance and render the component
private createApp() {
const self = this;
const app = createApp({
render() {
return h(self._def, self._props);
}
})
.use(Quasar);
// USE ANYTHING YOU NEED HERE
app.mount(this);
}
// Handle element being inserted into DOM
connectedCallback() {
const componentProps = Object.entries(SomeComponent.props);
componentProps.forEach(([propName, propDetail]) => {
// #ts-ignore
if (propDetail.type === Number) {
this._numberProps.push(propName);
}
this.setAttr(propName);
});
this.createEventProxies();
this.createApp();
this.connectObserver().observe(this, { attributes: true });
}
}
// Register as custom element
customElements.define('some-component-ce', VueCustomElement);
Now, we need to build it as library (I use Vite, but should work for vue-cli as well)
// vite.config.ts
export default defineConfig({
...your config here...,
build: {
lib: {
entry: 'path/to/build.ts',
name: 'ComponentsLib',
fileName: format => `components-lib.${format}.js`
}
}
})
Now we need to import the built library in a context that has Vue3, in my case index.html works fine.
// index.html (Vue2 project)
<!DOCTYPE html>
<html lang="">
<head>
// Vue3
<script src="https://cdn.jsdelivr.net/npm/vue#3/dist/vue.global.prod.js"></script>
// Quasar styles
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/quasar#2.4.3/dist/quasar.prod.css" rel="stylesheet" type="text/css">
// Our built component
<script src="path/to/components-lib.umd.js"></script>
</head>
...rest of your html...
</html>
Now we are ready to use our component within our Vue2 (or any other) codebase same way we are used to with some minor changes, check comments below.
// App.vue (Vue2 project)
<template>
<some-component-ce
:message="message" // For primitive values
:obj.prop="obj" // Notice the .prop there -> for arrays & objects
#update:message="message = $event.detail" // Notice the .detail here
/>
</template>
<script>
export default {
data() {
return {
message: 'Some message here',
obj: { x: 1, y: 2 },
}
}
}
</script>
Now, you can use Vue3 components in Vue2 :)

Is it possible to pass data from Polymer component to Vue component?

The below code is what I'd like to do but it currently doesn't work. I'm trying to start building Vue components inside my Polymer app as a way to slowly migrate off Polymer.
I've been able to get a Vue component working inside my Polymer app, but I'm stuck on how to pass data from the Polymer component to the Vue component. Ideally, what I'd like to do is pass a Polymer property into the Vue component like I'm doing with testValue below (although the code below doesn't work)
Any pointers are greatly appreciated, thank you!
<dom-module id="part-input-view">
<template>
<style include="part-input-view-styles"></style>
<div id="vueApp">
<vue-comp id="test" test$="[[testValue]]"></vue-comp>
</div>
</template>
<script>
class PartInputView extends Polymer.Element {
static get is() { return 'part-input-view'; }
constructor() {
super();
}
static get properties() {
return {
testValue: 'This is working!'
};
}
ready() {
super.ready();
Vue.component('vue-comp', {
props: ['test'],
template: '<div class="vue-comp">{{test}}</div>'
})
const el = this.shadowRoot.querySelector('#vueApp')
let vueApp = new Vue({
el
});
}
}
</script>
</dom-module>
Yes, it's possible. Your code would've worked had it not been for your [incorrect] property declaration. You should see this error in the console:
element-mixin.html:122 Uncaught TypeError: Cannot use 'in' operator to search for 'value' in This is working!
at propertyDefaults (element-mixin.html:122)
at HTMLElement._initializeProperties (element-mixin.html:565)
at new PropertiesChanged (properties-changed.html:175)
at new PropertyAccessors (property-accessors.html:120)
at new TemplateStamp (template-stamp.html:126)
at new PropertyEffects (property-effects.html:1199)
at new PropertiesMixin (properties-mixin.html:120)
at new PolymerElement (element-mixin.html:517)
at new PartInputView (part-input-view.html:17)
at HTMLElement._stampTemplate (template-stamp.html:473)
In Polymer, string properties with a default value can only be declared like this:
static get properties() {
return {
NAME: {
type: String,
value: 'My default value'
}
}
}
There is no shorthand for this. You might've confused the shorthand for the uninitialized property, which would be:
static get properties() {
return {
NAME: String
}
}
If you fix that bug, you'll notice your code works...
class PartInputView extends Polymer.Element {
static get is() { return 'part-input-view'; }
static get properties() {
return {
testValue: {
type: String,
value: 'This is working!'
}
};
}
ready() {
super.ready();
Vue.component('vue-comp', {
props: ['test'],
template: '<div class="vue-comp">{{test}}</div>'
})
const el = this.shadowRoot.querySelector('#vueApp')
let vueApp = new Vue({
el
});
}
}
customElements.define(PartInputView.is, PartInputView)
<head>
<script src="https://unpkg.com/vue#2.6.10"></script>
<base href="https://cdn.rawgit.com/download/polymer-cdn/2.6.0.2/lib/">
<script src="webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<part-input-view></part-input-view>
<dom-module id="part-input-view">
<template>
<style include="part-input-view-styles"></style>
<div id="vueApp">
<vue-comp id="test" test$="[[testValue]]"></vue-comp>
</div>
</template>
</dom-module>
</body>

How to access shadowFunction of element1 form shadowRoot of other element2 in polymer 2.0

This is my html structure,
//--this is index.html--//
<body>
<my-app></my-app>
</body>
//--this is my-app.html--//
<dom-module id="my-app">
<my-page1></my-page1>
<my-page2></my-page2>
</dom-module>
//--this is my-page1.html--//
<dom-module id="my-page1">
<script>
class MyPage1 extends Polymer.Element {
static get is() { return 'my-page1'; }
covert_data(){
alert("in covert_data");
}
}
window.customElements.define(MyPage1.is, MyPage1);
</script>
</dom-module>
//--this is my-page2.html--//
<dom-module id="my-page2">
<paper-button on-tap="addData">Save</paper-button>
<script>
class MyPage1 extends Polymer.Element {
static get is() { return 'my-page2'; }
addData(){
var host = document.querySelector('my-app').shadowRoot.querySelector('my-page1');
host.covert_data();
}
}
window.customElements.define(MyPage2.is, MyPage2);
</script>
</dom-module>
<my-page1></my-page1> has 1 method called covert_data(). I am trying below code.
way1:
var host = document.querySelector('my-app').shadowRoot.querySelector('my-page1');
host.covert_data();
This giving me error host.covert_data is not a function
way2:
document.querySelector('my-page1').covert_data();
This giving me error Cannot read property 'covert_data' of null
How to call that covert_data() method in <my-page2></my-page2> in
polymer 2.0
Before starting with the solution i would like to give you a tip.
Please make sure that you post a working piece of code.
It's been quite a few months since i worked Polymer and it took me 15-20 minutes just to get your code working. An in that 15-20 minutes i thought about leaving it lot of times.
Posting a working code will help people on community to help you better.
Now to solution
I found lot of issues in your code. I don't know which were because of writing incomplete code in SO and which genuine, so i'll point them all.
script tag and registration missing for my-app.
constructor and Super missing for all the elements.
template tag is missing for shadowDom.
I don't know why your way 1 is not working, it's working fine for me. I can only guess it might be because of missing constructor, but way 2 will not work and my-page1 is not present directly in document. It's part of shadowRoot of my-app.
Below is the working snippet of your code. I've changed on-tap to on-click to avoid importing gesture events files.
<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<!-- my-app element -->
<dom-module id="my-app">
<template>
<my-page1></my-page1>
<my-page2></my-page2>
</template>
<script>
class MyApp extends Polymer.Element {
static get is() {
return 'my-app'
}
constructor() {
super();
}
}
window.customElements.define(MyApp.is, MyApp);
</script>
</dom-module>
<!-- page1 element -->
<dom-module id="my-page1">
<script>
class MyPage1 extends Polymer.Element {
static get is() {
return 'my-page1';
}
constructor() {
super();
}
covert_data() {
alert("in covert_data");
}
}
window.customElements.define(MyPage1.is, MyPage1);
</script>
</dom-module>
<!-- page2 element -->
<dom-module id="my-page2">
<template> <div on-click="addData">Save</div></template>
<script>
class MyPage2 extends Polymer.Element {
static get is() {
return 'my-page2';
}
constructor() {
super();
}
addData() {
var host = document.querySelector('my-app').shadowRoot.querySelector('my-page1');
host.covert_data();
}
}
window.customElements.define(MyPage2.is, MyPage2);
</script>
</dom-module>
<!-- calling element -->
<my-app></my-app>
In page1.html :
this.dispatchEvent(new CustomEvent('upway-func', {detail: {op:"Optionally I can send some data"}}));
In myApp.html define id to child element in order to call a function and on-upway-func event :
<my-app>
<page1 on-upway-func="callPage2Func"></page1>
<page2 id="child2"></page2>
</my-app>
...
callPage2Func(op){
console.log(op)//Optionally I can send some data
this.$.child2.covert_data(op);
}

How to call a function in polymer 2.0 on button onclick or anchor tag click?

I am seeing functionality like Compute function where i can use a function to compute same thing and then retrun something to that textcontent area but how to use them on button onclick or anchor onclick .
for example :
<dom-module id="x-custom">
<template>
My name is <span>[[_formatName(first, last)]]</span>
</template>
<script>
class XCustom extends Polymer.Element {
static get is() {return 'x-custom'}
static get properties() {
return {
first: String,
last: String
}
}
_formatName(first, last) {
return `${last}, ${first}`;
}
}
customElements.define(XCustom.is, XCustom);
</script>
</dom-module>
In this case the _formatName is manipulated in and we get resultant in html.
But how to to in button onclick and
So that i can manipulate for send some http request .
Also manipulate some data inside the function .
Polymer 2.0
I got the answer . First of all for two way binding you also check the polymer 1.0 doc for data binding that is in brief .
And then you can read polymer 2.0 doc but i don't see more for data binding event there.
I will say read both.
https://www.polymer-project.org/1.0/docs/devguide/templates
https://www.polymer-project.org/2.0/docs/devguide/templates
This have example for custom event .
https://www.polymer-project.org/2.0/docs/devguide/events.html#custom-events
<dom-module id="x-custom">
<template>
<button on-click="handleClick">Kick Me</button>
</template>
<script>
class XCustom extends Polymer.Element {
static get is() {return 'x-custom'}
handleClick() {
console.log('Ow!');
}
}
customElements.define(XCustom.is, XCustom);
</script>
</dom-module>
Also if you want argument to pass to the function you can do this.
<dom-module id="x-custom">
<template>
<button on-click="handleClick" data-args="arg1,arg2">Kick Me</button>
</template>
<script>
class XCustom extends Polymer.Element {
static get is() {return 'x-custom'}
handleClick(e) {
console.log('Ow!');
console.log(e.target.getAttribute('data-args'));
// now you got args you can use them as you want
}
}
customElements.define(XCustom.is, XCustom);
</script>
</dom-module>