I feel like this should be a very trivial thing to accomplish, yet I've been struggling for too long now. I'm trying to load a static minified script in my vue project.
This is my project structure:
project
-- public
index.html
-- src
App.vue
main.js
-- static
p5.min.js
vanta.waves.min.js
I'm trying to follow the setup as instructed here:
<script src="three.r92.min.js"></script>
<script src="vanta.waves.min.js"></script>
<script>
VANTA.WAVES('#my-background')
</script>
Since I'm only using the script in one component, I would like to load it there instead of globally including it in the index.html file.
export default {
...
methods: {
animate() {
const p5Script = document.createElement('script')
p5Script.async = true;
p5Script.defer = true;
p5Script.id = 'p5'
p5Script.src = '#/static/p5.min.js';
document.head.appendChild(p5Script);
const vantaScript = document.createElement('script')
vantaScript.async = true;
vantaScript.defer = true;
vantaScript.id = 'vanta'
vantaScript.src = '#/static/vanta.topology.min.js'
document.head.appendChild(vantaScript);
vantaScript.onload = () => {
// window???
window.VANTA.TOPOLOGY({
el: '#vanta',
color: 0xced4b1,
backgroundColor: 0xe0ebeb
})
}
}
},
mounted() {
this.animate();
},
});
I'm trying to access the VANTA object on window after it loads, but there is none. It feels like my approach is just wrong to begin with, but I'm not able to find any documentation on how to include static scripts?
Check this repo for a vue vanta component with slots to use in specific components https://github.com/iwatakeshi/vue-vanta
Related
I am working with mercadopago library. It's necessary to load the mercadopago script file and make an instance of the MercadoPago object. The thing is, nextJS loads the main bundle before including the mercadopago file, so I got an execution error because the object is not defined.
I tried different ways, loading the script file into the Head component with a normal tag and with the Next/Script object like:
<script src="https://sdk.mercadopago.com/js/v2" strategy="beforeInteractive"/>
Does not matter what I do, next always loads the script after the main bundle file. If I set a setTimeout to wait to instance the Mercadopago object it runs, but obviously this is not a good option. Which is the correct way to solve this?
Load the script in _document.js before next.js scripts, create _document.js in pages directory to extend the html document the way you like.
import Document, { Html, Head, Main, NextScript } from "next/document";
export default class MyDocument extends Document {
render(){
return (
<Html>
<Head>
/*This is loads the script in head tag of your html document*/
<script src="https://sdk.mercadopago.com/js/v2" strategy="beforeInteractive"/>
</Head>
<body>
/*your script can also go here before loading next scripts*/
<Main />
<NextScript />
</body>
</Html>
)
}
}
OK, I solved this using the onLoad method available on Next/Script component. What I needed to do was to move the script inclusion to my own component and include the component there adding the onLoad props and passing a function that executed my object instance after loading it.
Here my code:
const onload = () => {
const controller = new Controller(props);
setController(controller);
};
const pay = () => controller.load(props.disabled);
const attrs = {onClick: pay};
if (!controller || props.disabled) attrs.disabled = true;
return(
<>
<section className="mercadopago-checkout-pro-component">
<div ref={refContainer} className="cho-container">
<button className="btn btn-secondary" {...attrs}>
Pay
</button>
</div>
</section>
<Script src="https://sdk.mercadopago.com/js/v2" onLoad={onload}/>
</>
);
Go to your component where you need this script.
import Script from 'next/script'
const myComponent = () => {
const [razorpayInstance, setRazorpayInstance] = useState();
const handleLoadScript= () => {
var options: any = {
"key": "myKey"
};
res = new Razorpay(options);
setRazorpayInstance(razorpay)
}
return( <> <Script id="onload-id" src="https://mycdn.com/slugs" onLoad={handleLoadScript} /> </> )};
export default myComponent;
I am working on the CMS based vue page. In the page I have one root container inside that I have two child container as looks below
<div id="app">
<div class="above-the-fold">...</div>
<div class="below-the-fold noscript-container">
<noscript>
<div v-if="true">{{someDynamicBinding}}</div>
<div v-if="false">{{someDynamicBinding}}</div>
</noscript>
</div>
</div>
If javascript is enabled I am removing noscript tag and appending inside contents to the parent element using a below script.
document.addEventListener("DOMContentLoaded", function () {
const appendContents = function () {
const noScript = document.querySelector(".noscriptcontainer noscript");
const belowContent = noScript.innerHTML;
noScript.parentElement.innerHTML += belowContent;
console.log("elm appended");
window.removeEventListener("scroll", appendContents);
noScript.remove();
console.log("eve removed");
};
window.addEventListener("scroll", appendContents);
});
Now the problem is vue doesn't evaluating v-if or {{dynamicBinding}}
Jsfiddle Link https://jsfiddle.net/69yr3mp5/2/
Now what is the best way to make this work?
Try to replace the second if with v-else-if
<div v-if="true"><h1>
{{someDynamicBinding}}
</h1></div>
<div v-else-if="false"><h1>
{{someDynamicBinding}}
</h1></div>
</noscript>
TL;DR;
You can update the template run-time, but you can't update the template after component is mounted.
Run-time, in the context of these frameworks, means that it is not pre-compiled, which is something that Vue supports. However, the problem here (one of them anyway) is that you are modifying the template after the application is mounted. The Vue app will not continue to check the template after the initial read. While I'm not sure what you're trying to accomplish here, It seems that the approach is wrong.
To illustrate, if you use this code, it will compile the updated code, but not until you scroll.
let mounted = false
document.addEventListener("DOMContentLoaded", function() {
const appendContents = function() {
const noScript = document.querySelector(".noscript-container noscript");
const belowContent = noScript.innerHTML;
noScript.parentElement.innerHTML += belowContent;
console.log("elm appended");
window.removeEventListener("scroll", appendContents);
noScript.remove();
console.log("eve removed");
if (!mounted) {
Vue.createApp({
data() {
return {
someDynamicBinding: 'Some Content'
};
},
mounted() {
console.log('mounted')
},
}).mount('#app');
};
mounted = true
}
window.addEventListener("scroll", appendContents);
});
We are moving from GEB/Spock to Cypress for front end automation. With GEB/Spock, the page objects had static content. This was used to create the selectors for the page objects.
class LoginPage extends Page {
//private static Logger log = LoggerFactory.getLogger(LoginPage.class);
static url = 'login/'
static at = { title == "Login to TalentBank"}
static content = {
backgroundImage { $("div [style*=\"background-image\"]") }
logo { $(".center-img img") }
emailHeader { $("#emailGroup:not([style*=\"none\"]) label") }
emailTextBox { $('#emailGroup:not([style*="none"]) #email') }
nextButton { $('#loginWithEmail') }
pwdHeader { $("#passwordGroup:not([style*=\"none\"]) label") }
pwdTextBox { $("#passwordGroup:not([style*=\"none\"]) #password") }
loginButton { $("#loginWithPassword") }
loginError(wait: true) { $("#loginError") }
In cypress, I'm unsure where to create and call these objects. Are the selectors supposed to be created as fixtures or as support files? I've read through the cypress documentation, but can't find what I am looking for.
Edit: 2/4
Under support, I tried creating a LoginPage.js file to contain the objects
// Login Page Objects
const emailTextBox = $('#emailGroup:not([style*="none"]) #email');
My test is under integration directory. I use the new constant in my test. There are no errors in my IDE, as the test appears to find the constant as it displays in the Ctrl+Space code completions.
describe('Successfull log in to the System', function() {
it('Standard User - Successful log in ', function() {
cy.get(emailTextBox).type('RodneyRuxin#mailinator.com')
When I run my test however, I get an error that says
ReferenceError: emailTextBox is not defined
.
Okay so these are different selectors to interact with:
I would recommend a completely different class and then do an import of them?
So for example a file called locators.js
which contains:
export const backgroundImage = () => cy.get("div [style*=\"background-image\"]");
then in your other file, you can import it like so:
import * as locators from "../locators/locators.js";
and call it like this (example):
locators.backgroundImage()
.should('be.visible')
.click();
Hope this helps!
I am trying to implement the HTML5 version of the PhotoeditorSDK from this website - https://docs.photoeditorsdk.com/guides/html5/v4/introduction/getting_started
I have tried to implement using the Angular Github repo but it would seem that package.json illustrate this only works for Angular 5 & 6 and our app currently is a little outdated being Angular 4.x (and we cannot upgrade to 5 at this time)
Using the HTML5 method is fairly easy in a simple application but within an Angular 4 this seems to be tricky as due to Angular restrictions I am unable to use <script> tags within the component.
In the index <head> I have added the following:
<head>
<!-- React Dependencies for the SDK UI -->
<script src="js/vendor/react.production.min.js"></script>
<script src="js/vendor/react-dom.production.min.js"></script>
<!-- PhotoEditor SDK-->
<script src="js/PhotoEditorSDK.min.js"></script>
<!-- PhotoEditor SDK UI -->
<script src="js/PhotoEditorSDK.UI.DesktopUI.min.js"></script>
<link rel="stylesheet" href="css/PhotoEditorSDK.UI.DesktopUI.min.css"/>
</head>
In the template itself there is a simple <div> as follows :
<div id="editor" style="width: 100vw; height: 100vh;"></div>
The script tag itself looks as following - and would basically attach an image that would show within the editor to edit etc..
<script>
window.onload = function () {
var image = new Image()
image.onload = function () {
var container = document.getElementById('editor')
var editor = new PhotoEditorSDK.UI.DesktopUI({
container: container,
// Please replace this with your license: https://www.photoeditorsdk.com/dashboard/subscriptions
license: '{"owner":"Imgly Inc.","version":"2.1", ...}',
editor: {
image: image
},
assets: {
// This should be the absolute path to your `assets` directory
baseUrl: '/assets'
}
})
}
image.src = './example.jpg'
}
</script>
I am trying to figure out the best way to use <script> above directly within an Angular 4 Component - I know this is best practice but what is the best way to do this?
Your component has an element with id editor. This will only be available in the ngAfterViewInit hook of the component. Once this is called, the window.onload has been called ages ago. Also when the onload is called, the element doesn't exist at all yet, so it's also a bad idea to put it there.
Best would be to call the method from inside the ngAfterViewInit of your component, and use the #ViewChild annotation:
declare const PhotoEditorSDK: any;
#Component({
template: `<div style="width: 100vw; height: 100vh;" #editor></div>`
})
export class EditorComponent implements AfterViewInit {
#ViewChild('editor') editor: ElementRef<HTMLElement>;
ngAfterViewInit(): void {
const image = new Image();
image.addEventListener('load', () => {
const editor = new PhotoEditorSDK.UI.DesktopUI({
container: this.editor.nativeElement,
license: '{"owner":"Imgly Inc.","version":"2.1", ...}',
editor: {
image
},
assets: {
baseUrl: '/assets'
}
});
});
image.src = './example.jpg'
}
}
Say I have a simple view model (widget.js):
import {Behavior} from 'aurelia-framework';
export class Widget
{
static metadata() { return Behavior.customElement('widget') }
constructor()
{
this.title= "AwesomeWidget";
}
}
With the following view: (widget.html):
<template>
<div>${title}</div>
</template>
Now say I inject some markup like this into the DOM:
var markup = `<div><widget></widget></div>`;
var $markup = $(markup);
var $placeholder = $("#placeholder");
$placeholder.append($markup);
How can I now tell Aurelia to compile this newly added part of the DOM against a new instance of Widget? I know it involves ViewCompiler but need an example to help me along. I'd greatly appreciate any help. Thanks!
A few months ago the TemplatingEngine class got a newly accessible enhance API method. This shortcuts the need to manually use the viewCompiler and compile method which was originally the only easy approach. This blog post details how you can use the enhance API to Aureliaify dynamically added HTML in your pages.
This has the added benefit of not needing to clean up the compiled HTML or detach anything either.
Here's an example: https://gist.run/?id=762c00133d5d5be624f9
It uses Aurelia's view compiler to compile the html and create a view instance, bound to the supplied view-model.
view-factory.js
import {
inject,
ViewCompiler,
ViewResources,
Container,
ViewSlot,
createOverrideContext
} from 'aurelia-framework';
#inject(ViewCompiler, ViewResources)
export class ViewFactory {
constructor(viewCompiler, resources, container) {
this.viewCompiler = viewCompiler;
this.resources = resources;
this.container = container;
}
insert(containerElement, html, viewModel) {
let viewFactory = this.viewCompiler.compile(html);
let view = viewFactory.create(this.container);
let anchorIsContainer = true;
let viewSlot = new ViewSlot(containerElement, anchorIsContainer);
viewSlot.add(view);
view.bind(viewModel, createOverrideContext(viewModel));
return () => {
viewSlot.remove(view);
view.unbind();
};
}
}
Usage:
let view = this.viewFactory.insert(containerElement, viewHtml, viewModel);