How to pass data from URL to JavaScript and have nested pages? - javascript

I Have 3 Questions URL Related and looking for an answer or source / topics to learn it from:
How to remove the .html extension from the URL
example: https://stackoverflow.com/questions instead of https://stackoverflow.com/questions.html
How to have Nested Pages in the URL
example: qyz.com/movies/latest/...
how do i configure these variables in a url? (-, ?v='id', &)
example: on a movie app where the user can filter the movies the the filters gets stacked and shows the matching results qyz.com/movies/2022-english-action
Note: I'm only familiar with HTML & Vanilla JavaScript

What you are trying to implement is a Single Page Application (SPA). There is not a correct answer to your question, but multiple frameworks that I could suggest to answer your question - I will tell one example first, and give you a hint of how they work (could differ from one to the other).
One of the simplest that I know is Barba.js, where you put your HTML content in multiple content tags, configure these as pages and their routes, and configure transitions.
Below is an example copied from their documentation (link to this exact documentation by clicking the above hyperlink):
<body data-barba="wrapper">
<!-- put here content that will not change
between your pages, like <header> or <nav> -->
<main data-barba="container" data-barba-namespace="home">
<!-- put here the content you wish to change
between your pages, like your main content <h1> or <p> -->
</main>
<!-- put here content that will not change
between your pages, like <footer> -->
</body>
and the router definition in your main-script.js that you import when executing HTML:
import barba from '#barba/core';
import barbaRouter from '#barba/router';
// define your routes
const myRoutes = [{
path: '/index',
name: 'home'
}, {
path: '/product/:id',
name: 'item'
}];
// tell Barba to use the router with your routes
barba.use(barbaRouter, {
routes: myRoutes
});
// init Barba
barba.init();
Now back to your question topics, which are important configurations required for any SPA:
-> All requests must be proxied to this /index.html file - if you request for /any/other/path, it should actually serve the /index.html file - see below basic NGINX configuration for reference:
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
location / { # Anything that goes beyond path /*
try_files $uri $uri/ /index.html; # it tries to open files, such as image resources, .js, etc. - if not found, it serves the /index.html
}
}
and 3. -> This is what frameworks try to simplify. They use methods to explode URL and get query parameters from URL - abstracting all the rules to display/hide content based on the URL you are. Below some snippets of these functions mentioned above.
Explode URL (source):
var url = 'http://www.mymainsite.com/somepath/path2/path3/path4';
var pathname = new URL(url).pathname;
console.log(pathname);
Parse query-params (source):
const queryString = "?product=shirt&color=blue&newuser&size=m"; // This can be fetched from "window.location.search" in real world.
const urlParams = new URLSearchParams(queryString);
const product = urlParams.get('product')
console.log(product);
const color = urlParams.get('color')
console.log(color);

Related

Redrects with special characters in Next.js

I'm setting up redirects for a client in Next.js. We have a configuration like so inside next.config.js:
{
source: '/page.aspx:urlstr*',
destination: 'https://someurl.com/page.aspx:urlstr*',
permanent: true,
},
The actual URLs hitting this will have a long URL string like so:
page.aspx?woXxJNrRIMKVC109awwTopP+k2NmkvXf+MzijTEc3zIZ3pf4n+Yknq
This is being URL encoded to be:
https://someurl.com/page.aspx?woXxJNrRIMKVC109awwTopP%20k2NmkvXf%20MzijTEc3zIZ3pf4n%20Yknq
The old server hosting these pages at the destination can't handle the URL encoded query string. Is there a way to force Next.js to not parse it?
So the solution was in fact to use the Next.js middleware feature:
https://nextjs.org/docs/advanced-features/middleware
It's a little buggy though. The docs say to add middleware.js to the same level as the pages directory, but you actually need to add _middleware.js inside the pages directory.
Also the matches feature does not seem to work for me at all, so here's my solution:
import { NextResponse } from 'next/server'
export function middleware(request) {
if (request.nextUrl.pathname.startsWith('/page.aspx')) {
let url = new URL(request.url);
return NextResponse.redirect(`https://someurl.com${url.pathname}${url.search}`)
}
}

Next.js – encodeURIComponent doesn't work with `/` in `getStaticPaths`

Minimal repro site: https://nextjs-paths-issue.vercel.app/
Minimal repro code: https://github.com/saadq/nextjs-encoding-issue
Home page
Food page
I am trying to iterate through an array of food objects and create static pages for each one based on its title. This works for most of the foods, but if the food title contains a /, then navigating to the page (such as the "Nice strawberry/kiwis dessert" page) will throw a 404.
In the home page, I encode the URL when I create the Link and then in the getStaticPaths function, I create the paths using the same encoded link. However, it doesn't seem to work when deployed.
The page does work locally when running npm run dev, but it seems that in the actual output build there are issues. Is there something I can do to allow paths with encoded slashes to work?
Home page
const HomePage: NextPage = () => (
<>
<h1>Home</h1>
<ul>
{foods.map((food) => (
<li key={food.title}>
<Link href={`/food/${encodeURIComponent(food.title)}`}>
{food.title}
</Link>
</li>
))}
</ul>
</>
)
Food page
export const getStaticProps: GetStaticProps<Props, Params> = (ctx) => {
const title = ctx.params?.foodTitle as string
const food = foods.find((food) => food.title === title) as Food
return {
props: {
food
}
}
}
export const getStaticPaths: GetStaticPaths = () => {
const paths = foods.map((food) => `/food/${encodeURIComponent(food.title)}`)
return {
paths,
fallback: false
}
}
const FoodPage: NextPage<Props> = ({ food }) => {
return (
<>
<Link href='/'>Go Back</Link>
<h1>{food.title}</h1>
<h2>Amount: {food.amount}</h2>
</>
)
}
export default FoodPage
The example I posted here is a bit contrived, for my actual app I was able to get it to work by using fallback: 'blocking'. It's not a totally exportable static website anymore unfortunately, but I only have a few pages that will run into this issue so it's fine that a few of them will have a slight loading time from the server.
https://nextjs.org/docs/basic-features/data-fetching
// We'll pre-render only these paths at build time.
// { fallback: blocking } will server-render pages
// on-demand if the path doesn't exist.
return { paths, fallback: 'blocking' }
An old thread, but I've spent many hours looking for a solution so I'd like to share.
In order to get links in the format /categories/page in getStaticPath, and at the same time to use the same component in the /category/ and in the /category/page/, but with different parameters, you should use the page format [...category].js. This allows you to pass parameters in getStaticPaths as an array:
params: {
category: [category, page],
}
In result it will generate page category/page and still have fallback: false.
Trying to add '/' directly to the path (as you did) creates html pages category%2fpage.html.
More details about static paths
And about catch all routes
Ofc, because you have / in the path. %2F symbol representing /
Just make custom function for path encoding and use it
static stringToParamUrl(input: string) {
let url = input.normalize("NFD").toLowerCase().replace(/[\u0300-\u036f]/g, "").replace(/[^a-zA-Z0-9-_]/g, "-")
return url
}
Modifications:
"I create the paths using the same encoded link. However, it doesn't seem to work when deployed." if it's worked it is not correct. Your other links are working exept those containing "/". "/" is a reserved character for the URL. In next js it's working as "separator".
Also (about Link):
You need to have <a> tag in Link
<Link><a>something</a></Link>
Also (about fallback):
Fallback is not changing the logic of URLs. Using fallback will generate static page on the "first demand" - nothing about URL not working.
You had no errors during deployment time because you didn't create static page (fallback false)
What you need:
"I am trying to iterate through an array of food objects and create static pages for each one based on its title." = you need to add to your database a field called "URL" or something like this, and iterate elements based on URL *you previously generated while creating your array) and not name, because:
You need to have "unique" URL (to get data from your api via [pid] parameters or something)
You need to avoid reserved and forbidden symbols in URL
It's better to prettify url. ( I shared a function with you)
Maybe I didn't understand your question correctly

Vue Router, GitHub Pages, and Custom Domain Not Working With Routed Links

My domain: myname.com
My GitHub repo: myname
My GitHub name: myname
Underlying GH-Pages URL: myname.github.io/myname
My Issue: I have the following pages set up with a History Vue Router: home, contact, projects.
Whenever I go to myname.com/contact, it routes me to 404 instead. When I click on a link to redirect me to /contacts, it pushes it to the address bar but if you refresh, it routes to 404. I have read posts such as: Deploy Vue to GitHub Pages. Error with vue-router and Vue router on page refresh gets 404 but the issue persists and the custom domain is an added complexity. I have also read this: Vue Router return 404 when revisit to the url but, there is an example of a React Router page with a Browser Router working on GH-Pages: https://milosrancic.github.io/reactjs-website/activities, repo: https://github.com/milosrancic/reactjs-website.
Here is what I have done:
My package.json has the following line: "homepage": "https://myname.com/"
My vue.config.js is exactly this:
module.exports = {
publicPath: '/'
}
My Router is on my path: /src/router/index.js, passed into main.js new Vue(), and is exported like this:
export default new VueRouter({
base: process.env.BASE_URL,
mode: 'history',
routes
})
Inside my App.vue (entry component), the template looks like this:
<template>
<div id="app">
<nav-bar></nav-bar>
<router-view></router-view>
</div>
</template>
I was unable to fix the problem through Vue but, as tao mentioned, this is likely an issue with GitHub and how they handle URLs. The simplest fix is to use a Hash Router instead so that the entry URL stays static. However, this will introduce that, arguably distasteful /#/ in your URL.
Given how catch-all routes are not supported natively, someone has found a workaround for this: https://github.com/rafgraph/spa-github-pages. Note: This is likely not good for SEO because the intended URLs do not actually exist. This is doing a trick with their 404 redirects and handling it on the index page. This was for a portfolio site and as such, I am ok with this for now. If I, or someone else, finds a better solution, this will be updated.
Workaround:
Inside /public add a file called 404.html and paste the following contents:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CHANGE THIS TO YOUR TITLE</title>
<script type="text/javascript">
// MIT License
// https://github.com/rafgraph/spa-github-pages
// This script takes the current url and converts the path and query
// string into just a query string, and then redirects the browser
// to the new url with only a query string and hash fragment,
// e.g. https://www.foo.tld/one/two?a=b&c=d#qwe, becomes
// https://www.foo.tld/?/one/two&a=b~and~c=d#qwe
// Note: this 404.html file must be at least 512 bytes for it to work
// with Internet Explorer (it is currently > 512 bytes)
// If you're creating a Project Pages site and NOT using a custom domain,
// then set pathSegmentsToKeep to 1 (enterprise users may need to set it to > 1).
// This way the code will only replace the route part of the path, and not
// the real directory in which the app resides, for example:
// https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes
// https://username.github.io/repo-name/?/one/two&a=b~and~c=d#qwe
// Otherwise, leave pathSegmentsToKeep as 0.
var pathSegmentsToKeep = 0;
var l = window.location;
l.replace(
l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') + '/?/' +
l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') +
(l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +
l.hash
);
</script>
</head>
<body>
</body>
</html>
Inside /public/index.html, add the following inside the <head> right after <title>:
<script type="text/javascript">
// MIT License
// https://github.com/rafgraph/spa-github-pages
// This script checks to see if a redirect is present in the query string,
// converts it back into the correct url and adds it to the
// browser's history using window.history.replaceState(...),
// which won't cause the browser to attempt to load the new url.
// When the single page app is loaded further down in this file,
// the correct url will be waiting in the browser's history for
// the single page app to route accordingly.
(function(l) {
if (l.search[1] === '/' ) {
var decoded = l.search.slice(1).split('&').map(function(s) {
return s.replace(/~and~/g, '&')
}).join('?');
window.history.replaceState(null, null,
l.pathname.slice(0, -1) + decoded + l.hash
);
}
}(window.location))
</script>
This has worked for me and I can now visit the contacts page by simply entering myname.com/contact. This should also work if you have nested SPA inside /public, using the same trick.
If anyone is still looking for answers there is a simpler and neater workaround for this:
https://stackoverflow.com/a/65539760/11798104
Basically copy index.html and replace it with the name 404.html in the dist folder when you deploy
I don't totally understand your comment/confusion regarding why/how the re-routing works since you correctly summarized the first half which is Github sees it as an invalid URL and sends it to your custom 404 page. The 404 page then translates it into a query string pointed at the root page index.html.
Are you missing custom the index.html page in your set up? index.html itself decodes the query string back into "route param"-type representation so that when your app actually loads, it can initialize with the correct view and state.
I actually use this exact setup for my GH pages site: https://mirajp.github.io --> https://www.miraj.dev

Angular replace url after hashbang

In our app, we are serving static angular app files out of foobar.com/public/angular. The home page (foobar.com) is configured to serve foobar.com/public/angular/index.html. Thus, in our index file's <head>, we have:
<base href="http://foobar.com/public/angular">
However, during development, we use foobar.com/debug, which causes the server to use this instead:
<base href="http://foobar.com/public/angular-debug">
The server returns unprocessed (not minified, uglified, etc) versions of the files from this url. We are also using angular-route for our views.
TLDR:
We have a production route: foobar.com
We have a dev route: foobar.com/debug
We have a base tag: <base href="http://foobar.com/public/whatever">
We are using hash-bang urls for angular routes
The trouble is generating urls on the client side. As discussed in comments, Click here does not work because the resulting url contains the base tag, which the server rejects and looks odd since the user starts somewhere else: foobar.com/public/angular/#/myRoute). The best I could think of to create the desired url was this:
/**
* Returns current url after replacing everything after the hashbang with a new string.
*
* #param {string} s - string to change route with (e.g. 'asdf')
* #return {string} - new url e.g. http://google.com/foobar/#!/asdf
*/
makeRouteUrl = function(s) {
var root = $location.absUrl().match(/.*#!/);
if (root && (root.length > 0)) {
root = root[0];
return( root + s );
} else {
// no hashbang in path? Choose home page (not intended use)
return "/";
}
}
However, this just seems a little gross. Is there a function in angular that makes the above function unnecessary? I have looked for functions in $location, $route, etc., but nothing seems to handle our complex setup correctly. We need to ultimately get the url as a string, not navigate (we are supporting open in new tab, so need to used ng-href). Since Angular apps often use hashbang urls for routing, I figured there must be something that allows you the just change the route after the hashbang while still preserving what's in front of it.
I'm not entirely sure if I understand the problem...
$location.url('/stuff/after/hash?params=hey');
Or if you only want to set the path after #:
$location.path('/stuff/after/hash');
Is what I usually use. But you need to return the url without hash after this?
How about
window.location.origin + window.location.pathname
For the current url without hashes and parameters?
Have you considered using the UI Router library?
In general, it's excellent.
Specific to your question, the UrlMatcher service provides a method format that- unless I'm misunderstanding what you're asking- does what you're trying to do.
new UrlMatcher('/your/route/here').format(optionalNamedParameters);
// returns a formatted URL

How to reference documents in my website to be independent of the website location and usage location

I'm relatively new to client side development. I'm creating an angularJS directive which references a static html, in [root]/Static/template.html.
I guess the problem is not unique to angularJS.
Now I need this address to be root relative, so that it can be loaded regardless of where I use the directive. The problem is that I don't know where my site will be uploaded, so it might be put in www.mysite.com/ or might be www.mysite.com/system/
I also can't use relative path, as it will be sensitive to where I use the directive, so for instance if I use Static/template.html, it will be found by documents in the website root, but not in the inner folders.
What is the correct way to reference documents to be robust?
If your static folder is relative the place where your application is deployed, e.g.:
www.example.com/index.html
www.example.com/Static
www.example.com/root/index.html
www.example.com/root/Static
www.example.com/root/foobar-app/index.html
www.example.com/root/foobar-app/Static
Then you need to extract the base url and store it somewhere. window.location API could be used to do that.
E.g.:
<!-- index.html -->
<!-- should probably be placed into external script file -->
<script>
function extractPath(url) {
return url.match(/.*\//) // find all chars until the slash
}
var baseurl = window.location.origin + extractPath(window.location.pathname);
window.baseurl = baseurl; // store in global scope
</script>
This snippet shows the general idea. Now elsewhere in your code you can read the base url path to access static resources. E.g.:
var image_url = window.baseurl + "Static" + image_path;
In AngularJS you would normally store that variable in the main app controller. If you only have one factory to access static resources, you could consider storing the baseurl there.
URL that starts with / is the URL from the root.
So if you set /Static/template.html, you can access template.html from both paths(www.mysite.com/ and www.mysite.com/system/).

Categories

Resources