We are working on a nextjs app with i18n. Now nextjs adds the language code in url automatically like /{language-code}/subroute/my-page. But we need it to be like /subroute/{language-code}/my-page. Is it possible through some configuration?
I checked basePath at https://nextjs.org/docs/api-reference/next.config.js/basepath, but it adds a prefix to ALL pages in the app. We just need some pages.
Also there is a so-called middleware to add custom routing rules: https://nextjs.org/docs/advanced-features/middleware. Is it possible to use it to need our needs?
I am learning ASP.NET coming from a Node.js background.
When I create a new MVC project, I can choose to have built-in register/login.
This gives me the following views, where I can register and login.
But I am confused as I cannot find the corresponding controller or views in the directory, which is problematic if I want to customize the behaviour.
Can someone shed the light on how this works and where are the controller and view? Thanks.
It is not in project folder. UI is loaded from Microsoft.AspNetCore.Identity.UI library.
You can check it's code in below URL. you can understand how to configure by looking at code.
https://github.com/dotnet/aspnetcore/tree/main/src/Identity/UI/src
They are not using MVC style, they are using Razor pages with code behind C# model.
You can provide your own UI by using attribute like [IdentityDefaultUI(typeof(LoginModel<>))] on your page model.
Reference:
https://github.com/dotnet/aspnetcore/blob/main/src/Identity/UI/src/Areas/Identity/Pages/V5/Account/Login.cshtml.cs
The controller folder contains the controller file in which you will see the server side(C#) code it will receive request from view and process on it and send back to view. And views folders contains the html code that get input from user. In this project structure, you will see shared folder that contains generic view files and _loginpartial.cshtml is login view.
We have a large already existing MVC 5 ASP.NET 4.5.1 web application. The core concept since there are so many areas it covers, is that each page was it's own application. All the existing pages don't use anything besides JQuery, regular Javascript, and Handlebars templating.
Angular 2 seems very exciting, but I'm trying to figure out how exactly it will work with our philosophy. For example below is how our mvc routing serves up our separate apps currently...
Area/Controller1/Action1(App1)
Area/Controller1/Action2(App2)
Area/Controller2/Action1(App3)
etc.
Then we have separate API controllers to serve up our JSON data. From initial readings/learnings of Angular 2 I can't seem to wrap my head around how I would serve separate apps (since everything I can find always wants the index.html as the home, which makes sense if you really are making a SPA). Essentially, we're trying to continue development with multiple SPAs served up via this existing structure, not have to change older "legacy" apps on the site to use Angular until they are revisited individually, and figure out how to do the routing effectively.
Routing:
So an example in my head would be
Area/Controller/Action(App)/routeUrlstuff
I still want to be able to let them copy and paste that extended link and be brought back using the angular routing, instead of maybe just cutting off that URL and starting the angular app at it's starting point.
I don't really have any code to show, as I'm attempting to do a current project as a proof of concept that using Angular 2 from now on is viable for the application.
I recently worked on a project which had similar concerns. We did thought about implementing multiple SPAs but in the end decided to implement one SPA with multiple modules.
I think we can extend that solution for multiple SPAs as well. Let’s look at a simple use case:
You want to create 2 SPAs
UserApp containing 2 modules (AddUser and ManageUser)
ProductApp containing 2 modules (AddProduct and ManageProduct)
You have following MVC controller actions that you want to use for above SPAs:
myApp/user/add
myApp/user/manage
myApp/product/add
myApp/product/manage
MVC controller actions 1 and 2 are to be used with SPA UserApp, routing to AddUser and ManageUser modules. Similarly, controller actions 3 and 4 are to be used with SPA ProductApp, routing to AddProduct and ManageProduct modules.
Conceptually this looks like:
Multiple SPAs with multiple modules
Angular Bundling:
I would leave the typescript transpiling and bundling to Angular CLI. If your project gets complex and you feel that the angular cli can’t handle your bundling needs, you can always eject webpack configuration.
There is a really good blog from Yakov Fain that you can look for configuring cli to bundle multiple SPAs. Basically you will be configuring angular cli to output your SPAs to different dist folders. In our case, let’s assume that these will be:
userAppDist for UserApp SPA
productAppDist for ProductApp SPA
MVC Layouts:
To Load different SPAs on different pages, you will have to create different layouts or sub-layouts for each SPA.
Let’s Say: _userLayout.cshtml for UserApp
Inside _userLayout.cshtml, you will have to load your scripts from userAppDist folder
Something like this:
<main class="layout">
<base href="/">
</main>
#*Now load scripts from userAppDist*#
Similarly you will have to implement layout for the other SPA loading scripts from productAppDist. Let’s say _productLayout.cshtml
Routes:
To keep things simple you can match your server routes to angular SPA module routes. Otherwise, you will have to implement HashLocationStrategy on angular App.
Assuming you are going with the simple option, you have following views:
myApp/user/add --> AddUser.cshtml
myApp/user/manage --> ManageUser.cshtml
myApp/product/add --> AddProduct.cshtml
myApp/product/manage --> ManageProduct.cshtml
AddUser.chtml and ManageUser.cshtml will use _userLayout and will look like:
<user-app></user-app>
This is targeting UserApp SPA
AddProduct.cshtml and ManageProduct.cshtml will use _productLayout and will look like:
<product-app></product-app>
This is targeting ProductApp SPA
The MainAppComponent templates for these Apps will have
<router-outlet></router-outlet>
which will resolve to the angular module routes, based on the routes from the server. Now you have to match the routes in you angular Apps
Example for UserAppRoutingModule:
const routes: Routes = [
{ path: 'myApp/user/add', loadChildren: 'userApp/modules/add-user/add-user.module#AddUserModule' },
{ path: 'myApp/user/manage', loadChildren: 'userApp/modules/manage-user/manage-user.module#ManageUserModule' }
];
Similarly, you will have to define matching routes for the product pages in product App SPA.
Hope this helps.
Update: Configuration to avoid multiple route files
For above use case, there will be following controllers:
UserController with index action pointing to user/index.cshtml (using _userLayout) having following code:
<user-app></user-app>
ProductController with index action pointing to product/index.cshtml (using _productLayout) having following code:
<product-app></product-app>
You will also need to modify you routeConfig.cs to include following:
routes.MapRoute("user", "user/{*catchall}", new { controller = "User", action = "Index", id = UrlParameter.Optional });
routes.MapRoute("product", "product/{*catchall}", new { controller = "Product", action = "Index", id = UrlParameter.Optional });
Above changes will force following behaviour:
Routes to myapp/user --> will go to user/index.cshtml
Also, any extended routes after myapp/user/blah/blah will still resolve to user/index.cstml
User/index.chtml spins up Angular User SPA and then angular routes will kick in. You will observe similar behavior from product route and product/index.cshtml as well.
Final route configuration on angular SPA user:
const routes: Routes = [
{ path: 'user', redirectTo: 'user/add' },
{ path: 'user/add', loadChildren: 'userApp/modules/add-user/add-user.module#AddUserModule' },
{ path: 'user/manage', loadChildren: 'userApp/modules/manage-user/manage-user.module#ManageUserModule' }
];
The first route is default for user app. This maps to the MVC route myapp/user. Rest of the routes are not required to match MVC routes. You will have to do similar configuration on product SPA as well.
From a solution architect perspective, I would recommend changes both on server side and client side.
To being with, lets say there are 3 apps
Area/Controller1/Action1(App1)
Area/Controller1/Action2(App2)
Area/Controller2/Action1(App3)
1. Identify SPA's
Develop angular SPA for main application. The view from the main application has links to Controller1. This should be a route in your angular SPA. However Controller1/Action1 should be a route not available in angular SPA. It needs to be requested directly from the server using html href links. This essentially means page reload.
2. Project Locations on Server
Once development is complete for different SPA's, generate the production build with compressed JS and index.html.
Place the different SPA under their respective folders. For example, /home/user/proj/Controller1/Action1 is a folder which has independent SPA
proj
-- Controller 1
---- Action 1
---- Action 2
-- Controller 2
---- Action 1
3. Webserver Configuration
Configure your web/app server. For example in nodejs. (IIS will have something similar)
# For Action SPA
location = /area/(.*)/(.*) {
root /home/user/proj/$1/$2;
}
# For main SPA
location = / {
root /home/user/proj;
}
where $1 will contain target Controller
and $2 will contain target Action
The index.html from /area/target Controller/target Action will be fetched and these Action are independent SPA of their own.
4. Summary
Different teams can work on Main, App1, App2 and App3 as independent deliverables with this approach.
The idea is to configure MVC routing to map all possible urls for each controller to the same corresponding views i.e.:
routes.MapRoute("app1", "app1/{*catchall}", new {controller = "App1", action = "Index", id = UrlParameter.Optional});
routes.MapRoute("app2", "app2/{*catchall}", new {controller = "App2", action = "Index", id = UrlParameter.Optional});
Each Index.cshtml will have its own script section pointing to corresponding angular app. So if you paste URL app2/angular/dashboard/page it will route too corresponding controller and view (App2/Index.cshtml), then when angular router kicks in it will open the rest part of URL /angular/dashboard/page page.
Then adjust base url and angular routing for each app, which it depends on your app structure.
You don't have to do the routing in Angular for the overall site. If you're already doing routing on the ASP.NET MVC side just keep it that way. Think of it as each page request is it's own angular application. That means each page has it's own ng-app in the body, and it includes it's own angular.js and whatever that app.js is as well. app.js will init angular and setup the controllers/components you use for that page only. Each page would do that. If you have multiple views within each app, then you can use angular routing for just those views for just that app. When a person clicks on a new app link, it uses ASP.NET server side routing to give the new page that will init angular, setup inter-page routing on client side for just that app, and setup controllers used by that app.
I am not sure if i got the whole image but will try to help by telling you how i do multiple SPAs with Angular.
In _Layout.chtml, you create the following
<body data-ng-app="appMain">
<main>
#RenderBody()
</main>
</body>
In Home.chtml with above layout set, you write the following
<script src="#Url.Content("~/Models/HomeModel.js")" type="text/javascript"/>
<script src="#Url.Content("~/ViewModels/HomeViewModel.js")" type="text/javascript"/>
<div data-ng-controller="HomeViewModel" ng-init="">
home view html content
</div>
In HomeController.cs you create the Index action that return the above
For routing and multiple modules, angular is very good on that. See another sample code below:
CreateAccount.chtml with above layout set.
<script src="#Url.Content("~/Models/AccountCreateModel.js")" type="text/javascript"/>
<script src="#Url.Content("~/ViewModels/AccountCreateViewModel.js")" type="text/javascript"/>
<div data-ng-app="accountCreate" data-ng-controller="AccountCreateViewModel">
html content
</div>
AccountCreateViewModel.js:
var accountCreateModule = angular.module('accountCreate', ['common'])
.config(function ($routeProvider, $locationProvider) {
$routeProvider.when(rootPath + 'Area/Controller/Action(App)/routeUrlstuff', { templateUrl: rootPath + 'Templates/redirectPage.html', controller: 'AccountCreateViewModel' });
$routeProvider.otherwise({ redirectTo: rootPath + 'Area/Controller/Action(App)' });
$locationProvider.html5Mode(true);
});
accountCreateModule.controller("AccountCreateViewModel", function ($scope, $window) {
// module scope code here
});
Each angular module has its own routing and business logic completely distinct from other modules. Each area, with its mvc actions (full page reload) and web api methods (JSON)) is now an SPA silo written in a very neat way.
I know that MVC is a design pattern that separates Model, View, Controller.
Model - Logic
View - Client View
Controller - connection between the two.
In case I want to change one of this things it will be easy just to change view\Model and the controller.
So is it possible to use only WebApi and MVC without Aps.Net pages (cshtml files)?
You can return html files
return new FilePathResult("path/FileName.html", "text/html");
And .cshtml files are Razor View Engine files, not Asp.Net pages.
You can alson change the view engine, see here for a list of .net view egines.
In short: yes, you can.
To elaborate: not sure what you mean, as .cshtml files are essentially the view part of MVC (the V part). ASP.NET MVC controllers by default return content of the .cshtml file by calling View() helper method.
But you can for example render html for the client inside your custom controller class without calling for a static html content. Or you might create WEB API project, with routing, models, and controllers, but no views - just plain data returned to the client.
We liked the approach to have all the pages regarding support - for example - under http://www.company.com/support. After migrating to ASP.NET MVC 3 and trying this we can run every type of page but not inside the same folder.
Is there any workaround for this?
Thanks.
If you need to mix MVC pages and non-MVC pages in the same folder, here's some tips:
Remove the default route "/{controller}/{action}/{id}" and make routes for each MVC page. That way any request that isn't caught by a route falls through to the "old" request handling.
A return View(); method call in a controller looks for a view in a folder named as the controller in the Views folder, so specify the name of the view, e.g. return View("/support/index");.
Note that the MVC views doesn't actually have to be in the folder support, you can put them anywhere you like, it's the routes that determine which URLs are handled by MVC.