How to do routing/navigation in Elm without the # (hash) in the URL? - elm

Using the UrlParser.parseHash function i was able to successfully parse the following url:
http://localhost:8000/MyRepl.elm/#home/something-else
The behavior is as expected, when i copy paste this in the browser and hit enter - the app loads with the appropriate view.
But now i want to remove the # and for this i used UrlParser.parsePath function. I kept the rest of the code exactly as before - but for some reason this doesn't work.
When i copy paste this and hit enter:
http://localhost:8000/MyRepl.elm/home/something-else - notice no #.
The browser creates a direct request to the elm -reactor localhost server.
There is no routing happening. The elm reactor server returns a 404 - as if there is no file named /MyRepl.elm/home/something-else
But routing without # should be possible because the http://package.elm-lang.org/packages - Docs site is written in elm and there is no # in the url as you can see.
Questions:
Anyone has experienced the same problem? Any ideas how to fix this?
Or can you point me to a repo where navigation without # works as expected?

You need a backend that servers your index page for every request. After serving the index page the routing will happen as usual in Elm.
For example in express it would look something like:
router.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
router.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
Elm reactor doesn't support this.
If you are using webpack you can do the same with the historyApiFallback attribute How to tell webpack dev server to serve index.html for any route

Related

Vue direct URL is not working, only router-link click

This may be a known Vue routing thing that I am totally missing.
So I have a .vue file that uses the url /hardware.
Here is the routing
{
path: "/hardware",
name: "Hardware",
component: () =>
import(/* webpackChunkName: "hardware" */ "../views/Hardware.vue")
},
Going to /hardware directly using a link on an external site or typing it in the address bar does not work, gives me Page Not Found.
But clicking on this link in my nav bar does work.
<router-link to="/hardware">Hardware</router-link>
Am I missing something super obvious that I missed when I was learning routing? Is this because it is a single page application? Thanks in advance for any help.
Adding that I do have history mode on, wondering if this is the issue?
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
Following back from comments to answer (Netlify) Vue-router works locally and not at the hosting/deployment side like Apache/Nginx/Firebase Hosting as:
1)
Pretty-URL / Hashbang dilemma in SPA.
The server needs to redirect when your Vue project enabled history mode. in apache, just some redirect rules needed to be done via .htaccess similarly, so as most of the hosting services included Netlify (you need to check the routes redirect rules at Netlify there). As server page not found, telling us that your route doesn't have actual files under that specified /route at their side.
Previous thread: Vue Router return 404 when revisit to the url
2) If your project for Multi-page-mode instead of going hashbang SPA, Your Vue Project needed to be configured little bit further: Either via SSR or pre-rendering static files before deployment
It could be that your browser is adding a trailing slash to giving you "/hardware/" which does not match your route. In the past, I had created an alias to match both routes such as "/hardware" and "/hardware/".
I faced the same issue nowadays and decided to share my thoughts with the community.
You can easily resolve the bug just by removing mode: "history" from the Router. Then it will be automatically replaced by the hash (#) in your URLs. It's going to work then even if you'll use a direct link in the browser.
However, based on the latest SEO recommendations History mode is more preferable because URLs without # are better tracked by Google.
If you would like to save History mode, you need to enable history mode on your server. I use Express middleware and the solution in my case is next:
const express = require('express');
const history = require('connect-history-api-fallback');
const app = express();
app.use(history());
app.use(express.static('src'));
app.get('/', (req, res) => {
res.sendFile('src/index.html');
});
app.listen(3000, () => console.log('server started'));

How to serve single 404 page for entire site in ExpressJS?

I'm building out an Express web app slowly but surely and want to add a specific page to return when a 404 response occurs. Here's what I have so far:
app.js
app.get("*", function (req, res) {
res.send("What are you looking for???", 404);
});
This is after all of my middleware, route imports (I have them modularized in other files), and right before the listener fires to start the app. As it stands now, this text is shown anytime there's a 404 on the root route (/), but doesn't not display when I'm farther down in the site's directory. For example, if I'm in a subdirectory like /players/PageThatDoesNotExist no error page displays and the app times out.
What can I do to rectify this and ensure this is fired anytime there's a page not found on the app? Thanks in advance for your help.
The problem with your code is that you override all other routes with it and respond to any incoming request with 404 status. Try to register 404 route handler like this and make sure it goes after all your other routes.
app.use(function(req, res){
res.status(404).render('404.hbs');
});
And then render your entire custom 404 page, or alternatively
res.send('<h1>No page found</h1>');
Response with some string value

Heroku adding 'undefined' to BASE_URL environment variable

attempting to deploy a vuejs, express app to heroku. The app displays, but cannot access the api, because heroku seems to add 'undefined' in the middle of the base_url. Everything works locally as expected.
Here is my heroku config var:
BASE_URL: https://goore-todos.herokuapp.com/
Here is the Vuejs component api request:
fetchTodo () {
let uri = process.env.BASE_URL + '/api/all';
axios.get(uri).then((response) => {
this.todos = response.data;
});
},
As mentioned, this works locally.
the console shows the following error:
VM71:1 GET https://goore-todos.herokuapp.com/undefined/api/all 404 (Not Found)
and the view is empty.
requests to https://goore-todos.herokuapp.com/api/ via Postman work as expected.
In this case it looks like process.env.BASE_URL is undefined. As it is undefined, the url you're trying to access is considered to be relative. That means it uses your current domain and appends the path to it.
If your frontend and backend are running on the same domain there is no reason to try to pass the API url as a variable as you can just use relative URLs.
If you want to access the URL via an environment variable it is a bit trickier with a frontend app. The Vue app is running on the user's browser, not on your server so you can't directly access it.
I had this problem and had to go through and hard code all of my URLs with the heroku URL. I tried resetting the config variables in Heroku which had an extra slash at the end, but in the end hard-coding each API call was the only thing that fixed it.

Deep linking single page application with dotNet core

I am using a dotNet core project to host an Angular2 application. I am having problems with the deep linking URLs.
For example, when I initially browse to http://localhost:54675/app/dashboard I get a 404 error because there is nothing to serve at app/dashboard. I want to actually load index.html (the angular app) and then have routing take me to app/dashboard.
I use the code below to redirect to index.html if I get a 404 and the URL has no extension.
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
{
context.Request.Path = "/index.html";
await next();
}
});
This will not work when I have a routing with parameters that include JSON such as:
http://localhost:54675/app/repairReturnListing;filter=%7B%22Status%22:[%22AWP%22]%7D
My if statement ignores requests with an extension and Path.HasExtension throws and ArgumentException on this path. The path resolves to this on the server side:
"/app/repairReturnListing;filter={\"Status\":[\"AWP\"]}"
I removed the 'HasExtension' condition and then I get a lot of console errors looking for map files that I don't host. Like this:
Failed to parse SourceMap:
http://localhost:54675/lib/js/rxjs/operator/timeout.js.map
I don't get these errors in the network tab. I think this is something used for debugging.
My angular2 app uses HTML5 routing. I use static files to serve the angular2 application. I have one webApi controller that returns some configuration data (the rest of the data is returned by another webApi project).
Waiting on a 404 and redirecting seems like a work-around and it's not even working.
Any suggestions on the best way to do this?
Check out ng2-kestrel-appserver http://tattoocoder.com/kestrel-as-a-static-server-for-angular/
Does exactly what you're looking for. It was created for RC2 but should work for the current release with few or no changes.

Tell Apache not to freak out over 404, and allow Ember-CLI router/adapter logic handle slug in URL

My new Ember-CLI app uses a user portal slug in the URL to display proper information to the user. For example (fake URL): http://my.server.portals.com/robertplant
I'm using a combination of router and adapter logic to get the user portal name form the url slug, and then display the data related to it. It probably needs some more work, but here's what I have so far:
Router code extract:
Router.map(function () {
this.route('portal', {path: '/:portal_slug'}, function () {
this.resource('account', {path: '/'});
});
});
Adapter code extract (for hitting the right API end point based on portal):
namespace: function () {
var portal = window.location.pathname.match(/^\/([^\/]*).*$/)[0];
return 'abc' + portal + '/api/v1';
}.property().volatile(),
I can hit the app locally (e.g.: http://localhost:4200/robertplant/) with no issues. It runs using Ember-CLI’s built in web server.
However, when I move the app to the server, which runs Apache, and try to hit it (e.g.: http://my.server.portals.com/robertplant), I get:
Not Found
The requested URL /robertplant was not found on this server.
Which makes sense I suppose, since there isn’t really a directory named the same as the slug. However, there has to be a way, I would think, to tell Apache to ignore the problem it thinks it is having, and allow the app router to handle it. The local web server is doing it somehow.
Ideally, the solution would leave the URL displayed the same. Also, re-writing the request to point to something like http://my.server.portals.com?slug=robertplant causes Ember-CLI assets to be looked for at the wrong path (can't set baseUrl dynamically).
I'd appreciate any feedback on how to set up the app in Apache to allow for this to happen.
Solution:
Say the current subdomain is my.portal.com. Create another subdomain that points to the same directory on the server. Name it my2.portal.com
For the first subdomain, add a mod rewrite rule which rewrites something like
http://my.portal.com/joe_blow
as
http://my.portal.com?portal_slug=joe_blow
This allows you to hit the url without a 404.
Set the asset paths (in the generated index.html) to point to the second subdomain. E.g.:
http://my2.portal.com/assets/app_name.js
This allows the app to find the assets without the issues associated with the rewrite or the slug in the url.
Of course, you can also place the assets anywhere else, including an S3 bucket. But in my case, I have a constraint of having to store them on the same server/network for security reasons. And my way you can deploy all the files to the same location.
That's it! Works like a charm.
The only thing I'm not fond of, is having to edit the index file after it's generated. I will try to automate it at some point.