Stencil Component Method definition - javascript

I am facing a problem developing a stencil.js web component. I have this error:
(index):28 Uncaught TypeError: comp.hideDataPanel is not a function
at HTMLDocument. ((index):28)
I want to integrate my stencil component to a bigger browserify project
Before doing that I am trying the following index.html with the stencil server-dev
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<title>Advanced SearchBar</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0">
<meta name="theme-color" content="#16161d">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta http-equiv="x-ua-compatible" content="IE=Edge"/>
<script src="/build/app.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <!-- Added -->
<link rel="manifest" href="/manifest.json">
</head>
<body>
<advanced-searchbar target_url="http://localhost:1234/results" target_id="pdbCode"></advanced-searchbar>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
let comp = document.getElementsByTagName('advanced-searchbar')[0];
comp.hideDataPanel(); // --> Does not work all the time
})
let comp = document.getElementsByTagName('advanced-searchbar')[0];
comp.addEventListener('clickedOnResult',function(e){
comp.hideDataPanel(); // --> Works Fine
})
</script>
</body>
</html>
Methods of my component appear to be unresolved at the DomContentLoaded event while, the same method is resolved at the asychronous user click event. Note that user can use .hideDataPanel() method thanks to #Method() inside typescript code (as shown here: https://stenciljs.com/docs/decorators)
For the purpose of future integration I would like to bind my stencil component method at the startup of my web application.
How do i do that ?
Shall i wait for a particular event in the page or component lifecycle for component method to be resolved ?
Thank you in advance

Maybe my understanding is wrong and I've never used the event, but according to mdn it sounds like the "DomContentLoaded" event is fired when the HTML DOM has been loaded. I guess that's too early for our webcomponents to be loaded, as they're coming from JS.
So you'd need to register on an event that fires when the whole page (including JS) has been loaded which I guess is window.load.
You could also add an #Event() within the stencil component you're trying to access after it has been loaded and .emit() that event within componentDidLoad(). Stencil events seem to propagate upwards in your dom-tree, so they'll eventually reach document.
So you can register and event listener on that event like:
document.addEventListener('myCustomStencilEvent', handlerFunction);

Related

How to embed external js code into React component

I am working in a project, where I have to use a widget_api code provided by RIPE Stat, to integrate the graphical widget into a React component.
For example in HTML5, I would have done it like this, and it works fine.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Widget</title>
</head>
<body>
<script src="https://stat.ripe.net/widgets/widget_api.js"></script>
<div class="statwdgtauto">
<script>
ripestat.init("rir-geo",{"resource":"80.12.67.0/24"},null,{"size":"500","disable":["controls"]})
</script>
</div>
</body>
</html>
While doing research I found this react-safe library which allows to do the same thing in React
Here is the code of my index file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>MyWHOIS</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="bd-home">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./js/polyfill.js" defer ></script>
<script src="https://stat.ripe.net/widgets/widget_api.js" async></script>
</body>
</html>
And the component that I created for...
import React, { Component } from 'react';
import Safe from "react-safe";
export default class EmbedComponent extends Component {
constructor(props){
super();
/*
const widget_api = document.createElement("script");
widget_api.src = "https://stat.ripe.net/widgets/widget_api.js";
widget_api.async = true;
document.body.appendChild(widget_api);
*/
}
render() {
const params1 = {"family":4,"warnings":1,"delegated":1,"resource":"FR"};
const params2 ={"resource":"127.0.0.1/24"};
const control = {"size":"500","disable":["controls"]};
return (
<div>
<h4>Include Embed</h4>
<div className="statwdgtauto">
<Safe.script>
{`ripestat.init("rpki-by-country",${params1},null,${control})`}
</Safe.script>
<Safe.script>
{`ripestat.init("rir-geo",${params2},null,${control})`}
</Safe.script>
</div>
</div>
)
}
}
I have errors, when I include the file with the tag with async or defer, I have a cors error
A parser-blocking, cross site (i.e. different eTLD+1) script, , is invoked via document.write. The network request for this script MAY be blocked by the browser in this or a future page load due to poor network connectivity. If blocked in this page load, it will be confirmed in a subsequent console message. See for more details.
And without defer or async I have this problem of writing on the document
Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened. widget_api.js:90
Define a new state isLoading
constructor(props) {
super(props);
this.state = {
isLoading: true,
};
}
componentDidMount() {
const widget_api = document.createElement("script");
widget_api.src = "https://stat.ripe.net/widgets/widget_api.js";
widget_api.async = true;
// Wait for the onload here
widget_api.script.onload = () => setState({isLoading: false})
document.body.appendChild(widget_api);
}
and on you render function do something during the isLoading phase (you can display a loader instead of your current implementation)
The issue here is that you are locked into an implementation detail of ripeStat which prevents use of it in some of the modrn frameworks, react for instance.
If you look at the ripeStat code, here for instance, https://stat.ripe.net/widgets/widget_api.js you see that it has calls to document.write.
document.write would not kick in, in a deferred situation and is expected.Check here https://developer.mozilla.org/en-US/docs/Web/API/Document/write#notes and you will find the same error that you are seeing.
Once the document has been read, parsed and closed, document.write calls will not be entertained.
I think this is a limitation of the ripeStat library. What they should use instead is
const script = document.createElement('script');
script.src = 'https://stat.ripe.net/widgets/widget_api.js';
document.head.appendChild(script);
which will ensure that it works across the board correctly.
Another option, which is probably a better one is for an npm package from ripeStat which will help the moderm frameworks.
The only way it will work correctly right now within your react application is that the script has to be present before the browser starts parsing your HTML and interpreting it.

Using NuxtJS for Zoho CRM Widgets

Zoho CRM has something called Widgets to extend it's functionality. Using the widgets feature, you can directly embed UI components in a CRM and use the data form a third-party application to perform actions as per requirement.
A widget is basically an HTML file which is loaded in a popup once a custom button is fired. To store/retrieve data from Zoho CRM you need to load jQuery and their JS SDK in the HTML file.
The most basic HTML file looks like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://live.zwidgets.com/js-sdk/1.0.5/ZohoEmbededAppSDK.min.js"></script>
<title>Document</title>
</head>
<body>
<script>
ZOHO.embeddedApp.on("PageLoad",function(data) {
console.log(data);
//Custom Business logic goes here
});
ZOHO.embeddedApp.init();
</script>
</body>
</html>
In this file console.log(data) will log information about the page on which the widget is fired. On for instants a Lead page, it will log information about that lead, like the id.
Functions to store/retrieve data need to be used where it says //Custom Business logic goes here.
The code for getting all Leads in this widget looks like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://live.zwidgets.com/js-sdk/1.0.5/ZohoEmbededAppSDK.min.js"></script>
<title>Document</title>
</head>
<body>
<script>
ZOHO.embeddedApp.on("PageLoad",function(data) {
ZOHO.CRM.API.getAllRecords({Entity:"Leads"})
.then(function(data){
console.log(data)
})
});
ZOHO.embeddedApp.init();
</script>
</body>
</html>
Because I need to create multiple Zoho Widgets and use the same Vue Components on every Widget I thought of using NuxtJS. I successfully create the Vue Components, but I have no clue how to incorporate Zoho's JS SDK.
Is there anybody who can give me some suggestions how to make this work? Thanks!
why not having the SDK function in a external file from your html, where you invoke all the methods from the SDK api?
simply import your SDK to the header of the html.
hey i found this solution on the github issues page that actually worked. hopfully it will help you.
under data you can define your script as a string somthing like this:
<template>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div v-html="scripts"></div>
</template>
<script>
export default {
data: () => ({
scripts: "<script src='https://www.google.com/recaptcha/api.js'><\/script>"
})
}
</script>
and then just add a div tag in your page with v-html="script"
i tried it and it worked fine. but i don't feel it's the best practice. hopfully this will help you.
the source and all the discussion on github: https://github.com/nuxt/nuxt.js/issues/2000

React Native WebView handle or detect event from built-in script

i cannot create listener in my html-script to because the way to navigate the html is using inside/built in script from header source
right now when the snap-html is close, it would return blank and i have to manually use back button, and when the redirect finish, I use render count because i have no idea how to detect the inside built in event
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript"
src="https://app.sandbox.midtrans.com/snap/snap.js"
data-client-key="Client key"></script>
</head>
<body>
</body>
it's like opening snap, but i need to detect when it closes/ accepting redirect finish, so i could navigate back when the snap-html is forced to close/finish.

How can i reference body tag in vue js "right way"

I have this code in my layout file with my appSearch component:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="css/app.css" />
<title>title</title>
</head>
<body>
<div id ='app' >
<app-search ref='search'></app-search>
#include('layouts/header')
#yield('content')
</div>
<script type="text/javascript" src="js/app.js" ></script>
</body>
</html>
In the component I want to reference body tag and set it some class that will apply overflow: hidden. However vue can't be used outside div. The first thing that came to my mind is document.querySelector. But it's not 'vue-ish' way of doing this. Is there any way around it?
You cannot access the body tag with Vue, but with regular JavaScript, as you mentioned. If you are manipulating the DOM you need to make sure, that the template is rendered and you can then access the HTML element e.g. in mounted(), but the <body> is already rendered at the time, your Vue app is running. So you can just use document.querySelector (or more easily just document.body as mentioned in the comments) to access the <body> tag.
It is often not needed to access the body tag directly within a Vue app. You can usually achieve the same thing in a Vueish way. In your case, you could just put a class onto to the highest element of your Vue app (the App.vue) and let this element act as your <body>. Then, you have full control over the template with Vue.
If you are using Vue 3 and you need to have a managed element outside of the highest element of your Vue app, you can also use the new <teleport> tag. This is e.g. used for modal overlays, but it won't help you to add a class to the body element. See the docs for <teleport>.

Webpack, React & Babel, not rendering DOM

I finally after hours made my Webpack, React & Babel build setup, but when I try to just run a simple render Hello World it doesn't output anything in the DOM.
Here is my code.
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('content')
);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<script src="bundle.js"></script>
</head>
<body>
<div id="content"></div>
</body>
</html>
And when I watch my bundle.js I can see it imports all the React & ReactDOM I need to run the render.
The test I'm running is from: https://facebook.github.io/react/docs/getting-started.html.
Getting this console error: Uncaught Error: Invariant Violation: _registerComponent(...): Target container is not a DOM element.
Your bundle.js file is being loaded before the DOM has had time to load. This means that it won't be able to find your <div id="content"> when you have asked for it.
Try putting the script loader before the end of the body.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
</head>
<body>
<div id="content"></div>
<script src="bundle.js"></script>
</body>
</html>
When browsers find a <script> tag, they pause execution while they download the file. This means that your DOM has only partially rendered when your bundle.js script begins executing. So you would have essentially been passing undefined to the ReactDOM.render method.
If you have not had this issue in the past, perhaps you have been using the jQuery.ready event handler, which waits for the DOM to be loaded before executing.
bundle.js is executed when the content element isn't yet parsed an created. Just move your script element to the end of the markup.

Categories

Resources