Refreshing Angular page breaks the webpage due to bad routing [duplicate] - html

This question already has answers here:
Angular 2.0 router not working on reloading the browser
(32 answers)
Closed 6 years ago.
I have stored my single-page application in my server within a folder named as "myapp". I have changed the URL in the base to http://example.com/myapp/`.
My project has two pages. So I implement Angular 2 routing. I set the default page as login. When I type http://example.com/myapp/ in my browser it will redirect automatically to http://example.com/myapp/login. But if refresh that page I get a 404 error, saying that http://example.com/myapp/login is not found.
But if I run my project using the lite server everything is working. In this case the base URL in index.html will be "/". How do fix it?

Update for Angular 2 final version
In app.module.ts:
Add imports:
import { HashLocationStrategy, LocationStrategy } from '#angular/common';
And in NgModule provider, add:
{provide: LocationStrategy, useClass: HashLocationStrategy}
Example (app.module.ts):
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppComponent } from './app.component';
import { HashLocationStrategy, LocationStrategy } from '#angular/common';
#NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}],
bootstrap: [AppComponent],
})
export class AppModule {}
Alternative
Use RouterModule.forRoot with the {useHash: true} argument.
Example:(from angular docs)
import { NgModule } from '#angular/core';
...
const routes: Routes = [//routes in here];
#NgModule({
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot(routes, { useHash: true })
],
bootstrap: [AppComponent]
})
export class AppModule { }

For people (like me) who really want PathLocationStrategy (i.e. html5Mode) instead of HashLocationStrategy, see How to: Configure your server to work with html5Mode from a third-party wiki:
When you have html5Mode enabled, the # character will no longer be used in your URLs. The # symbol is useful because it requires no server side configuration. Without #, the URL looks much nicer, but it also requires server side rewrites.
Here I only copy three examples from the wiki, in case the Wiki get lost. Other examples can be found by searching keyword "URL rewrite" (e.g. this answer for Firebase).
Apache
<VirtualHost *:80>
ServerName my-app
DocumentRoot /path/to/app
<Directory /path/to/app>
RewriteEngine on
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html to allow HTML5 state links
RewriteRule ^ index.html [L]
</Directory>
</VirtualHost>
Documentation for rewrite module
nginx
server {
server_name my-app;
root /path/to/app;
location / {
try_files $uri $uri/ /index.html;
}
}
Documentation for try_files
IIS
<system.webServer>
<rewrite>
<rules>
<rule name="Main Rule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>

In fact, it's normal that you have a 404 error when refreshing your application since the actual address within the browser is updating (and without # / hashbang approach). By default, HTML5 history is used for reusing in Angular2.
To fix the 404 error, you need to update your server to serve the index.html file for each route path you defined.
If you want to switch to the HashBang approach, you need to use this configuration:
import {bootstrap} from 'angular2/platform/browser';
import {provide} from 'angular2/core';
import {ROUTER_PROVIDERS} from 'angular2/router';
import {LocationStrategy, HashLocationStrategy} from '#angular/common';
import {MyApp} from './myapp';
bootstrap(MyApp, [
ROUTER_PROVIDERS,
{provide: LocationStrategy, useClass: HashLocationStrategy}
]);
In this case, when you refresh the page, it will be displayed again (but you will have a # in your address).
This link could help you as well: When I refresh my website I get a 404. This is with Angular2 and firebase.
Hope it helps you,
Thierry

I had the same problem. My Angular application is running on a Windows server.
I solved this problem by making a web.config file in the root directory.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="AngularJS" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>

Perhaps you can do it while registering your root with RouterModule. You can pass a second object with property useHash:true like the below:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppComponent } from './app.component';
import { ROUTES } from './app.routes';
#NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
RouterModule.forRoot(ROUTES ,{ useHash: true }),],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

For people reading this that use Angular 2 rc4 or later, it appears LocationStrategy has been moved from router to common. You'll have to import it from there.
Also note the curly brackets around the 'provide' line.
main.ts
// Imports for loading & configuring the in-memory web api
import { XHRBackend } from '#angular/http';
// The usual bootstrapping imports
import { bootstrap } from '#angular/platform-browser-dynamic';
import { HTTP_PROVIDERS } from '#angular/http';
import { AppComponent } from './app.component';
import { APP_ROUTER_PROVIDERS } from './app.routes';
import { Location, LocationStrategy, HashLocationStrategy} from '#angular/common';
bootstrap(AppComponent, [
APP_ROUTER_PROVIDERS,
HTTP_PROVIDERS,
{provide: LocationStrategy, useClass: HashLocationStrategy}
]);

If you're running Angular 2 through ASP.NET Core 1 in Visual Studio 2015, you might find this solution from Jürgen Gutsch helpful. He describes it in a blog post. It was the best solution for me. Place the C# code provided below in your Startup.cs public void Configure() just before app.UseStaticFiles();
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();
}
});

Related

Angular app being able to access json file under assets folder in Microsoft Azure

Currently i have an angular 9 app which is able to access config.json file placed under assets folder.I have config service which is responsible for reading from this file. This works with no issues when i run locallly. The file path is /dist/assets/config.json.
However when i deploy this app on azure as Azure App Service (windows OS) strangely the app cannot find this config.json file even though i can clearly see the file is under assets folder. Below are the relevant code from each file. The code fails when the config service tries to grab the file config.json with an error message
Error occured while grabbing config files
config.service.ts:65 TypeError: You provided 'undefined' where a stream was expected. You can provide
an Observable, Promise, Array, or Iterable.
at subscribeTo (subscribeTo.js:27)
at subscribeToResult (subscribeToResult.js:11)
at MergeMapSubscriber._innerSub (mergeMap.js:59)
at MergeMapSubscriber._tryNext (mergeMap.js:53)
at MergeMapSubscriber._next (mergeMap.js:36)
at MergeMapSubscriber.next (Subscriber.js:49)
at Observable._subscribe (subscribeToArray.js:3)
at Observable._trySubscribe (Observable.js:42)
at Observable.subscribe (Observable.js:28)
at MergeMapOperator.call (mergeMap.js:21)
appmodule.ts
function configFactory(configService: ConfigService) {
return () => configService.loadConfig();
}
providers: [
{
provide: APP_INITIALIZER,
deps: [ConfigService],
multi: true,
useFactory: configFactory
},
ConfigService.ts
loadConfig() {
return this.http.get<Config>("assets/config.json").subscribe(x=>{
console.log("Successfully grabbed config files");
this.config=x;
},error=>{
console.log("Error in grabbing config files");
console.log(error);
});
}
web.config
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<system.webServer>
<staticContent>
<!-- this is so that app can find json file when deployed in azure -->
<mimeMap fileExtension=".json" mimeType="application/json" />
</staticContent>
</system.webServer>
angular.json
"assets": [
"src/favicon.ico",
"src/assets",
"src/web.config"
],
I also referred to this link Angular app unable to find asset files on Azure but the solution proposed there doesnt work in my case. I have also set "allowSyntheticDefaultImports": true in my tsconfig.app.json file.
ng build --prod --base-href /unrc-willis-web/
Or Try
ng build --prod --base-href "./"
Also interceptor had issues. interceptor was getting used for a non required call

Problem Using Active Reports in Angular 8

i tried to use Active Reports in Angular 8 and i did all the steps needed from this website,https://www.grapecity.com/activereportsjs/docs/GettingStarted/QuickStart/QuickStart-Angular
Still i get this error when i try to compile my app :
ERROR in The target entry-point "#grapecity/activereports-angular" has missing dependencies:
- rdlx-model
- #grapecity/ar-js-viewer/ExportPanel
- #grapecity/viewer-core
- #grapecity/ar-js-viewer
- #grapecity/viewer-core/features/search
and this is my code :
In App.Component.Html
gc-activereports-viewer [height]="height" [availableExports]="availableExports" (documentLoaded)="onDocumentLoaded($event)" #reportviewer></gc-activereports-viewer
In App.Module.Ts
import { BrowserModule } from '#angular/platform-browser';<br />
import { NgModule } from '#angular/core';<br />
import { ActiveReportsModule } from '#grapecity/activereports-angular';
import { AppComponent } from './app.component';
#NgModule({<br />
declarations: [<br />
AppComponent<br />
],<br />
imports: [
BrowserModule,
ActiveReportsModule
],
providers: [],
bootstrap: [AppComponent]
})<br />
export class AppModule { }
In App.Component.Ts
i used the exact same Code from the website.(Grapecity.com)
and the rest of the steps;
i used to run this Command as adminstrator **npm install #grapecity/activereports-angular** and it didn't work either
**CMD page shows me this message :
**<br /> D:\Angular Projects\Reporter-so> npm install #grapecity/activereports-angular<br />
npm WARN #grapecity/activereports-angular#1.1.0 requires a peer of #angular/common#^7.0.0 || ^8.0.0 but none is installed. You must install peer dependencies yourself.<br />
npm WARN #grapecity/activereports-angular#1.1.0 requires a peer of #angular/core#^7.0.0 || ^8.0.0 but none is installed. You must install peer dependencies yourself.<br />
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents#2.1.2 (node_modules\fsevents):<br />
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents#2.1.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
Write this code in tsconfig.app.json
"angularCompilerOptions": {
"enableIvy": false
}

refreshing the page results in 404 error- Angular 6

I am building an application with the help of Angular6 and facing problems in routing. All the routes are working when I click on a particular tab but whenever I refresh the current page, it is throwing 404 error. I have seen many posts regarding this issue on Stack overflow but failed to overcome from this problem.
Below is my app.module.ts
import {BrowserModule} from '#angular/platform-browser';
import {NgModule} from '#angular/core';
import {RouterModule, Routes} from '#angular/router';
import {AppComponent} from './app.component';
import {FetchApiComponent} from './fetch-api/fetch-api.component';
import {FormsModule} from '#angular/forms';
import {HttpClientModule} from '#angular/common/http';
import {UserServiceLatest} from './fetch-latest/app.service';
import {UserServiceTop} from './fetch-top/app.service';
import {YoutubePlayerModule} from 'ngx-youtube-player';
import {SidebarComponent} from './sidebar/sidebar.component';
import {FetchLatestComponent} from './fetch-latest/fetch-latest.component';
import { FetchTopComponent } from './fetch-top/fetch-top.component'
import {UserService} from './fetch-api/app.service';
import { ServiceWorkerModule } from '#angular/service-worker';
import { environment } from '../environments/environment';
import { AngularFireModule } from 'angularfire2';
import * as firebase from 'firebase';
import { firebaseConfig } from './../environments/firebase.config';
import { AngularFireDatabaseModule } from 'angularfire2/database';
import {PushService} from './push.service';
const appRoutes: Routes = [
{
path: '',
component: FetchApiComponent
},
{
path: '/latest',
component: FetchLatestComponent
},
{
path: '/top',
component: FetchTopComponent
},
{
path :'*',
component: FetchApiComponent
}
];
firebase.initializeApp(firebaseConfig);
#NgModule({
declarations: [
AppComponent,
FetchApiComponent,SidebarComponent, FetchLatestComponent, FetchTopComponent
],
imports: [
RouterModule.forRoot(appRoutes),
BrowserModule, YoutubePlayerModule,
FormsModule,
AngularFireModule.initializeApp(firebaseConfig),
AngularFireDatabaseModule,environment.production ?ServiceWorkerModule.register('firebase-messaging-sw.js'):[],ServiceWorkerModule.register('/firebase-messaging-sw.js', { enabled: environment.production }),
HttpClientModule,environment.production ? ServiceWorkerModule.register('ngsw-worker.js') : [], ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production })
],
providers: [UserService,UserServiceTop,UserServiceLatest,PushService],
bootstrap: [AppComponent]
})
export class AppModule {}
Can you point me in right direction?
You will see in your example url, that once you get the 404 error you can't make it work, but if you include a hash before the angular-specific url like /#latest it will work.
Why stops working when refreshing? your webserver is intercepting the GET request from your browser and is trying to go directly to the directory /latest, which doesn't exist. It doesn't know that it needs to go to /bosv2, find an angular app, and then add the small ending bit to your path which is a not-real directory but a routing for angular. In your local it would work as when you are doing ng serve, webpack webserver is prepared for this, but not the host where you are hosting the app.
By default, angular is using HTML5 style navigation, but with your current webserver settings you would need the old angularjs style (with hash#).
From here, you have two solutions:
Change your webserver configuration
Tell Angular to use HashLocationStrategy (perfectly valid solution), you can go old-school with the HashLocationStrategy by providing the useHash: true in an object as the second argument of the RouterModule.forRoot in the AppModule.
#NgModule({
imports: [
...
RouterModule.forRoot(routes, { useHash: true }) // .../#/latest/
],
...
I would say going the hash style has a couple of downsides, which may not be relevant in your scenario:
It doesn't produce the clean and SEO Friendly URLs that are easier for users to understand and remember.
You can't take advantage of the server-side rendering.
Hope you find this answer helpful :)
To avoid using hashed routes, you must edit your webserver configuration properly, which is the best solution. You just have to configure it so it fallbacks to index.html, which is Angular's bootstrap. Although there is no universal configuration for this, here are some:
Apache
Add a rewrite rule to .htaccess file
RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html
Nginx
Use try_files in your location block
try_files $uri $uri/ /index.html;
IIS
Add a rewrite rule to web.config
<system.webServer>
<rewrite>
<rules>
<rule name="Angular Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
GitHub Pages
You can't configure it directly, but you can add a 404 page. Copy index.html into 404.html in the same directory or add a symlink: ln -s index.html 404.html.
Firebase hosting
Add a rewrite rule.
"rewrites": [ {
"source": "**",
"destination": "/index.html"
} ]
Source: https://angular.io/guide/deployment#server-configuration
With .htaccess you can try with following way also:
<IfModule mod_rewrite.c>
RewriteEngine on
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html
# to allow html5 state links
RewriteRule ^ index.html [L]
</IfModule>
In app.module.ts
import {LocationStrategy, HashLocationStrategy} from '#angular/common';
After import add following line to providers.
{provide: LocationStrategy, useClass: HashLocationStrategy}
ex:
providers: [AuthService,
AuthGuard,
FlxUiDataTable,
{provide: LocationStrategy, useClass: HashLocationStrategy}]
This will solve your issue. Read Documentation here.
Add .htaccess file to your src folder.
.htaccess file
<IfModule mod_rewrite.c>
RewriteEngine on
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html
# to allow html5 state links
RewriteRule ^ index.html [L]
</IfModule>
Load .htaccess file in your build directory dist by adding it to assets in angular.json
"assets": [
"src/favicon.ico",
"src/assets",
"src/.htaccess"
],
I think you are getting 404 because your are requesting http://localhost/route which doesn't exist on tomcat server. As Angular 2 uses html 5 routing by default rather than using hashes at the end of the URL, refreshing the page looks like a request for a different resource.
When using angular routing on tomcat you need to make sure that your server will map all routes in your app to your main index.html while refreshing the page. There are multiple way to resolve this issue. Whichever one suits you you can go for that.
1) Put below code in web.xml of your deployment folder :
<error-page>
<error-code>404</error-code>
<location>/index.html</location>
</error-page>
2) You can also try using HashLocationStrategy with # in the URL for routes :
Try using:
RouterModule.forRoot(routes, { useHash: true })
Instead of:
RouterModule.forRoot(routes)
With HashLocationStrategy your urls gonna be like:
http://localhost/#/route
3) Tomcat URL Rewrite Valve : Re-write the url's using a server level configuration to redirect to index.html if the resource is not found.
3.1) Inside META-INF folder create a file context.xml and copy the below context inside it.
<? xml version='1.0' encoding='utf-8'?>
<Context>
<Valve className="org.apache.catalina.valves.rewrite.RewriteValve" />
</Context>
3.2) Inside WEB-INF, create file rewrite.config(this file contain the rule for URL Rewriting and used by tomcat for URL rewriting). Inside rewrite.config, copy the below content:
RewriteCond %{SERVLET_PATH} !-f
RewriteRule ^/(.*)$ /index.html [L]
Starting with Apache 2.4, you can use the FallbackResource directive instead of rewriting, so it will look like:
FallbackResource /index.html
If you have a different base href (say, /awesomeapp), change it for:
<Location /awesomeapp>
FallbackResource /awesomeapp/index.html
</Location>
If you are using cpanel then it is easy to solve this issue.
Go to Advanced Options
Step 1: Go to Error Pages.
Step 2: Copy your index.html code and paste it in 404.shtml.
That's it technically all your routes are redirected to index.html file. That's what angular wants :) and everything will work normal.
Here are some reference links
Namecheap Error Page Config
Godaddy Error Page config
In my case i did following thing
Method 1 :
in your app.module.ts import below thing
import { HashLocationStrategy, LocationStrategy } from '#angular/common';
#NgModule({
declarations: [...],
imports: [...],
providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}],
bootstrap: [AppComponent]
})
and build with
ng build --base-href /[PROJECT_NAME]/
method 2 :
for nginx,
nano /etc/nginx/sites-available/default
add follwing line in location block
location /[PROJECT_NAME] {
try_files $uri $uri/ /[PROJECT_NAME]/index.html;
}
sudo service nginx restart
and build with
ng build --base-href /[PROJECT_NAME]/
Refetch data on same URL navigation
imports: [RouterModule.forRoot(routes, {
onSameUrlNavigation: 'reload' })],
Make an .htaccess file and add this code; routes will start working:
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
RewriteRule ^ /index.html

angular2-google-maps with angular2 app

I have setup the angular2-google-map with angular2 application.
And here is the app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppComponent } from './app.component';
import { AgmCoreModule} from 'angular2-google-maps/core';
#NgModule({
imports: [ BrowserModule, AgmCoreModule.forRoot({apiKey: 'AIzaSyAe3ci9yavJcWt7MaJE8DusAuZo-QeRqkU'}) ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ],
})
export class AppModule { }
And i installed the angular2-google-maps with the command,
npm install angular2-google-maps --save
which got successfully installed. when i run the application with npm start,
it shows an error saying,
http://localhost:3000/angular2-google-maps/core Failed to load
resource: the server responded with a status of 404 (Not Found)
but i can see the angular2-google-maps folder inside the node_modules.
what is the issue?
You forgotten to add map for in system.config.js
'angular2-google-maps/core': 'npm:angular2-google-maps/core/core.umd.js'
By adding above to systemjs configuration. Module loader will understand to load core.umd.js when you tries to import angular2-google-maps/core .

Update react-router and react-router-relay to v2.x from v1.x (Location "/" did not match any routes)

I'm trying to update react-router to v2.6 and react-router-relay to v0.7 in my app but I'm struggling to follow the changelogs to address all breaking changes. I think I addressed all changes but I still can't make it work.
Warning: [react-router] Location "/" did not match any routes
Here is a step by step guide on what I did to address the changes. First I updated the npm modules.
My previous package.json where everything worked:
"dependencies": {
"babel-polyfill": "^6.9.1",
"babel-runtime": "^6.9.2",
"graphiql": "0.7.3",
"graphql": "^0.6.2",
"history": "1.13.1",
"isomorphic-fetch": "^2.1.1",
"react": "^15.2.1",
"react-addons-shallow-compare": "^15.2.1",
"react-dom": "^15.2.1",
"react-loader": "^2.0.0",
"react-relay": "^0.9.2",
"react-router": "1.0.0-rc3",
"react-router-relay": "^0.7.0",
"superagent": "^1.2.0"
},
"devDependencies": {
"babel-core": "^6.11.4",
"babel-eslint": "^6.1.2",
"babel-jest": "^13.2.2",
"babel-loader": "^6.2.4",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-transform-runtime": "^6.9.0",
"babel-preset-es2015": "^6.9.0",
"babel-preset-react": "^6.11.1",
"babel-preset-stage-0": "^6.5.0",
"babel-preset-stage-1": "^6.5.0",
"babel-preset-stage-2": "^6.11.0",
"babel-relay-plugin": "^0.9.2",
"jest-cli": "^12.1.1",
"react-addons-test-utils": "^15.2.1",
"webpack": "^1.13.1"
},
My new package.json, where I'm getting errors:
"dependencies": {
"babel-polyfill": "^6.9.1",
"babel-runtime": "^6.9.2",
"graphiql": "0.7.3",
"graphql": "^0.6.2",
"isomorphic-fetch": "^2.1.1",
"react": "^15.2.1",
"react-addons-shallow-compare": "^15.2.1",
"react-dom": "^15.2.1",
"react-loader": "^2.0.0",
"react-relay": "^0.9.2",
"react-router": "^2.6.0",
"react-router-relay": "^0.13.3",
"superagent": "^1.2.0"
},
"devDependencies": {
"babel-core": "^6.11.4",
"babel-eslint": "^6.1.2",
"babel-jest": "^14.0.0",
"babel-loader": "^6.2.4",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-transform-runtime": "^6.9.0",
"babel-preset-es2015": "^6.9.0",
"babel-preset-react": "^6.11.1",
"babel-preset-stage-0": "^6.5.0",
"babel-preset-stage-1": "^6.5.0",
"babel-preset-stage-2": "^6.11.0",
"babel-relay-plugin": "^0.9.2",
"jest-cli": "^12.1.1",
"react-addons-test-utils": "^15.2.1",
"webpack": "^1.13.1"
},
I removed the history module because it is now a direct dependency of react-router. Once I installed the latest modules, I reloaded my page and got the following errors:
Error 1:
Warning: [react-router] It appears you have provided a deprecated history object to <Router/>, please use a history provided by React Router with import { browserHistory } from 'react-router' or import { hashHistory } from 'react-router'. If you are using a custom history please create it with useRouterHistory, see https://github.com/reactjs/react-router/blob/master/upgrade-guides/v2.0.0.md#using-history-with-router for details.
Error 2:
Warning: Failed context type: Invalid prop/context relay supplied to Relay(App), expected undefined to be an object conforming to the RelayEnvironment interface.
in Relay(App) (created by RouterContext)
in RouterContext (created by Router)
in Router
Error 3:
Warning: Failed context type: Required context route was not specified in Relay(App).
in Relay(App) (created by RouterContext)
in RouterContext (created by Router)
in Router
Error 4:
Uncaught Invariant Violation: RelayContainer: Relay(App) was rendered with invalid Relay context undefined. Make sure the relay property on the React context conforms to the RelayEnvironment interface.
The first error gives a handy link to the upgrade-guides for react-router, so I followed it and updated my app.js file accordingly.
Before my changes it looked this:
import babelPolyfill from 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import ReactRouterRelay from 'react-router-relay';
import Loader from 'react-loader';
import { Router, Route } from 'react-router';
import createBrowserHistory from 'history/lib/createBrowserHistory';
import WelcomeQueries from './queries/WelcomeQueries';
import AppQueries from './queries/AppQueries';
import LandingQueries from './queries/LandingQueries';
import App from './components/app';
import Landing from './components/landing';
import './relay';
const renderProps = {
renderLoading: () => <Loader />,
};
ReactDOM.render((
<Router history={createBrowserHistory()} createElement={ReactRouterRelay.createElement}>
<Route component={App} queries={AppQueries} {...renderProps}>
<Route path="/myapp" component={Landing} queries={LandingQueries} {...renderProps} />
</Route>
<Route path="/myapp/tracks" component={Welcome} queries={WelcomeQueries} {...renderProps}/>
</Router>
), document.getElementById('myapp-body'));
Attempt 1
Using the update guide to 2.x I've changed my app.js file to use a custom history like this:
import babelPolyfill from 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import ReactRouterRelay from 'react-router-relay';
import Loader from 'react-loader';
import { Router, Route, useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
import WelcomeQueries from './queries/WelcomeQueries';
import AppQueries from './queries/AppQueries';
import LandingQueries from './queries/LandingQueries';
import App from './components/app';
import Landing from './components/landing';
import './relay';
const renderProps = {
renderLoading: () => <Loader />,
};
const appHistory = useRouterHistory(createHashHistory)({ queryKey: false })
ReactDOM.render((
<Router history={appHistory} createElement={ReactRouterRelay.createElement}>
<Route component={App} queries={AppQueries} {...renderProps}>
<Route path="/myapp" component={Landing} queries={LandingQueries} {...renderProps} />
</Route>
<Route path="/myapp/tracks" component={Welcome} queries={WelcomeQueries} {...renderProps}/>
</Router>
), document.getElementById('myapp-body'));
This then gives me the following error in the browser:
Error 5
Warning: [react-router] Location "/" did not match any routes
I believe that is the solution I want.. However I wasn't sure, so I also tried going the Browser (HTML5 pushState) History way and tried the following change:
Attempt 2
import babelPolyfill from 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import ReactRouterRelay from 'react-router-relay';
import Loader from 'react-loader';
import { Router, Route, browserHistory } from 'react-router'
import WelcomeQueries from './queries/WelcomeQueries';
import AppQueries from './queries/AppQueries';
import LandingQueries from './queries/LandingQueries';
import App from './components/app';
import Landing from './components/landing';
import './relay';
const renderProps = {
renderLoading: () => <Loader />,
};
const appHistory = useRouterHistory(createHashHistory)({ queryKey: false })
ReactDOM.render((
<Router history={browserHistory} createElement={ReactRouterRelay.createElement}>
<Route component={App} queries={AppQueries} {...renderProps}>
<Route path="/myapp" component={Landing} queries={LandingQueries} {...renderProps} />
</Route>
<Route path="/myapp/tracks" component={Welcome} queries={WelcomeQueries} {...renderProps}/>
</Router>
), document.getElementById('myapp-body'));
When loading this, I get back errors 2, 3 and 4. I think Attempt 1 is the correct approach but I don't know what this error means and googling it, gave me tons of answers (1, 2, which linked me to 3 and even 4 with IndexRoute), which I tried but none of the answers worked.
My App.js file looks like this, just for reference:
import React from 'react';
import Relay from 'react-relay';
import Top from './top';
export class App extends React.Component {
render() {
return (
<div>
<Top viewer={this.props.viewer}/>
{this.props.children}
</div>
);
}
}
export default Relay.createContainer(App, {
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
${Top.getFragment('viewer')}
}
`,
},
});
I'm not 100% sure if this might be the reason but I know that react-router v2.0.0-rc6 had a breaking change with the top level Router export being removed but I don't really know what that means for me? Does that I can't use Router anymore? If so, what am I supposed to instead?
After days of research and trial and error, I'm giving up and hope that someone here can help. Any pointers in the right direction would be greatly appreciated.
Finally got an answer to this question and had to add the following changes to make my code work again:
import babelPolyfill from 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import Loader from 'react-loader';
import Relay from 'react-relay';
import useRelay from 'react-router-relay';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import applyRouterMiddleware from 'react-router/lib/applyRouterMiddleware';
import WelcomeQueries from './queries/WelcomeQueries';
import AppQueries from './queries/AppQueries';
import LandingQueries from './queries/LandingQueries';
import App from './components/app';
import Landing from './components/landing';
import './relay';
ReactDOM.render((
<Router
history={browserHistory}
render={applyRouterMiddleware(useRelay)}
environment={Relay.Store}>
<Route
component={App}
queries={AppQueries}
render={({ props }) => props ? <App {...props} /> : <Loader />}>
<Route
path="/myapp"
component={Landing}
queries={LandingQueries}
render={({ props }) => props ? <Landing {...props} /> : <Loader />} />
</Route>
<Route
path="/myapp/tracks"
component={Welcome}
queries={WelcomeQueries}
render={({ props }) => props ? <Welcome {...props} /> : <Loader />} />
</Router>
), document.getElementById('myapp-body'));
Maybe this will help someone else :)