2

I coded an angular project on my own PC, everything worked fine when I ran it locally with the command ng serve. Now I want to deploy it on tomcat on a VM (provided by my school) because my project needs to be public.

I created a git repository and downloaded the angular project on the VM. Followed a tutorial to deploy the app in tomcat, which simply consisted of 2 steps:

  1. Run ng build --base-href=/angular/ to generate some files
  2. Copy the generated files to tomcat's /webapps subfolder

But it does not work as intended. To me it seems like there's an issue with routing.

Here's my routing module:

const appRoutes: Routes = [
  {path: 'coachPortal', component: CoachPortalComponent},
  {path: 'userPortal', component: UserPortalComponent},
  {path: 'users', component: UsersComponent},
  {path: 'userDetails/:id', component: UserDetailsComponent},
  {path: 'mySessions', component: MySessionsComponent},
  {path: '', redirectTo: '/coachPortal', pathMatch: 'full'},
  {path: '**', component: CoachPortalComponent}
];

@NgModule({
  imports: [RouterModule.forRoot(appRoutes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

To navigate between routes, I use this old simple code: window.location.href = '/users';
This worked like a charm when I ran it locally on my pc.

But I'm guessing the reason why it's not working anymore after deploying on tomcat is because tomcat adds the prefix angular/.
When running locally on my pc the urls were localhost:4200/{componenturl}.
But on the VM it's x.x.x.x:8080/angular/{componenturl}

But because of the routing redirects I am getting 404 errors. When I first visit the root url x.x.x.x:8080/angular, it matches with the rule {path: '', redirectTo: '/coachPortal', pathMatch: 'full'}. and loads the coachPortal component and even changes the url to angular/coachPortal. But refreshing the page or manually entering angular/coachPortal gives a 404 error. And all other links don't work either.

Also on the coachPortal I have an image with src ../../../assets/logo.png and it obviously doesn't load because of how the generated files in webapps folder have different folder structure... How can I fix this? The image is directly under assets folder.

M. Benamar
  • 309
  • 4
  • 14

3 Answers3

3

You can try use useHash: true. For me its working.

Example:

@NgModule({
    imports: [RouterModule.forRoot(routes, { useHash: true })],
    exports: [RouterModule],
})

and url on the VM will be x.x.x.x:8080/angular/#/{componenturl}

ErikHer
  • 227
  • 2
  • 5
  • 12
  • It does not work. The root page is blank (but there's 404 errors in the console) and any other link I enter shows 404 error on the page. – M. Benamar May 29 '19 at 18:33
2

My understanding is that when you try to navigate to a virtual sub directory that is usually resolved via Angular Routing, when it is deployed your webserver is looking for an actual directory of 'coachportal', which doesn't exist so returns a 404. If the route had been navigated to through your app (by navigation buttons etc.) Angular Router would resolve it and bring up the virtual sub-directory. Your webserver is stopping the route from getting to Angular Router to resolve it. Your webserver checks to see if there is an actual folder or file and then redirects to your root to handle the routing if there is no file or folder with that name. For web projects I have deployed on IIS I include a web.config file with the following code:

<configuration>
    <system.webServer>
      <rewrite>
        <rules>
          <rule name="Angular" 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>

from this post: Routing in Angular 7 running in subfolder not working correctly I'm not sure if it will work with your deployment setup, but should at least give you insight into what may be going on.

For your assets folder remove the ../s for a relative directory and just leave it as assets/logo.png it should handle this correctly. you can test it locally.

EDIT: changed some things for clarity

DanGo
  • 391
  • 1
  • 13
  • Thanks for the explanation! Where do you put the web.config file? – M. Benamar May 29 '19 at 16:21
  • In the root of the compiled project. I usually place it there manually and then make sure my CI tool doesn't delete it. Though if you want to add it as part of your project you probably can and keep it included as an asset on build. [Here](https://stackoverflow.com/questions/39538464/how-to-include-custom-files-with-angular-cli-build) is how that can be accomplished. @EricHer solution should work as well I just never liked the hashes in my url. The hash essentially tells the web server to ignore everything after the hash and then its sent to the root project and Angular router handles it. – DanGo May 29 '19 at 17:49
  • I just tried the hash thing and it does not work :( The root page is blank (but there's 404 errors in the console) and any other url I enter shows 404 error on the html page – M. Benamar May 29 '19 at 18:33
  • Have you tried the web.config? Does the hash strategy work locally? – DanGo May 29 '19 at 18:59
  • Sorry, hashing works! When running `ng build --base-href=/angular/` it generated a folder called 'frontend' somehow, so copying that folder to /webapps it was also called 'frontend'. I just had to rename the folder to 'angular' and now everything works! Thanks! – M. Benamar May 29 '19 at 22:48
  • in your angular.json the output path is probably set to `"outputPath": "dist/frontend",` If you set it to `"outputPath": "dist/angular",` it should save you a step. – DanGo May 30 '19 at 16:08
0

I also met this problem, and just find an easy way. Maybe it will help others. with apache-tomcat-7.0.55 and angular 11. with other versions not tested.

just based on @Erikher 's answer and something more to be done:

  • first: RouterModule.forRoot(routes, {useHash: true})

  • second*(may not need)*: providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}],

  • third: change base-href in index.html from '/' to '#'

Then it works for me.

peinearydevelopment
  • 11,042
  • 5
  • 48
  • 76