Astro.js is not correctly importing functions - import

I am brand new to Astro.js. I've tried several ways to import a "global" function on another page but I always get an error saying the function "is not defined". Here is my code from the master/global layout page...
export function test1() {
console.log('test1()');
}
And here is the code on a separate page...
import test1 from '../layouts/master.astro';
(later on that same page...)
setTimeout(() => {
test1();
}, 100);
Any suggestions?

Ah I figured it out. I updated the master page with the following code...
<script is:inline src="/global-methods.js"></script>
<script>
setTimeout(() => {
main();
}, 100);
</script>
("global-methods.js" is in the public directory.)
After doing that, I was able to reference my methods in subsequent pages.

Related

NEXTJS fix window is not defined on import [duplicate]

In my Next.js app I can't seem to access window:
Unhandled Rejection (ReferenceError): window is not defined
componentWillMount() {
console.log('window.innerHeight', window.innerHeight);
}
̶A̶n̶o̶t̶h̶e̶r̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶ ̶i̶s̶ ̶b̶y̶ ̶u̶s̶i̶n̶g̶ ̶p̶r̶o̶c̶e̶s̶s̶.̶b̶r̶o̶w̶s̶e̶r ̶ ̶t̶o̶ ̶j̶u̶s̶t̶ ̶e̶x̶e̶c̶u̶t̶e̶ ̶ ̶y̶o̶u̶r̶ ̶c̶o̶m̶m̶a̶n̶d̶ ̶d̶u̶r̶i̶n̶g̶ ̶r̶e̶n̶d̶e̶r̶i̶n̶g̶ ̶o̶n̶ ̶t̶h̶e̶ ̶c̶l̶i̶e̶n̶t̶ ̶s̶i̶d̶e̶ ̶o̶n̶l̶y̶.
But process object has been deprecated in Webpack5 and also NextJS, because it is a NodeJS variable for backend side only.
So we have to use back window object from the browser.
if (typeof window !== "undefined") {
// Client-side-only code
}
Other solution is by using react hook to replace componentDidMount:
useEffect(() => {
// Client-side-only code
})
Move the code from componentWillMount() to componentDidMount():
componentDidMount() {
console.log('window.innerHeight', window.innerHeight);
}
In Next.js, componentDidMount() is executed only on the client where window and other browser specific APIs will be available. From the Next.js wiki:
Next.js is universal, which means it executes code first server-side,
then client-side. The window object is only present client-side, so if
you absolutely need to have access to it in some React component, you
should put that code in componentDidMount. This lifecycle method will
only be executed on the client. You may also want to check if there
isn't some alternative universal library which may suit your needs.
Along the same lines, componentWillMount() will be deprecated in v17 of React, so it effectively will be potentially unsafe to use in the very near future.
If you use React Hooks you can move the code into the Effect Hook:
import * as React from "react";
export const MyComp = () => {
React.useEffect(() => {
// window is accessible here.
console.log("window.innerHeight", window.innerHeight);
}, []);
return (<div></div>)
}
The code inside useEffect is only executed on the client (in the browser), thus it has access to window.
With No SSR
https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)
function Home() {
return (
<div>
<Header />
<DynamicComponentWithNoSSR />
<p>HOME PAGE is here!</p>
</div>
)
}
export default Home
The error occurs because window is not yet available, while component is still mounting. You can access window object after component is mounted.
You can create a very useful hook for getting dynamic window.innerHeight or window.innerWidth
const useDeviceSize = () => {
const [width, setWidth] = useState(0)
const [height, setHeight] = useState(0)
const handleWindowResize = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
}
useEffect(() => {
// component is mounted and window is available
handleWindowResize();
window.addEventListener('resize', handleWindowResize);
// unsubscribe from the event on component unmount
return () => window.removeEventListener('resize', handleWindowResize);
}, []);
return [width, height]
}
export default useDeviceSize
Use case:
const [width, height] = useDeviceSize();
componentWillMount() lifecycle hook works both on server as well as client side. In your case server would not know about window or document during page serving, the suggestion is to move the code to either
Solution 1:
componentDidMount()
Or, Solution 2
In case it is something that you only want to perform in then you could write something like:
componentWillMount() {
if (typeof window !== 'undefined') {
console.log('window.innerHeight', window.innerHeight);
}
}
In the constructor of your class Component you can add
if (typeof window === 'undefined') {
global.window = {}
}
Example:
import React, { Component } from 'react'
class MyClassName extends Component {
constructor(props){
super(props)
...
if (typeof window === 'undefined') {
global.window = {}
}
}
This will avoid the error (in my case, the error would occur after I would click reload of the page).
global?.window && window.innerHeight
It's important to use the operator ?., otherwise the build command might crash.
Best solution ever
import dynamic from 'next/dynamic';
const Chart = dynamic(()=> import('react-apexcharts'), {
ssr:false,
})
A bit late but you could also consider using Dynamic Imports from next turn off SSR for that component.
You can warp the import for your component inside a dynamic function and then, use the returned value as the actual component.
import dynamic from 'next/dynamic'
const BoardDynamic = dynamic(() => import('../components/Board.tsx'), {
ssr: false,
})
<>
<BoardDynamic />
</>
I have to access the hash from the URL so I come up with this
const hash = global.window && window.location.hash;
Here's an easy-to-use workaround that I did.
const runOnClient = (func: () => any) => {
if (typeof window !== "undefined") {
if (window.document.readyState == "loading") {
window.addEventListener("load", func);
} else {
func();
}
}
};
Usage:
runOnClient(() => {
// access window as you like
})
// or async
runOnClient(async () => {
// remember to catch errors that might be raised in promises, and use the `await` keyword wherever needed
})
This is better than just typeof window !== "undefined", because if you just check that the window is not undefined, it won't work if your page was redirected to, it just works once while loading. But this workaround works even if the page was redirected to, not just once while loading.
I was facing the same problem when i was developing a web application in next.js This fixed my problem, you have to refer to refer the window object in a life cycle method or a react Hook. For example lets say i want to create a store variable with redux and in this store i want to use a windows object i can do it as follows:
let store
useEffect(()=>{
store = createStore(rootReducers, window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__())
}, [])
....
So basically, when you are working with window's object always use a hook to play around or componentDidMount() life cycle method
I wrapped the general solution (if (typeof window === 'undefined') return;) in a custom hook, that I am very pleased with. It has a similiar interface to reacts useMemo hook which I really like.
import { useEffect, useMemo, useState } from "react";
const InitialState = Symbol("initial");
/**
*
* #param clientFactory Factory function similiar to `useMemo`. However, this function is only ever called on the client and will transform any returned promises into their resolved values.
* #param deps Factory function dependencies, just like in `useMemo`.
* #param serverFactory Factory function that may be called server side. Unlike the `clientFactory` function a resulting `Promise` will not be resolved, and will continue to be returned while the `clientFactory` is pending.
*/
export function useClientSideMemo<T = any, K = T>(
clientFactory: () => T | Promise<T>,
deps: Parameters<typeof useMemo>["1"],
serverFactory?: () => K
) {
const [memoized, setMemoized] = useState<T | typeof InitialState>(
InitialState
);
useEffect(() => {
(async () => {
setMemoized(await clientFactory());
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
return typeof window === "undefined" || memoized === InitialState
? serverFactory?.()
: memoized;
}
Usage Example:
I am using it to dynamically import libaries that are not compatible with SSR in next.js, since its own dynamic import is only compatible with components.
const renderer = useClientSideMemo(
async () =>
(await import("#/components/table/renderers/HighlightTextRenderer"))
.HighlightTextRendererAlias,
[],
() => "text"
);
As you can see I even implemented a fallback factory callback, so you may provide a result when initially rendering on the server aswell. In all other aspects this hook should behave similiar to reacts useMemo hook. Open to feedback.
For such cases, Next.js has Dynamic Import.
A module that includes a library that only works in the browser, it's suggested to use Dynamic Import. Refer
Date: 06/08/2021
Check if the window object exists or not and then follow the code along with it.
function getSelectedAddress() {
if (typeof window === 'undefined') return;
// Some other logic
}
For Next.js version 12.1.0, I find that we can use process.title to determine whether we are in browser or in node side. Hope it helps!
export default function Projects(props) {
console.log({ 'process?.title': process?.title });
return (
<div></div>
);
}
1. From the terminal, I receive { 'process?.title': 'node' }
2. From Chrome devtool, I revice { 'process?.title': 'browser' }
I had this same issue when refreshing the page (caused by an import that didn't work well with SSR).
What fixed it for me was going to pages where this was occurring and forcing the import to be dynamic:
import dynamic from 'next/dynamic';
const SomeComponent = dynamic(()=>{return import('../Components/SomeComponent')}, {ssr: false});
//import SomeComponent from '../Components/SomeComponent'
Commenting out the original import and importing the component dynamically forces the client-side rendering of the component.
The dynamic import is covered in Nextjs's documentation here:
https://nextjs.org/docs/advanced-features/dynamic-import
I got to this solution by watching the youtube video here:
https://www.youtube.com/watch?v=DA0ie1RPP6g
You can define a state var and use the window event handle to handle changes like so.
const [height, setHeight] = useState();
useEffect(() => {
if (!height) setHeight(window.innerHeight - 140);
window.addEventListener("resize", () => {
setHeight(window.innerHeight - 140);
});
}, []);
You can try the below code snippet for use-cases such as - to get current pathname (CurrentUrl Path)
import { useRouter } from "next/router";
const navigator = useRouter()
console.log(navigator.pathname);
For anyone who somehow cannot use hook (for example, function component):
Use setTimeout(() => yourFunctionWithWindow()); will allow it get the window instance. Guess it just need a little more time to load.
I want to leave this approach that I found interesting for future researchers. It's using a custom hook useEventListener that can be used in so many others needs.
Note that you will need to apply a little change in the originally posted one, like I suggest here.
So it will finish like this:
import { useRef, useEffect } from 'react'
export const useEventListener = (eventName, handler, element) => {
const savedHandler = useRef()
useEffect(() => {
savedHandler.current = handler
}, [handler])
useEffect(() => {
element = !element ? window : element
const isSupported = element && element.addEventListener
if (!isSupported) return
const eventListener = (event) => savedHandler.current(event)
element.addEventListener(eventName, eventListener)
return () => {
element.removeEventListener(eventName, eventListener)
}
}, [eventName, element])
}
If it is NextJS app and inside _document.js, use below:
<script dangerouslySetInnerHTML={{
__html: `
var innerHeight = window.innerHeight;
`
}} />

ClipboardJS with React, using document.getElementById()

Originally, I had it working fine.
Then I did this and now I can't get it to work
ClipboardField.js
import React from 'react';
export default (props) => {
return(
<div id="clip" data-clipboard-text={props.code} onClick={props.onClick}>
<p> Copy to clipboard.</p>
</div>
);
}
Field.js
class DashWizardTwoCore extends Component {
componentDidMount(){
const btns = document.getElementById('clip');
const clipboard = new Clipboard(btns);
}
componentDidUpdate() {
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
}
render(){
const someCode = "const foo = 1"
return (
<div>
<ClipboardField code={this.someCode} /> }
</div>
);
}
}
If you take the code out of ClipboardField and into Field it works. It's mostly, how do I use document.getElementById() in my parent component to find something in my child?
Their examples:
https://github.com/zenorocha/clipboard.js/blob/master/demo/constructor-selector.html#L18
https://github.com/zenorocha/clipboard.js/blob/master/demo/constructor-node.html#L16-L17
https://github.com/zenorocha/clipboard.js/blob/master/demo/constructor-nodelist.html#L18-L19
Your code is fine you just have a few issues:
you are binding clipboard.on in componentDidUpdate which won't trigger here since you are not really changing anything (in the ClipboardField component) that triggers this event.
You are passing {this.someCode} in the code prop which would be undefined should just be {someCode}
So it's just a matter of moving your clipboard.on to the componentDidMount right after the new Clipboard and use code={someCode}
https://jsfiddle.net/yy8cybLq/
--
In React whenever you want to access the actual dom element of your component we use what react calls as refs, I would suggest you do this rather than using getElementById as this is the "best practice".
However stateless components (like your ClipboardField component above) can't have refs so you just need to change it to be a normal component.
Here's a fiddle with your code but using refs instead: https://jsfiddle.net/e5wqk2a2/
I tried including links to the react docs for stateless components and refs but apparently don't have enough "rep" to post more than 2 links, anyways quick google search should point you in the right direction.
I adjusted your code and created a simple integration of clipboard.js with React.
Here's the fiddle: https://jsfiddle.net/mrlew/ehrbvkc1/13/ . Check it out.

Using search feature in Ionic framework

I am a UI person and very new to ionic framework.. I wanted to add search feature in my android app built using Ionic framework. After a research i found that I will need to use this plugin https://github.com/djett41/ionic-filter-bar. but there is no detail documentation available. Can anyone please guide how to use this plugin working. I have made all setup but stuck with actual code.
First of all you must install the plugin. You can use bower for that:
bower install ionic-filter-bar --save
and it will copy all the javascript and css needed in the lib folder inside www.
Then you must add the references to the css to your index.html:
<link href="lib/ionic-filter-bar/dist/ionic.filter.bar.css" rel="stylesheet">
same thing for the javascript:
<script src="lib/ionic-filter-bar/dist/ionic.filter.bar.js"></script>
You have to inject the module jett.ionic.filter.bar you your main module:
var app = angular.module('app', [
'ionic',
'jett.ionic.filter.bar'
]);
and you must reference the service $ionicFilterBar in your controller:
angular.module('app')
.controller('home', function($scope, $ionicFilterBar){
});
Now you can start using it.
In my sample I want to trigger the search-box when the user clicks/taps on a icon in the header. I would add this HTML to the view:
<ion-nav-buttons side="secondary">
<button class="button button-icon icon ion-ios-search-strong" ng-click="showFilterBar()">
</button>
</ion-nav-buttons>
The action trigger an event in my controller showFilterBar:
$scope.showFilterBar = function () {
var filterBarInstance = $ionicFilterBar.show({
cancelText: "<i class='ion-ios-close-outline'></i>",
items: $scope.places,
update: function (filteredItems, filterText) {
$scope.places = filteredItems;
}
});
};
which creates the $ionicFilterBar and shows it.
As you can see here I am using an array of objects $scope.places
$scope.places = [{name:'New York'}, {name: 'London'}, {name: 'Milan'}, {name:'Paris'}];
which I have linked to the items member of my $ionicFilterBar. The update method will give me in filteredItems the items (places) filtered.
You can play with this plunker.
Another option is to use the plugin to actually fetch some data remotely through $http.
If we want to achieve this we can use the update function again.
Now we don't need to bind the items to our array of objects cause we won't need the filtered elements.
We will use the filterText to perform some action:
$scope.showFilterBar = function () {
var filterBarInstance = $ionicFilterBar.show({
cancelText: "<i class='ion-ios-close-outline'></i>",
// items: $scope.places,
update: function (filteredItems, filterText) {
if (filterText) {
console.log(filterText);
$scope.fetchPlaces(filterText);
}
}
});
};
We will call another function which will, maybe, call $http and return some data which we can bind to our array of objects:
$scope.fetchPlaces = function(searchText)
{
$scope.places = <result of $http call>;
}
Another plunker here.
PS:
If you want to configure it using some sort of customization you must do it in your configuration using the provider $ionicFilterBarConfigProvider:
angular.module('app')
.config(function($ionicFilterBarConfigProvider){
$ionicFilterBarConfigProvider.clear('ion-ios-close-empty');
})
PPS:
In my plunker I've included the css and the script directly copying it from the source.
UPDATE:
Someone asked not to replace the list with the updated one.
My cheap and dirty solution is to check if the filterText contains some values. If it's empty (no searches) we go throught each element an set a property found = false otherwise we compare the places array we the filteredItems array.
Matching elements will be marked as found.
function allNotFound(filteredItems) {
angular.forEach($scope.places, function(item){
item.found = false;
});
}
function matchingItems(filteredItems) {
angular.forEach($scope.places, function(item){
var found = $filter('filter')(filteredItems, {name: item.name});
if (found && found.length > 0) {
console.log('found', item.name);
item.found = true;
} else {
item.found = false;
console.log('not found', item.name);
}
});
and now we can integrate the filter bar this way:
$scope.showFilterBar = function () {
var filterBarInstance = $ionicFilterBar.show({
cancelText: "<i class='ion-ios-close-outline'></i>",
items: $scope.places,
update: function (filteredItems, filterText) {
if (!filterText) {
allNotFound();
} else {
matchingItems(filteredItems);
}
}
});
};
We can use the found attribute of the object to change the style of the element.
As always, a Plunker to show how it works.
Ionic uses Angular, and Angular include an atributte filter very useful. Look this: https://docs.angularjs.org/api/ng/filter/filter and the example there. Regards

Writing script src dynamically via wicket

I want my page to load javascript dynamically to my body:
<script type= "text/javascript" src="this path should be decided from wicket dynamically"/>
I am using wicket version 1.4 therefore JavaScriptResourceReference does not exist in my version (for my inspection it wasn't ' )
how can I solve this ?
thanks in advance :).
I specify my comment into an answer.
You can use this code snippet:
WebMarkupContainer scriptContainer = new WebMarkupContainer("scriptContainer ");
scriptContainer .add(new AttributeAppender("type", Model.of("text/javascript")));
scriptContainer .add(
new AttributeAppender("src", urlFor(
new JavaScriptResourceReference(
YourClass.class, "JavaScriptFile.js"), null).toString()));
add(scriptContainer );
and the corresponding html:
<script wicket:id="scriptContainer "></script>
Just change the string JavaScriptFile.js to load any other Javascript file.
JavascriptPackageResource.getHeaderContributor() does exactly what you need.
You need nothing in your markup, just add the HeaderContributor it returns to your page.
Update: For Wicket 1.5 see the migration guide, but it goes like this:
public class MyPage extends WebPage {
public MyPage() {
}
public void renderHead(IHeaderResponse response) {
response.renderJavaScriptReference(new PackageResourceReference(YuiLib.class,
"yahoo-dom-event/yahoo-dom-event.js"));
response.renderCSSReference(new PackageResourceReference(AbstractCalendar.class,
"assets/skins/sam/calendar.css"));
}
}
If you want to put your <script> element in the body, you can simply declare it as a WebMarkupContainer and add an AttributeModifier to set the src attribute. Although in that case wicket won't generate the relative URLs for you, you have to do it yourself.
I'm not sure I understood completely.
If you are trying to create and append a script to the body after the page is loaded you should do it this way:
<script type="text/javascript">
function load_js() {
var element = document.createElement("script");
element.src = "scripts/YOUR_SCRIPT_SRC.js"; // <---- HERE <-----
document.body.appendChild(element);
}
// Wait for the page to be loaded
if(window.addEventListener)
window.addEventListener("load",load_js,false);
else if(window.attachEvent)
window.attachEvent("onload",load_js);
else
window.onload = load_js;
</script>
What I did here is create a new script element, and then apply to it its source.
That way you can control dynamicaly the src. After that I append it to the body.
The last part is there so the new element is applied only after the page is loaded.

javascript "window.history.forward(1);" not working

I'm trying to prevent the back button from working on one of my asp.net mvc pages.
I've read a couple of places that if i put "window.history.forward(1);" in my page it will prevent the back button from working on a given page.
This is what I did in my page:
<script type="text/javascript">
$(document).ready(function () {
window.history.forward(1);
});
</script>
It doesn't seem to be working. Am I using this incorrectly or is this approach wrong? thanks.
The way I've seen this trick used is to put history.forward() on every page before the page where you don't want the back button to work, then every time the user hits the back button it forwards them back to where they were. The common use is to prevent others from returning to a page (usually in a given, linear sequence) once they have progressed. This is sometimes used in the sign-in sequence for banking websites, for example.
As far as I know, there is no way to actually disable the back button. Sometimes people get around this by opening the page in a new window, which will not have a history of pages preceding it, and thus nothing to go back to. Others simply display a warning message before going back to inform a user that they may lose unsaved data, if that is the main concern.
That said, maybe this will help you: http://viralpatel.net/blogs/disable-back-button-browser-javascript/
maybe:...
<script type="text/javascript">
function disableBackButton()
{
window.history.forward();
}
setTimeout("disableBackButton()", 0);
$(document).ready(function () {
disableBackButton();
});
</script>
Use on the page in which you don't want back button to work.
window.history.forward(1);
This is working for me... Hope it helpful for you..
<script type="text/javascript">
window.history.forward();
function noBack(){
window.history.forward();
}
</script>
$(document).ready(function() {
noBack();
});
You can use
history.go(index)
index =0 //for the current page.
index>0 //e.g 1,2 for forward navigation
index<0 //e.g -1,-2 for backward navigation
history.go(-2)
<SCRIPT type="text/javascript">
window.history.forward();
function noBack() { window.history.forward(); }
</SCRIPT>
And in html Body tag write the following code.
<body onload="noBack();" onpageshow="if (event.persisted) noBack();" onunload=" " >
Try this, it worked for me.
Not sure if this is relevant but I found it and it might be worth a try:
<script type="text/javascript">
function preventBack() {
window.history.forward();
}
setTimeout("preventBack()", 0);
window.onunload = function() {
null
};
</script>
<script>
function preventBack() {
window.history.forward();
}
setTimeout("preventBack()", 0);
window.onunload = function () {
null
};
</script>
window.history.forward();
function noBack()
{
window.history.forward();
}
function setit() {
noBack();
}
<script>
function preventBack() {
window.history.forward();
}
setTimeout("preventBack()", 0);
window.onunload = function () {
null
};
</script>
The code needs to be on the page infront as well as the page you require for it to work