VueJS components - javascript

I want to make use of VueJS components to clean up my scripts. Therefore I have a component for each page I am loading in into Laravel. Everything works fine so far. But I have issues transfering my current script logic to a component.
This is the current script setup importing the to be used components:
main.js
import HomeView from './components/HomeView.vue';
import IncidentView from './components/IncidentView.vue';
window.app = new Vue({
el: '.content',
components: {
HomeView, IncidentView
},
methods: {
// Init GIS
init: function() {
// Initialize GIS Map
initGISMap(this.$els.map);
},
}
(...)
}
Key for me is the window.app = new Vue... part. I make use of google maps and therefore when the page is loaded it searches for an app.init method. This is part of the script I am loading within the blade template:
<!DOCTYPE html>
<html>
<body class="hold-transition skin-blue sidebar-mini">
<section class="content-header">
<h1>
#yield('page_title')
<small>#yield('page_description')</small>
</h1>
</section>
<!-- Main content -->
<section class="content">
#if (isset($vueView))
<component is="{{ $vueView }}">
#endif
#yield('content')
</component>
</section>
<!-- /.content -->
<script src="https://maps.googleapis.com/maps/api/js?key=KEY&libraries=places&callback=app.init" async defer></script>
</body>
</html>
The individual pages (where I create for each a module in Vue) look like this:
#extends('app', ['vueView' => 'home-view'])
#section('page_title', 'title')
#section('page_description', 'title')
#section('content')
content
#endsection
By defining the vueView variable, the correct module I am importing in my script is used.
The goal is to use HomeView component as the main google maps view. And the other components for different pages I load when clicking the corresponding link in my theme. At the end, I do not want to have all VueJS code in one script. Therefore the models.
When I transfer all the content of this current JS file, I get an error complaining that app.init is not a function. The component looks like this:
<script>
export default {
data: {
// SEARCH
addressComponents: '',
autocompletePlace: '',
autocompleteAddress: '',
(...)
}
How do I modify my component in a way, that the app.init load would still work?

Someone has already mentioned GuillaumeLeclerc/vue-google-maps which is available on npm as vue-google-maps, but be warned that that version only works with vue 1.x
If you are using v2, look at this great vue2 fork xkjyeah/vue-google-maps - it's available as vue2-google-maps on npm.
The API is straightforward and doesn't diverge far from the v1 version, and the repository is much more active than its upsteam counterpart.
It really made my vue map work a lot more painless than rolling my own, which is what I was initially doing.
I hope that helps

Maybe you should use, or at least study the code, of a package that does that.
For example, you can check vue-google-maps on Github : https://github.com/GuillaumeLeclerc/vue-google-maps/
It defines a whole lot of Components related to Google Maps.

If I understand you right, you have a Google Maps script which, when loaded, calls window.app.init, right?
Does your Vue component have an init() method (none is shown above)? If there was one, it would need to be at app.methods.init() remember for VueJS, standard callable methods live under methods key.
Alternatively: you don't mention if your app.init is part of the Vue component or not. If its not it would appear that your app.init function is being overridden because you are redefining window.app as your vue component.
Lastly, if your init is part of Vue, is your google maps fn callback calling this init() before the Vue is loaded and therefore no such method exists (yet)?

Related

How to use mailerlite popups in a Next.js application

I'm integrating a mailerlite popup for a client's next.js project, and I'm having a difficult time converting the JavaScript snippets into the jsx required to make the popups function properly. On first load it seems to work just fine, but on relaod I'm getting the following error.
window is not defined
I've encountered the issue while dealing with DOM manipulation, but in this case, judging from the code in the snippet, I need the window object.
Install the following snippet of Javascript on every page of your website right before the closing tag.You only need to add this snippet once, even if you plan to have a few different webforms.
<!-- MailerLite Universal -->
<script>
(function(m,a,i,l,e,r){ m['MailerLiteObject']=e;function f(){
var c={ a:arguments,q:[]};var r=this.push(c);return "number"!=typeof r?r:f.bind(c.q);}
f.q=f.q||[];m[e]=m[e]||f.bind(f.q);m[e].q=m[e].q||f.q;r=a.createElement(i);
var _=a.getElementsByTagName(i)[0];r.async=1;r.src=l+'?v'+(~~(new Date().getTime()/1000000));
_.parentNode.insertBefore(r,_);})(window, document, 'script', 'https://static.mailerlite.com/js/universal.js', 'ml');
var ml_account = ml('accounts', '912433', 'd5p1f7l9g0', 'load');
</script>
<!-- End MailerLite Universal -->
I've placed this code in my Layout wrapper. As previously stated, it works fine on first load, but as soon as the user navigates to a new page above error shows up.
PS I found an old question regarding this topic here, but it's old and not quite relevant to my situation. I need to figure out how to convert the above snippet for nextjs. Any help at all would be appreciated.
This approach treats the MailerLite universal tag as its own <script> hosted on your site's domain.
Add a NextJS custom document.
Create a JavaScript file containing the MailerLite universal tag code in ./public. I put mine in ./public/scripts/ml.js.
Add a <script> tag loading #2 in your custom _document.js file:
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head>
<script async src="/scripts/ml.js"></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
Everything worked as intended from there! (Caveat: I'm only using embedded forms).

Loading a var from window global into React App not working

I know there are lots of answers to this. My purpose is to have a js file that I can use terraform to inject into a docker image so I can easily change the client logo during deployment and have a single docker image for my react ui. I have tried the solutions in the following answers:
how-can-i-pass-a-variable-from-outside-to-a-react-app
how-to-include-external-javascript-in-react
external-javascript-is-not-working-in-react-js
react-accessing-a-var-from-a-script-in-a-component
The simplest method seems to be to import a script in head in index.html, with the var declared therein. I have the following in my index.html and env.js is in the static folder where other js files (plotly) are loaded successfully.
...
<script src="http://localhost:3000/env.js"></script>
</head>
I have the following in env.js
window.logo_file = '/images/demo.jpg';
Then in my React component I have:
<div className={'clientsLogo'}>
<img
src={window.logo_file}
alt={'client-logo'}
height={61}
style={{ verticalAlign: 'bottom' }}
/>
</div>
I also tried adding export default logo_file using logo_file = '/images/demo.jpg'; in env.js.
I tried assigning the window.logo_file to a const prior to using it.
But the imported script does not have it's variable added to the global window object and it escapes me why not.
The answer is that the env.js, i.e. the file with your window global variable declared, has to go at the top of the head section of your index.html file to be loaded before the react app is loaded.

Multiple Vue instances bind to parent & child elements

I would like to ask for best setup of Vue instances (components), when I do not want use SPA.
My HTML looks like:
<html>
<div id="layout">
<div>Some Layout based content...</div>
<div id="homepage">
<simple-form-component></simple-form-component>
</div>
</div>
<script type=text/javascript src="homepage.js"></script>
<script type=text/javascript src="layout.js"></script>
</html>
Currently I have simple example ot two .js files:
layout.js
homepage.js
// layout.js
import Vue from 'vue/dist/vue.js'
new Vue({
el: '#layout',
});
// homepage.js
import Vue from 'vue/dist/vue.js';
import SimpleForm from '../../../../components/SimpleForm.vue';
new Vue({
el: '#homepage',
components: {
SimpleForm,
},
});
It seems to work fine, but I am suspicious that this is not 100% correct, due to console error:
[Vue warn]: Unknown custom element: - did you register
the component correctly? For recursive components, make sure to
provide the "name" option.
(found in )
Do you have any ideas/experience how to setup my scenario correctly?
PS: I would like to have my JS split into different files, managed by webpack and distributed by backend.
Thank you,
Mario
I've found workaround v-pre, my HTML will change the line: <div id="homepage" v-pre>. One small issue is, that I will not see my components in Vue Devtools.

Is it possible to create a web component anyone can use without installation?

I'm just getting started with web components, and if I understand correctly, the point is for them to be reusable by anyone. Is it possible to create a component that can be used by anyone simply by adding a piece of html to their static site (similar to how JavaScript widgets are added, simply by copy-pasting a few lines of code), or does it need to be installed by someone? Or is this not an intended use case of web components?
Yes. A Web Component is a kind of "Javascript widget".
Typicially, you define a Web Component in a Javascript file.
You can then include it in any HTML with a <script> element.
Example with a minimal custom element called <hello-world>:
In hello-world.js:
customElements.define( 'hello-world', class extends HTMLElement {
constructor() {
super()
this.attachShadow( {mode: 'open' })
.innerHTML = 'Hello World!'
}
} )
In your main page index.html:
<html>
<head>
<!-- import the web component -->
<script src="hello-world.js">
</head>
<body>
<!-- use the new element -->
<hello-world></hello-world>
</body>
</html>
Note: alternately, one could also copy-paste the Javascript code that defines the custom element to its main HTML page.

Load multiple components to js file but use only the required one(s)

I'm learning to use the svelte framework to make components. But I haven't been able to make an instance of only one component from those I have loaded into the js file.
So, this is my html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script src="./build/ReviewPanelComponent.js"></script>
</body>
</html>
My js input file (where I import the components):
import ReviewPanel from './ReviewPanel.html';
const reviewPanel = new ReviewPanel({
target: document.querySelector( 'body' ),
data:
JSON.parse(localStorage.getItem('rooms'))
});
export default {
components: {
reviewPanel,
}
}
And the html for the component:
<style>
#main{
background-color:white;
}
h3,
h4,
h5{
font-weight: bold;
}
</style>
<div class="container">
<div class="row">
<div id="main" class="col-xs-12">
<div id="title">
{{#if BookedRooms.length > 0}}
{{#each BookedRooms as br}}
<h3>{{br.Title}}</h3>
{{#each br.PossibleRateTypes as prt}}
<h4>{{prt.UUID}}</h4>
{{/each}}
{{/each}}
{{/if}}
</div>
</div>
</div>
</div>
So, at this moment, it loads the component, because it has an instance on the js input file. The thing is, at this moment, if I want a second instance I have to create one of the js file itself. I was thinking more like just importing all the components I want in the js file and in the html file where I want to use them I would just call them.
Is this possible. I've been reading the docs but I haven't found what I want.
EDIT: I forgot to say that I'm using webpack, also. Hence the input and output files. I managed to use multiple components. The solution I've found was to just do this on my js input file:
import ReviewPanel from './ReviewPanel.html';
window.ReviewPanel=ReviewPanel;
window.ReviewPanel2=ReviewPanel;
I put the imported object in a global variable so I can call it where I want it, like so:
<div id="reviewPanel"></div>
<script type="text/javascript">
// Creates the review panel component
window.ReviewPanel = new ReviewPanel({
target: document.querySelector('#reviewPanel'),
data:
JSON.parse(localStorage.getItem('rooms'))
});
</script>
Yet, although I have this solution, my expectation would be import it only once in the js input file, without having to rely on global objects, and then just call it where I need it like so:
// And passing the parameters
<ReviewPanel />
Although I haven't seen much on React.js that's how I generally see programmers doing this. I was expecting the same. Still, as I have other things that need to be done, I'll leave it like this for now, to be optimized later.
Still, if anyone knows how to do this I'm all ears, as I'm new to Svelte and there's not much info on it, apart from the docs, which I consider a bit confusing on same parts.
If I understood correctly, to achieve your goal you could use a module bundler such as Rollup or webpack with their respective Svelte plugin and loader. In this perspective, the easiest way to start is to use a template based on one or the other.

Categories

Resources