How do I setup nested views in AngularJS? - uiview

I have an application with various screens. Each screen is assigned a URL, such as #, mails, contacts, and so on.
In my main HTML file I have an ng-view element which updates according to the route the user selects. So far, so good.
Now some of these screens have a sub-navigation. E.g., #mails does have an inbox and a sent folder. They present themselfes with two columns: The sub-navigation on the left, the mails of the appropriate folder on the right.
When you navigate to #mails, it shall redirect you to #mails/inbox, so that basically inbox is the default sub-view for mails.
How could I set this up?
The only approach I can currently think of (I am quite new to AngularJS, hence forgive me if this question is a little bit naive) is to have two views, one for #mails/inbox, and the other for #mails/sent.
When you select a route, these views are loaded. When you select #mails it simply redirects you to #mails/inbox.
But this means that both views must use an ng-include for the sub-navigation. Somehow this feels wrong to me.
What I'd like more is to have nested views: The top one switches between screens such as mails, contacts, and so on, and the bottom one changes between sub-views such as inbox, sent, and so on.
How would I solve this?

AngularJS ui-router solved my issues :-)

Another library to check out: http://angular-route-segment.com
You can use it instead of built-in ng-view and $route.
Sample route config looks like this one:
$routeSegmentProvider.
when('/section1', 's1.home').
when('/section1/prefs', 's1.prefs').
when('/section1/:id', 's1.itemInfo.overview').
when('/section1/:id/edit', 's1.itemInfo.edit').
when('/section2', 's2').
segment('s1', {
templateUrl: 'templates/section1.html',
controller: MainCtrl}).
within().
segment('home', {
templateUrl: 'templates/section1/home.html'}).
segment('itemInfo', {
templateUrl: 'templates/section1/item.html',
controller: Section1ItemCtrl,
dependencies: ['id']}).
within().
segment('overview', {
templateUrl: 'templates/section1/item/overview.html'}).
segment('edit', {
templateUrl: 'templates/section1/item/edit.html'}).
up().
segment('prefs', {
templateUrl: 'templates/section1/prefs.html'}).
up().
segment('s2', {
templateUrl: 'templates/section2.html',
controller: MainCtrl});

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.

Multipage SPAs with AngularJS & Rails using ui-router

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.

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.

Handling inbound links in AngularJS

I have a single-page application and I have all my internal links working with ng-click, which dynamically loads data into ng-view. I'm trying to figure out how to handle inbound links from outside the app, for example a link to 'ROOT_PATH.com/#/posts/250'.
My $routeProvider catches the route and correctly loads the specific post data. From that point, when I click on the posts index ng-click link, it correctly loads and shows the posts index, but the route is still 'ROOT_PATH.com/#/posts/250'.
I'd like the app to always show the root path. Or at least always show the root path when posts index is displayed.
$routeProvider.when('/posts/:postId', { templateUrl: '../assets/mainIndex.html', controller: 'PostCtrl' } )
# Default
$routeProvider.otherwise({ templateUrl: '../assets/mainIndex.html', controller: 'IndexCtrl' } )

Backbone.js with Rails

I'm currently trying Backbone.js along with a rails app. My problem is, that I don't know how to implement the Backbone controllers and views with my rails app. I've read a lot of tutorials, but they are always using just one controller in backbone.js.
For example, I have two controllers in rails.
Activities Controller
Includes two views, a google map and a search field. The google map is inserted with a backbone view, the searchfield is in HTML and gets its functionality through a backbone view.
The search field should fetch data from my rails model and display markers inside the map.
And the other one is
Users Controller
Here the users profile is viewed, and I want to add some ajax functionality like updating values and other things
In my application.js I start the app using
var App = {
Views: {},
Controllers: {},
Collections: {},
init: function() {
new App.Controllers.Activities();
new App.Controllers.Users();
Backbone.history.start();
}
};
$(function() {
App.init();
});
The problem is, that I don't need the Activities controller in my User Profile and the Users controller in the Rails Activities controller. How could I solve this? Should I try reading the current URL within javascript and then decide which controller is used?
Or should I put the JavaScript file into the application.html.erb and then decide here which controller should be used?
Or is this the wrong way to use backbone.js controllers?
Am I getting sth wrong with the structure of backbone.js? Or am I using the Controllers in a wrong way?
Another question is, how to add little JavaScript, in particular jQuery, functionality through Backbone.js? For example, I want to remove the label inside a field, when the user clicks into the field. Or I want to do some tab-functionality and just toggle the visibility of some elements.
Should I create for each element that is using javascript a Backbone view? Or is this overload?
Hope I made myself clear and anybody can help,
thx!
Why not make use of the routes feature Backbone provides to decide which method to call? The activities controller would contain only routes use for activities, the user controller only for the user handling, and so forth.
Like this you can instantiate the controller just as you do and the routing will decide what happens based on the current location's hash.
If you can't use links with hashes (or there are no such links on your page), I'd simply name my view containers specific enough to attach events only for the current view when needed.
jQuery plugins etc. belong into views IMO. Same goes for your tabs and input hint toggle.
Update
On a general level (and I would not necessarily recommend doing it this way): If you have two methods:
// should be only called for the 'Foo' controller
function foo() {
alert("FOO");
};
// should be only called for the 'Bar' controller
function bar() {
alert("BAR");
};
and want to call only one of them depending on the current Rails controller, create a small helper:
e.g. in you *helpers/application_helper.rb*
def body_class
controller.controller_name
end
then call this method in your layout file (or header partial):
<body class="<%= body_class %>">
…
and use e.g. jQuery to "split" your JS execution:
if ($('body').hasClass('foo')) {
foo();
} else if ($('body').hasClass('bar')) {
bar();
}
I personally use jammit (from the same author of backbone). You can group stylesheets and javascripts files by module and use them on your different pages. So you create a module for your activities view and another for your user view each requiring the necessary JavaScript file. Two nice things about this:
you have only code used on the page loaded on the page
jammit comprsses everything for you
This is the way to go when not creating a single page web app where you relies on # routes to navigate from one controller to another. In your case you have multiple single page apps inside a main rails app.
Im a Backbone.js newb, so please someone correct me if Im wrong, but I think youre confusing what Backbone controllers are used for.
Backbone.js controllers basically consists of hashbang routes(similar to Rails routes) and actions that these routes call. For instance if you have a rails route that will render a view at http://mysite.com/backbone_test and you have a following backbone route:
var MyController = Backbone.Controller.extend({
routes: {
"foo/:bar": "myFirstFunction"
},
myFirstFunction: function(bar){
console.log(bar);
});
then if you go to http://mysite.com/backbone_test#foo/THIS-IS-AMAZING then MyController.MyFirstFunction gets executed and "THIS-IS-AMAZING" gets logged in JS console.
Unless you have a direct need for this sort of hashbang-related functionality, such as saving a state of your JavaScript application via an url (for example: http://my.app.com/#key=value&key=value&ad-infinitum=true ) Id advise against using Backbone controllers. You can get all of the functionality u described via Models Collections and Views.
Nice thing about Controllers and Backbone in general is that its modular and different parts can be used independently.
I guess the summary of this answer is that if you dont have a single page javascript application, do not use Backbone Controllers, rely on your rails controllers instead.

Resources