Share global variables between aspx and Vue pages - javascript

Before anybody starts screaming at me for using global variables, I have a few things to say:
This is only for one variable and will only be used temporary (my estimates says that it will be gone within one year)
I'm open for suggestions on how to solve this a better way
Our project has a bit of a unusual setup. We have an old ASP.Net website, that we are slowly converting to VueJs, page by page. Due to this, we are using UMD
Currently Vue is used for the menu, showing content, etc. When showing an old aspx page, we are using iframe. My goal is for my Vue to be able to catch events thrown from the open aspx page
For this I thought the mitt emitter would be perfect for the job. I'm using it in my Vue project already
On the code side of things, I have the following:
I have a Toolbox.js file, where I try to keep my global variable
var emitter;
async function ClickFromOldPage()
{
emitter.emit('test');
}
In mounted in my vue file (the first thing that opens) I'm setting the global variable
mounted()
{
window.emitter = window.mitt();
window.emitter.on('test', this.Test);
},
And then I have an aspx page that calls ClickFromOldPage
Both the vue file and aspx file is loading Toolbox.js like this
<script src="../Vue3/Javascript/Toolbox.js"></script>
From what I could gather around the net, this should do it, but emitter is undefined when calling Toolbox from the aspx page
Another way was by using top to set/get variables, but this gives the same result
I also tried localStorage, but this can't handle complex objects like the emitter
The only other way to handle this challenge I can think of, is by using cookies or localStorage, where the aspx page writes 'Clicked' (or whatever), then have a function in Vue that checks for said data every second or so. This doesn't sound like a good solution, but the only solution I can come up with
Any other ideas?

Related

How to have one main JavaScript file for multiple pages?

I have a small application with two pages: the login page and the main page. My problem is that when I use just one main JavaScript file for both pages (like it's recommanded) I get a lot of ReferenceError because some variables on a page are not defined on the other one...
e.g:
Line of code for the login page
copyrightYear.textContent = new Date().getFullYear();
Main page complains
Uncaught TypeError: Cannot set property 'textContent' of null
How can I fix that? Don't tell me I have to say if(copyrightYear) { do stuff } everytime, it's just a nightmare if I have to do that for every variable
Two answers for you:
The recommendation isn't a dictate
My problem is that when I use just one main JavaScript file for both pages (like it's recommanded)
That's a very general recommendation. It doesn't apply to every situation. There's no point in loading code in a page that won't use that code.
If you have code that you use in both pages, and also page-specific code, it's absolutely fine to have a file that both pages share and also page-specific files:
<script src="main.js"></script>
<script src="page1.js"></script>
If you're really worried about the extra HTTP request (much less of an issue these days than it used to be), use modules and a bundler like Webpack or Rollup that will create a bundle combining the main module with page 1's module for page 1 and another bundle combining the main module with page 2's module for page 2.
But even then, the extra HTTP request may be better for your users, if you expect them to go from page1 to page2 in a reasonable timeframe. The reason is that if you have main.js and page1.js/page2.js and you allow caching of them, when the user goes to page1 they get main.js and page1.js, and then when they go to page2 main.js comes out of their local cache and they only have to load page2.js. In contrast, if you had a single bundle file for each page, they'd have to re-download all of that common code when going from page1 to page2 (or vice versa). But if you expect a visitor to visit either page1 or page2 but not the other one, you save the HTTP request by using page-specific bundles.
There's really no one-size-fits-all solution. :-) There are all sorts of considerations.
Note that HTTP/1.1 made additional HTTP requests more efficient and is nearly universally supported, and HTTP/2 makes them a lot more efficient, effectively eliminating the case for reducing the number of HTTP requests as part of the page startup. All major modern browsers support HTTP/2 as do up-to-date web servers.
Put the code for each page in a function
If you really want to keep a single page, put the code that's specific to each page in functions for those pages, and then have code in the main part of the file call the appropriate function based on location.pathname.
You figured it out, you have to check this for every variable. But generally it's much more convenient to use functions and only call these functions when you need them.
So for example, say you want to set some copyrightYear (even tough this shouldn't be set via JS, you should generate this on Backend side to have it in the source code)
You have something like this:
function updateYear() {
// here you do your magic of selecting the element, setting the year, whatever.
}
// another function, totally unrealted to updateYear()
function toggleMenu() {
// some function where you toggle the menu if you click somewhere
// like: button.addEventListener('click', () => {} );
}
And in your JS file you have one block where you call all these functions:
if (document.querySelectorAll('.elementForYear') {
updateYear(); // here we call it because we are sure this element exists... so everything inside function must work
}
if (document.querySelector('.myMenu') {
toggleMenu(); // if the element myMenu exists, we know we can add these toggle Functionality.
}
You can also add these if inside the function and call the function regardless of if it's needed or not, that's up to you and up to coding guidelines.
Generally I find it makes sense to have one function only rely on one (or max two to three elements if it's some toggling of other elements) ... And then you just check for one element. And if this one element exists you can go ahead and call the function.

Using Skulpt with React

I'm interested in creating a React web app that teaches kids how to use Python through programs such as Turtle graphics. I thought that Skulpt would be a good library to use since it already has a demo that shows here how to use it within a HTML page. However, while it loads fine in a normal web page, I'm having difficulty getting it to run in my React app, probably due to the global variables that skulpt.min.js sets. Specifically, I get the following message:
./src/utils/skulpt-stdlib.js
Line 1:1: 'Sk' is not defined no-undef
I imagine it's because the skulpt-stdlib.js script relies on the skulpt.min.js script to be read first and set its global variables before the skulpt-stdlib.js script works. What would I need to do in React to get this to work the same way it does in a normal web page? Thank you!
try this, hope this works, i am also trying, i will update this if it works completely.
add the below code in the beginning of skulpt.min.js
function Sk() {}
and the below code in the end of skulpt.min.js
export { Sk };

How to use Firebase in all documents?

My firebase is setup and initialized in my index.html, but then I can only use it there. Is there any way to use it on all documents?
It is set up in my index page, and then I use the data base and JQuery on a separate JS file to manipulate things also in that index page. I am doing similar things on another HTML page but when I call a function, it doesn't do anything/can't get the data. I thought I should initialize the database on that page too (within script tags of course, just copy/paste the code to initialize), and that also doesn't do anything. I also tried initializing it within the JS code as well, which seems to be the most intuitive thing to do, and that doesn't work either.
I initialize the database with the following:
var firebaseConfig = {...}
firebase.initializeApp(firebaseConfig);
var db = firebase.firestore();
Then just below, I can call a function in the JS and it uses the database information. But when I call a function in another HTML doc, it won't use the database.
I have initialized it in the JS file itself before, and that got me what I wanted but I can't do it again for some reason.
Once you navigate from one page to the next, you browser pretty much forgets anything from the previous page. This means that you will need to include Firebase into any page where you use it.
Firebase is often used in so-called single-page applications, which are implemented as a single real page, but emulate many application screens within that page. That way they only have to include/load Firebase once, but can still show multiple screens to the user. If you're interested in this approach, I recommend looking into some common single-page application frameworks, such as Angular, React, and Vue.

My Shopify app replaces the product form - option_selection.js breaks: can't access its "parentNode" property

My Shopify app replaces the product pages 'Add To Cart' form / product form, with it's own form of sorts. It is Liquid logic that decides whether or not to render the entire <form> element.
This works great, but on some themes (like Jumpstart by Shopify), the product page bugs out completely, throwing me an error saying:
option_selection.js - can't access its "parentNode" property
Which I believe is the option_selection.js function where it is looking for the select box / variant ID somewhere on the page.
Of course, this variant ID / select box does not exist because it is not being rendered.
How can I replace the add to cart form while satisfying the option_selection.js functions?
Usually this wouldn't be a big deal, but Shopify's app review team will give me problems with this, and on the Jumpstart theme specifically, this error causes the product photos to not render; breaking the page completely.
Any ideas here? Much appreciated!
Axing the entire product form seems a bit extreme - there's no way to do what you need to do in a less invasive way?
Assuming not, you'll want to expand your install so that you can update any code in a theme that initializes the product form to take into account the possibility that you've defied the theme's simplistic assumptions.
For the option_selection.js compatibility, you'll be looking for where new Shopify.OptionSelectors is being invoked. If your code has set a variable through Javascript, that may be the easiest check to make. Example of an inline install that assumes your code creates a function named MyAppNamespace.isProdHidden:
Original:
new Shopify.OptionSelectors( ...
Updated:
!(window.MyAppNamespace && MyAppNamespace.isProdHidden({{ product.id | json }}) ) && new Shopify.OptionSelectors( ...
The added piece of code will evaluate to false if and only if your app has loaded properly and your isProdHidden function returns a truthy value. This scenario would prevent the new Shopify.OptionSelectors part from running, since we're using the && as a sort of short-circuit/emergency-stop operation.
If your app failed to load (or was uninstalled from the store without the liquid code being updated), or if MyAppNamespace.isProdHidden returns false, then the added block of code evaluates as true and the new Shopify.OptionSelectors happens as normal.
The above is equivalent to wrapping the entire new Shopify.OptionSelectors call in an if statement, with the install benefit that the party installing your app doesn't need to read the theme code to figure out where the OptionSelectors call ends. In most themes the OptionSelectors code is spread out over multiple lines and occasionally theme developers declare their onVariantChange function as an inline anonymous function - neither of which are big obstacles for experienced developers, but a huge complication for novices and store owners without this kind of expertise.
Making the status of your app available somehow through Javascript is probably the best thing for you to do as far as theme-install-compatibility goes. Some themes have their OptionSelectors call right in the product page, which can be affected by dynamic Liquid variables, but many have this code tucked away in a .js file in the assets folder instead. Still other themes don't use Shopify's OptionSelectors code at all and instead run their own thing, and thus your app could interfere in completely unexpected ways or places. Creating tools to make it easier to integrate your app into somebody else's code is therefore one of the best things you can do.
You'll also want to make sure that your code is able to handle multiple products, as many stores have quick-shops all through the site which can load arbitrary product forms. By making sure you have made the tools available, it's possible for you, your support team (if any) and theme devs can make the required updates to (almost!) any arbitrary theme.
Hope that helps!

Ext js dynamic loading of libraries when using iframes

Good day all.
I have a big Ext js application in which I have to insert a new section which will be loaded in iframes.
To maintain the routes of Ext js, each page of the new part will be a different view, with a different viewController in which will be only the iframe that will load the page of the new application.
Now I'd like to make some cleaning in the mess, for example, all the new application pages will use some shared libraries, which will be inserted in the app.json of Extjs so they will be available for each iframe page.
but most of the scripts of these new pages will be specific of that very page, so it will be a big waste of resources if all of those scripts have to be inserted in the app.json and so they are available for every part of the application.
The idea was, is there a clean way to specify some libraries used only by a single viewController (and its iframe) and when the user change the route, everything is destroyed and the memory is cleaned?
If you need to load a specific js only if a window or frame is called, the best way is to use an Ext.Loader method called loadScript
Ext.loader.loadScript(url)
Loads the specified script URL and calls the supplied callbacks. If this method is called before Ext#isReady, the script's load will delay the transition to ready. This can be used to load arbitrary scripts that may contain further Ext.require calls.
Also remember that your on html5! So you can use other methods to load a js script when you need it. An example:
System.import("yourScriptFile.js").then(function(){
// script loaded.
});
If you want to load multiple files you can also use it:
Promise.all(["url1", "url2"].map(System.import)).then(function(){
// loaded all here
});
If you need to destroy a window iframe you only need to use the method close, as you can se here the method close on the default case will delete the window from the dom and destroy the object from the memory.
So, in my opinion the best way is to use iframe windows, and use the close method to destroy them at the end of their life.
If you only need to use a container in which you're inserting your new html with your js scripts, you can instead call a simple .destroy() method.
Remember that there are other ways to split your project's app.js in different parts, have also a look here to have an idea.

Categories

Resources