Multipage SPAs with AngularJS & Rails using ui-router - ruby-on-rails

I have a Rails application in which I am trying to replace some views with AngularJS, but not all. In each view I replace, I want it to basically act as if it is it's own SPA. I am also trying to use ui-router to manage states within each of these SPAs.
For example, I have a Rails route that maps to a view ".../checkout/1. This triggers a Rails view in which I load the initial SPA for the flow and then let angular take over. I would like to setup ui-router states that are just specific to this checkout flow.
Where I am getting stuck is how to have states that are only specific for that flow with that base url. If I setup the states:
$stateProvider
.state('start', {
url: "/",
templateUrl: "..."
})
.state('route2', {
url: "/route2",
templateUrl: "..."
})
.state('route3', {
url: "/route3",
templateUrl: "..."
});
This works and for .../checkout/1#/, .../checkout/1#/route2, .../checkout/1#/route3.
However, it also work in my other SPA views which I do not want. So, if I do another rails view that uses another SPA, e.g. .../item/1 then the above route will also work for .../item/1#/ and .../item/1#/route2, etc.
Instead, I would like each to be it's own SPA and not conflict with each other. I am not sure how to do this. Can ui-router be somehow namespaced using the base url or can I have independent SPAs that have different stateProviders? Any thoughts on how I should go about this?
Thanks

Each SPA should have its own angular module defining an application with its own state definitions. So there can't be any conflict.

Related

Add a #/ to the start of the url in a controller redirect in Grails 3.2.3

I am building a custom angular app in Grails, but sticking as much as possible to the default Grails Controller View behaviour.
What I'm trying to do is: using the scaffolding controller. Get the same behaviour but by ading a #/ to the start of the url. So that after you save a Record, you'd be redirected to:
http://localhost:8080/#/country/show/5
instead of
http://localhost:8080/country/show/5
So that Angular kicks in again. I know this isn't the standard Angular behaviour but I'm trying to use as few angular files as possible since I have very little knowledge in angular.
The default scaffolding redirect is:
redirect country
And I tried using:
redirect base: "#/", country
redirect country, [base: "#/"]
redirect country, base: "#/"
redirect absolute: "#/", country
But they all throw 500 error when called.
This is my current app config in angular:
app.config(function($routeProvider) {
$routeProvider.when("/:controller/:action",{
templateUrl:function(params){
return '/'+params.controller+'/'+params.action;
}
})
.when("/:controller/:action/:param",{
templateUrl:function(params){
return '/'+params.controller+'/'+params.action+'/'+params.param;
}
});
});
Have you tried this?
class SomeController {
LinkGenerator linkGenerator
def action() {
redirect uri: linkGenerator.link(
controller: 'country', action: 'show', id: 5, base: '/#')
}
}
UPDATE: Given the requirement to support multiple formats, teaching angular to work with "pretty" URLs might be the only way. Here is an example posting that has the following code:
angular.module('scotchy', [])
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl : 'partials/home.html',
controller : mainController
})
.when('/about', {
templateUrl : 'partials/about.html',
controller : mainController
})
.when('/contact', {
templateUrl : 'partials/contact.html',
controller : mainController
});
// use the HTML5 History API
$locationProvider.html5Mode(true);
});
I ended up giving up on angular entirely. To show the views inside a dialog I just extract the html segment from the response returned from the server and I get all the default behaviour that Grails includes out of the box.
Since grails is a full stack framework and it's front end is highly customizable. Unless you want to build an entire angular app it's not worth replacing the front end with angular just for it's single view capabilities.
The server responses may be slightly bigger than what I need to show inside a dialog, but it doesn't hurt performance. And the controllers don't know if their views are being rendered inside a dialog or as a full web page, which is what I wanted in the first place.

using angular router in custom pages

I have a rails app with dashboard page. So the page will be http://mywebsite.com/dashboard. It has few links available which will load pages via ajax and show it in a div section inside the dashboard page. Its all working fine. So lets assume I want to use angular here and I specify code like below.
var myapp = angular.module('myapp', ["ui.router"])
myapp.config(function($stateProvider, $urlRouterProvider){
$stateProvider
.state('route1', {
url: "/route1",
templateUrl: "route1.html"
})
})
My doubt is that:
So in here if dashboard is the root url then the url generated is http://mywebsite.com/#route1
What if my dashboard page is defined like this
http://mywebsite.com/dashboard and I want to define route like http://mywebsite.com/dashboard/#route1
Note: Its not a single page application. But I want the dashboard page
to be like a single page one..
This will work fine and the route will be relative to your URL
http://mywebsite.com/dashboard.
If you were using HTML5 mode with Angular UI router if you try and interoperate the full URL. But because you are not using HTML5 mode, Angular UI router routes using #.

Adding AngularJS to existing ruby on rails app

I'm thinking about adding AngularJS to an existing app.
The app is an event management app with a dashboard action for each event.
So we have something like http://localhost:3000/events/2/dashboard
We are trying to make the dashboard view more user friendly by adding AngularJS.
The goal is to implement a controller which will retrieve the event id to make calls to other services and populate the results into the view.
I've read about the $routeParams but in most of the case the examples were dealing with single page applications with their own templates.
Is there any clean way to just retrieve the event id param from the url?
Any help would be highly appreciated.
By using the angularUI router you can retrieve the event_id param from the url and you can also have nested views.
angular
.module('paisApp')
.config ['$stateProvider', '$urlRouterProvider', ($stateProvider, $urlRouterProvider) ->
$urlRouterProvider
.otherwise("/")
# Define 'app' states. The order of the state is important.
$stateProvider
.state "event_dashboard",
parent: "default"
url: "/events/:event_id/dashboard"
views:
"":
controller: "EventsController"
templateUrl: "/assets/events/dashboard.html.erb"
Now in your events controller you can use the following to access the event_id
$stateParams['event_id']

MVC5 and Angular.js routing - URLs not matching

I'm having some trouble with routing in Angular.js and MVC5. I've simplified it down to the example below.
My angular routing code is:
app.config(["$routeProvider", "$locationProvider", function ($routeProvider, $locationProvider) {
$routeProvider.when("/", {
template: "<h1>index</h1>",
})
.when("/Home/About", {
template: "<h1>about</h1>",
})
.when("/Home/Contact", {
template: "<h1>contact</h1>",
})
.otherwise({
template: "<h1>Error</h1>",
});
}]);
The MVC Home controller has a method for each, and each view has a DIV with ng-view attribute.
When I browse to either about or contact the route is still mapping to Index.
If I change the index path to :
$routeProvider.when("/Home/Index", {
template: "<h1>index</h1>",
})
then Otherwise kicks in and I see Error.
The code looks identical to other angular code I've used, and examples on the web. Any suggestions?
Updated: Thanks to the answers below. I think I didn't explain my problem well last night, the result of a long day. My problem is that I'm trying to have a mini-spa on a sub page of the site, so the route for the main page would be:
.when("/userPermissions/index", {
templateUrl: "/scripts/bookApp/userPermissions/main.html",
controller: "userPermissionController",
})
And the path of "/userPermissions/index" which would be the page provided by the server isn't being matched by the routing.
Angular is by design a Single Page Application (SPA) framework. It is designed to process requests within a single server page request, and handle route changes without making subsequent calls to the server. Hence, for every page load, the page is at the "root" of the application. or /, no matter what path was used on the server to load the page.
Subsequent page loads and routing are handled using the 'hash' notation /#/someroute in order to suppress a browser reload. Thus, the actual route being matched by the angular $routeProvider is http://example.com/#/Home/About, but this is loaded from the / server route.
If you redirect the page to /Home/About on the server, but still want to get to that match in Angular, then the route would be http://example.com/Home/About#/Home/About. Quite problematic, as you can imagine.
HTML5 Routing Mode can be used to remove the #/ from your routes, Allowing you to match http://example.com/Home/About in the Angular $routeProvider. But, for Angular to really shine, you should also configure Rewrites on your server, and not handle these Routes as separate views in your ASP.Net application. Generally, you will have a much cleaner solution if you can restrict server communications to API calls, as mixing Server HTML (or Razor) with Client Side Angular gets very confusing very fast.
I would suggest you to create one base for your angular SPA.
That means you will need to create a C# controller inside your application that will have one action i.e. Index
SPAController.cs
using System.Web.Mvc;
namespace AngularApp.Controllers
{
public class SPAController : Controller
{
public ActionResult Index()
{
return View();
}
}
}
Views/SPA/Index
<ul>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
<div ng-view></div>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular-route.min.js"></script>
Now all is set hit html ng-view will load partial view by watching route.
Hit in browser http://anything.com/spa/#/. Then your angular app will start working on page.
I would not suggest you to use html5mode() inside MVC app. That will create many problem inside your app. And it will take more time to manipulate things.
Thanks.

Error when registering an AngularJS controller dynamically in a separate file

So I'm an Angular noob trying to introduce AngularJS to an already existent RoR environment.
Since it's a relatively heavy and robust web app, I thought the best way to go about it would be to write a controller for each section in its' own file (coffee, for the record), and load it with the view through $routeProvider, registering the controller dynamically by doing something like this (omitting irrelevant parts):
app.config [
"$routeProvider", "$controllerProvider",
($routeProvider, $controllerProvider) ->
app.registerCtrl = $controllerProvider.register
$routeProvider
.when("url1",
templateUrl: "url1.html.haml"
controller: "url1Ctrl"
)
.when("url2"
templateUrl: "url2.html.haml"
controller: "url2Ctrl"
)
.otherwise redirectTo: "/homepage"
I load the main js file (and in it the above snippet) in the layout file, and each designated controller file in the appropriate view (that is dynamic, obviously). In each of them, I try to load the relevant controller using:
#url1Ctrl = app.registerCtrl 'url1Ctrl', [
'$scope', .....,
( $scope, ....) ->
# some logic
Problem is, I get an error saying "Uncaught TypeError: undefined is not a function". I thought I was doing something wrong with the provider function but it seems I'm being a lawful citizen there.
Also read a bunch of similar questions on SO:
How to separate Controller file in angularjs (basically using ".controller" instead of registering it? Didn't work for me)
Separating Controllers in AngularJS (same idea)
How to create separate AngularJS controller files? (same, but the controller files are always loaded, not lazy-loaded dynamically...)
Loading an AngularJS controller dynamically (seems like an over-complication?)
AngularJS Controller Not Registering (tried it, not the same issue i'm having, it seems)
Would love some input on what part of Angular I'm misinterpreting here.

Resources