On this blog page, Captain Codeman describes how to implement Redux with Polymer 2.x. However, when I use it, I get the following error complaining about the variable MyApp being undefined. Where and how should I define the the MyApp variable?
Uncaught ReferenceError: MyApp is not defined
at my-redux-store.html:23
at my-redux-store.html:42
(anonymous) # my-redux-store.html:23
(anonymous) # my-redux-store.html:42
user-setter-behavior.html:114 Uncaught ReferenceError: ReduxBehavior is not defined
at user-setter-behavior.html:114
(anonymous) # user-setter-behavior.html:114
my-redux-store.html
<link rel="import" href="../bower_components/polymer/polymer-element.html">
<link rel="import" href="../bower_components/polymer-redux/polymer-redux.html">
<link rel="import" href="my-redux-actions.html">
<link rel="import" href="my-redux-middleware.html">
<link rel="import" href="my-redux-reducers.html">
<link rel="import" href="my-redux-selectors.html">
<script>
(function() {
const composeEnhancers = typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: Redux.compose;
const enhancer = composeEnhancers(
Redux.applyMiddleware(...MyApp.middleware),
);
const store = Redux.createStore(MyApp.rootReducer, enhancer);
const reduxMixin = PolymerRedux(store);
/* #mixinFunction */
const actionsMixin = (superClass) => {
return class extends reduxMixin(superClass) {
static get actions() {
return MyApp.actions
}
}
}
/* #mixinFunction */
MyApp.ReduxMixin = Polymer.dedupingMixin(actionsMixin);
}());
</script>
MyApp is just a global namespace, just like the Polymer object. It's explained in the previous article that it starts by referencing.
MyApp = {}
or, if you are using strict mode, maybe:
window.MyApp = {}
You can also create it if it isn't defined using something like:
MyApp = MyApp || {}
Related
I am writing a test to check if it loads script correctly. This script is to load scripts for user tracking.
Here is my pseudo code.
import Head from 'next/head';
export const TrackingScript = () => {
return (
<Head>
<script
type="text/javascript"
src="https://..."
</script>
</Head>
)
}
I want to write a test
import { render } from '#testing-library/react';
import { TrackingScript } from './TrackingScript';
it('loads tracking script correctly', () => {
render(TrackingScript);
...
})
I am adding tag manager to my next.js site (next version 12.0.4) and the docs here https://nextjs.org/docs/basic-features/script say that I can use next/script in the _document.js file.
When I follow the instructions I then get an error that points me to this page https://nextjs.org/docs/messages/no-script-in-document-page saying I cannot use next/script in the _document.js page, this page points back to the original page.
I'm confused, what is the correct implementation?
After having a dig around I came across this post:
Next 11 and adding Script tags not working. No scripts are rendered
Basically saying scripts where not working, which once I had ignored the warnings and tried in my solution I found also to be the case.
The solution to use next/head in my _app.js seems to work fine.
read the above comment by #juliomalves for the explanation as to why this is to be done in _app.js
import Script from "next/script";
const App = ({ Component, pageProps }) => (
<>
<Script
id="tagmanager-main"
strategy="afterInteractive"
async
src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_MEASUREMENT_ID}`}
></Script>
<Script
id="tagmanager-setup"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${process.env.NEXT_PUBLIC_MEASUREMENT_ID}');
`,
}}
/>
{/* eslint-disable-next-line react/jsx-props-no-​spreading */}
<Component {...pageProps} />
</>
);
export default App;
This was my _document.tsx, maybe there was something in there stopping it from working, but I am happy enough with the working solution above.
import * as React from 'react';
// eslint-disable-next-line #next/next/no-document-import-in-page
import Document, { Html, Head, Main, NextScript } from 'next/document';
import createEmotionServer from '#emotion/server/create-instance';
// import theme from '../../styles/theme';
import createEmotionCache from '../lib/createEmotionCache';
// eslint-disable-next-line #next/next/no-script-in-document
import Script from 'next/script';
export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
{/* PWA primary color */}
{/* <meta name="theme-color" content={theme.palette.primary.main} /> */}
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>
<Script
id="tagman"
strategy="afterInteractive"
async
src="https://www.googletagmanager.com/gtag/js?id=G-xxxxx"
></Script>
<Script
id="tagman-datalayer"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-xxxxx');
`,
}}
/>
<script
src={`https://maps.googleapis.com/maps/api/js?key=${
process.env.NEXT_PUBLIC_MAPS_API_KEY || ''
}&libraries=&v=weekly`}
async
></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with static-site generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
// Resolution order
//
// On the server:
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. document.getInitialProps
// 4. app.render
// 5. page.render
// 6. document.render
//
// On the server with error:
// 1. document.getInitialProps
// 2. app.render
// 3. page.render
// 4. document.render
//
// On the client
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. app.render
// 4. page.render
const originalRenderPage = ctx.renderPage;
// You can consider sharing the same emotion cache between all the SSR requests to speed up performance.
// However, be aware that it can have global side effects.
const cache = createEmotionCache();
const { extractCriticalToChunks } = createEmotionServer(cache);
ctx.renderPage = () =>
originalRenderPage({
// eslint-disable-next-line react/display-name, #typescript-eslint/no-explicit-any
enhanceApp: (App: any) => (props) =>
<App emotionCache={cache} {...props} />,
});
const initialProps = await Document.getInitialProps(ctx);
// This is important. It prevents emotion to render invalid HTML.
// See https://github.com/mui-org/material-ui/issues/26561#issuecomment-855286153
const emotionStyles = extractCriticalToChunks(initialProps.html);
const emotionStyleTags = emotionStyles.styles.map((style) => (
<style
data-emotion={`${style.key} ${style.ids.join(' ')}`}
key={style.key}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }}
/>
));
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
...emotionStyleTags,
],
};
};
I have a problem with my script, I am using next and React in my pages / _app.js file I have the following code:
import axios from "axios";
import { $ } from "jquery";
import App from "next/app";
import Router from "next/router";
import { destroyCookie, parseCookies } from "nookies";
import Layout from "../components/_App/Layout";
import "../public/css/boot.css";
import "../public/css/icons.css";
import "../public/css/themes/style.css";
import "../public/jquery";
import "../public/scripts";
import baseUrl from "../utils/baseUrl";
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
const { token } = parseCookies(ctx);
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
if (!token) {
const isProtectedRoute =
ctx.pathname === "/profile" ||
ctx.pathname === "/admin/add-product" ||
ctx.pathname === "/my-orders-history" ||
ctx.pathname === "/admin/users" ||
ctx.pathname === "/admin/dashboard";
if (isProtectedRoute) {
redirectUser(ctx, "/auth/login");
}
} else {
try {
const payload = { headers: { Authorization: token } };
const url = `${baseUrl}/api/account`;
const response = await axios.get(url, payload);
const user = response.data;
const isRoot = user.role == "root";
const isAdmin = user.role == "admin";
// if authenticated but not root or admin
const isNotPermitted =
!(isRoot || isAdmin) &&
(ctx.pathname === "/admin/add-product" ||
ctx.pathname === "/admin/customers" ||
ctx.pathname === "/admin/orders" ||
ctx.pathname === "/admin/dashboard");
if (isNotPermitted) {
redirectUser(ctx, "/products");
}
pageProps.user = user;
} catch (error) {
// console.error("Error getting current user", error);
//invalid token
destroyCookie(ctx, "token");
redirectUser(ctx, "/auth/login");
}
}
return { pageProps };
}
componentDidMount() {
window.addEventListener("storage", this.syncLogout);
require($)(window);
}
syncLogout = e => {
if (e.key === "logout") {
Router.push("/");
}
};
render() {
const { Component, pageProps } = this.props;
return (
<Layout {...pageProps}>
<Component {...pageProps} />
</Layout>
);
}
}
export default MyApp;
In my components/_App/Layouts.js tenho o seguinte codigo:
import React from "react";
import Head from "next/head";
import Footer from "./Footer";
import StaticHeader from "./StaticHeader";
const Layout = ({ children, user }) => {
return (
<React.Fragment>
<Head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WdpShoes | Home</title>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght#400;700;800&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="./public/css/icons.css" />
<link href="https://file.myfontastic.com/gMNiHf8HU5GG4r6y622k2N/icons.css" rel="stylesheet" />
<link rel="stylesheet" href="/css/boot.css" />
<link rel="stylesheet" href="/css/themes/style.css" />
<link rel="shortcut icon" href="/css/themes/logo/favicon.png" />
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</Head>
<body>
<StaticHeader user={user} />
{children}
<Footer />
<script src="/jquery.js"></script>
<script src="/scripts.js"></script>
<script src="/css/themes/script.js"></script>
</body>
</React.Fragment>
);
};
export default Layout;
In the Components that are in my Layout the jquery and javascript are working but in my components they are not
When I import my script into my pages / _app.js file I encounter the following error:
ReferenceError: $ is not defined
at Object../public/scripts.js (C:\Users\walter\Desktop\mystore.next\server\pages_app.js:5217:1)
I looked for some materials and I didn't find anything that could help me solve this problem that is preventing me from developing several projects
I know the purpose of the site is to answer questions but to better explain my problem I have the complete code at:
https://codesandbox.io/s/dawn-mountain-vme5e?file=/pages/_app.js
And in its current state it encounters the Internal Server Error due to the fact that the $ function is returning an undefined,Because my script and jquery are not working correctly in my components
To understand your issue, a little theory is needed. Next.js uses server-side rendering and static generation, which means the all code is initially run on the server. jQuery is a client-side library and it can't run on the server. Therefore, you need to ensure that all the jQuery code is executed only on the client side. You can do this by checking if the window object exists and only then executing jQuery. Also, you've forgotten to include jQuery in your scripts.js file.
Modify the
scripts.js file:
import $ from "jquery";
if (typeof window !== "undefined") {
$(function () {
// Rest of the code...
}
}
Next, modify the componentDidMount function in _app.js:
componentDidMount() {
window.addEventListener("storage", this.syncLogout);
}
I'm working on SEO component which needs a canonical URL.
How can I get URL of static page in Next.js with Automatic Static Optimization turned on?
Using useRouter from next/router you can get the pathname for the current page and use it in a <Head/> tag as following:
import { useRouter } from "next/router";
const site = "https://gourav.io";
const canonicalURL = site + useRouter().pathname;
<Head>
<link rel="canonical" href={canonicalURL} />
</Head>
Building on fzembow's comment about useRouter().asPath and GorvGoyl's answer, here's an implementation which manages to handle both dynamic routes and excludes anchor and query param URL extensions:
import { useRouter } from "next/router";
const CANONICAL_DOMAIN = 'https://yoursite.com';
const router = useRouter();
const _pathSliceLength = Math.min.apply(Math, [
router.asPath.indexOf('?') > 0 ? router.asPath.indexOf('?') : router.asPath.length,
router.asPath.indexOf('#') > 0 ? router.asPath.indexOf('#') : router.asPath.length
]);
const canonicalURL= CANONICAL_DOMAIN + router.asPath.substring(0, _pathSliceLength);
<Head>
<link rel="canonical" href={ canonicalURL } />
</Head>
use package called next-absolute-url . It works in getServerSideProps. Since getStaticProps run on build time so dont have data available.
can be used as
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const { req, query } = ctx;
const { origin } = absoluteUrl(req);
return {
props: {
canonicalUrl: `${origin}/user-listings`,
},
};
};
export const getStaticProps:GetStaticProps = async (ctx) => {
return {
props: {
canonicalUrl: 'https://www.test.com',
},
};
};
A super ugly, yet, the most adequate solution I found:
const canonicalUrl = typeof window === 'undefined' ?
'' :
`${window.location.origin}/${window.location.pathname}`;
My solution was to use new URL with router.asPath.
const CURRENT_URL = process.env.NEXT_PUBLIC_CURRENT_SITE_URL
const getCanonical = (path: string) => {
const fullURL = new URL(path, CURRENT_URL)
return`${fullURL.origin}${fullURL.pathname}
}
export const Page = () => {
const router = useRouter()
const canonical = getCanonical(router.asPath)
<Head>
<link rel="canonical" href={canonical} />
</Head>
<div>
...
<div>
}
I added a canonical link tag to _app.js so that it appears on every page:
<link
rel="canonical"
href={typeof window !== 'undefined' && `${window.location.origin}${useRouter().asPath}`}
key="canonical"
/>
I have a custom Web Component, <app-list> that I'm trying to extend into <calcs-list>.
// app-list.html
<script>
window.customElements.define('app-list',
class AppList extends HTMLElement {
constructor() {
super();
}
}
);
</script>
In calcs-list.html I've got:
<link rel="import" href="app-list.html">
<script>
window.customElements.define('calcs-list',
class CalcsList extends AppList {
constructor() {
super();
console.log('CalcsList constructed');
}
}
);
</script>
However, I get the error
Uncaught ReferenceError: AppList is not defined at calcs-list.html:11
Line 11 references class CalcsList extends AppList {
Both files are siblings of the same folder. I tried using an absolute path when importing app-list.html into calcs-list.html but got the same result.
I also tried importing both components into my main index.html file:
//index.html
<link rel="import" href="/src/components/app-list.html">
<link rel="import" href="/src/components/calcs-list.html">
<app-list></app-list>
<calcs-list></calcs-list>
But experience the same result.
The app-list component works in my application without any issue.
I'm scratching my head on this one and because Web Components are considerably new, there isn't a whole lot of troubleshooting info online, especially with V1 of Web Components.
Thanks!
It's because when you write:
customElements.define('app-list',
class AppList extends HTMLElement {}
);
the class AppList is defined only in in the scope of the define() call. That's why it's not seen when you use it after in the second import file.
Instead, you should first define the class (globally) and then use it in the custom element definition:
// app-list.html
<script>
class AppList extends HTMLElement {
constructor() {
super();
}
}
window.customElements.define('app-list', AppList);
</script>
Thanks to #Supersharp, I re-wrote my custom component declaration as such:
// app-list.html
<script>
class AppList extends HTMLElement { ... }
customElements.define('app-list', AppList);
</script>
And calcs-list.html:
<script>
class CalcsList extends AppList { ... }
customElements.define('calcs-list', CalcsList);
</script>
A note of caution: If you declare a tag within the parent element (the element that's being extended) with an id then this will conflict with the extended element's call to super().
For example:
<template id="app-list">
...
</template>
The way to work around this is to use a JavaScript string literal, as referenced by the Google Developers, and not use an id at all.
<script>
let template = document.createElement('template');
template.innerHTML = `
<style> ... </style>
<div> ... </div>
`;
class AppList extends HTMLElement {
constructor() {
super();
let shadowRoot = this.attachShadow({mode: 'open'}).appendChild(template.content.cloneNode(true));
}
}
</script>