How can I route requests to sub-directories using AngularJS? - angularjs

I'm coming from a PHP MVC background and I'm starting to use AngularJS. In PHP I would do something like this to direct the user to different parts of a website: grab the url, split it up, then send the user to the requested page. For example, if the user entered www.mysite.com/info/product/1, then PHP would split up this url and say "okay, use the 'info' controller." Then, the 'info' controller would say "okay, use the product controller." This controller would do what it does, and then send the user to the requested url.
In angular I'm trying to figure out the equivalent to this. I've seen things like this:
angular.module('phonecat', []).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/phones', {templateUrl: 'partials/phone-list.html', controller: PhoneListCtrl}).
when('/phones/:phoneId', {templateUrl: 'partials/phone-detail.html', controller: PhoneDetailCtrl}).
otherwise({redirectTo: '/phones'});
}]);
Does this mean that I have to enter in every possible url this way? In other words, do I have to duplicate the above code for any url that is a sub link of www.mysite.com/phones/. Like this:
....
when('/phones',...).
when('/phones/:phoneId',...).
...
and then do the same for some other directory (e.g., www.mysite.com/computers/...).
Any help would be greatly appreciated.

It looks like you're on the right path (pun intended).
In the route provider you define what view and controller will be used for a given URL ($location) essentially. Along with the path itself you can include parameters as you show with phoneId. Keep in mind these aren't paths on the server, these are simply URLs being used by the angular application to keep track of it's history and navigate for "deep linking" to show the appropriate view when a link is copied.
If you're doing some sort of RESTful interface where the PHP has some "directory structure" or URL that implies the data model that is a separate issue. You would handle that by using the $resource in AngularJS I believe though I'm not familiar with actually implementing this. So far I've used $http in a service I define to handle my calls to the server and am just manually setting up what files are called, but for large complex projects a REST interface is probably a good idea so as to avoid all the manual work and potential errors.

Related

Angular js use $windows from directive code

OK, just when I think I understand AngularJS I get zapped.
I have an application that uses a number of different google maps. I want the user to click on a marker and then have the system so to a new screen with information relating to what was clicked.
Everything is working well up to a point. I get the click event and then get ready to go to the appropriate screen. My code at that point looks like:
$window.location.href = "#/" + ScreenName + "/" + Parameter ;
At this point I get the error:
ReferenceError: Can't find variable: $window
Which searching tells me I need to inject $window
I have tried a bunch of different ways to do this injection, but this is also where my personnal knowledge base fails me.
I think I need to have my app.js file look like this:
.config([
'$routeProvider',
function($routeProvider)
{
$routeProvider.
when("/customer/:cust_gid", {templateUrl: "views/div_Cust.html", controller: "customerController"}).
when("/location/:locn_gid", {templateUrl: "views/div_Locn.html",controller: "locationController"}).
otherwise({redirectTo: '/utilities'});
}])
.config(function ($windowProvider) {
var $window = $windowProvider.$get();
console.log($window);
});
This does nothing for me. I need to know If I am close and just don't have the syntax right or is there something else missing.
Do I need an include file in my index.html file to load $windows?
Can someone give me a kindergarten level answer to this question.
Appreciate
Stan
$windows is a globally service which is included in angularjs.
You don't need an extra library reference for it.
To use it, just inject is as any other service in the controller which should use it
app.controller('locationController', function($scope, $window)..

AngularJS routing with ng-view relative to HTML page

I am quite new to Angular and now trying to make a simple routing with it. I have my landing page, currently called index2.html, containing some .js and .css includes and a div containing <ng-view></ng-view> where my content should go into.
My app.js looks like this:
var module1 = angular.module('module1', ['ngRoute']);
module1.config(function($routeProvider, $locationProvider){
$routeProvider.when("/",
{
templateUrl: "page1.html",
controller: "uiCtrl"
}
).when("/:param",
{
templateUrl: "page1.html",
controller: "uiCtrl"
}
).when("/transactions",
{
templateUrl: "page2.html",
controller: "uiCtrl"
});
});
But actually this does quite confusing things. Calling http://myurl.com/index2.html, the content of page1.html is properly loaded into the ng-view. So far, so good, but calling index2.html/123 gives my a Not Found instead of interpreting 123 as a parameter. I don't know why, but to make 123 a paremeter i have to call index2.html#123, which works, but then instantly updates the url to index2.html#/123.
Calling index2.html/transactions doesn't work at all. How can i call my /transactions route?
EDIT: If this is useful, i am using JQueryMobile as well in these pages.
I finally got what I want by getting the HTML5 mode work, which makes things so much easier.
After setting $locationProvider.html5Mode(true); I had to re-configure my webserver, what I wasn't able to manage until I found this nice guide: https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server-to-work-with-html5mode
Now I can access each of my routes at the conventional way using a slash. Parameters can be passed with ? and &, as usual and I don't need to grapple with confusing hashtags and self-changing url's anymore.

How to set in angularjs templateUrl to location.pathname in otherwise clause

I'm new to angularjs and want to integrate it in a cakephp app. For some pages I don't have a controller since no javascript is exectuted there or because I still have to create them. I however don't want to list them all in the routes. For this reason i set it like the following:
angular.module('desktop', []).
config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider
.when('/', {templateUrl: 'pages/index', controller: IndexController})
.when('/clubs', {templateUrl: 'partials/clubs.html',controller: ClubListController})
.otherwise({templateUrl: location.pathname});
}]);
This is however not working. When I go to /help, nothing happens. What am i doing wrong?
From my comments:
As far as I know it there is currently no way to do this with just routes. Routes are made to be static, they are defined as the app loads and do not update dynamically as time goes by. So using location.pathname() (or directly checking window.location) won't work since the route be set to whatever the value is when the app starts, and then never change again. It won't update when you load a new path unless you do a full browser reload (this is btw possible, but a hacky sollution).
But people have been working around it using includes, which might work for you depending on what you are after. See this question and the accepted answer for an example of how this works.
AngularJS - How to use $routeParams in generating the templateUrl?

Require factory method when called by controller

I'm using an angularJS and requireJS seed which you can download here: LINK
In that seed only the controllers that are called download the relevant controller which is then triggered. I've been trying to call a factory from my controller (with no luck) plus I would like the services/factories only to download the relevant factory if it has been called.
I've attempted to require a function within the factory method (much like the controller) but it is not working.
This is where I left off: Plunkr link
user971824.
I've put together something I call couchPotato that lazy-registers just about anything in angular using requirejs and the resolution features of $routeProvider (or any other router that does lazy promise-based resolution.
I've created a plunker based on yours demonstrating how you could do it with couchPotato. If you take a look at it, I think you'll see that it's a bit simpler because you don't actually create modules for all of the things you register lazily.
couchPotato grew out of some other example apps I found on the web. What I wanted was a tight way to do the lazy registration and a provider/service combo seemed ideal. I also wanted to maintain the ability for one component to depend on another, like in your example, you want the controller to depend on the factory... couchPotato lets you specify those dependencies in requirejs syntax.
So your controller, in my rendition, looks like this:
define(['app', 'myFactory'], function(app) {
app.couchPotato.registerController([
'mycontroller',
[
'myFactory',
function(myFactory) {
var message = myFactory.getCustomers();
alert(message);
}
]
]);
});
In this example, I made the controller, the factory and the value all lazy, but you could pick and choose and have some registered the "old fashioned way" at configuration time and others registered with couchPotato when they're needed for a given route.
http://plnkr.co/edit/Z3v1mszQiiq024po8Ocp?p=preview
A couple of things to note:
1) I put in a default route in order to trigger the lazy loading of your controller, your service (factory) and the version value.
2) I modified your service to append the version just to show how one component can depend on another (the service depends on the version value, the controller depends on the service).
So, within the require configuration, you don't actually specify any of this... it's all done lazily within your route.
$routeProvider.when('/',
$couchPotatoProvider.resolveDependenciesProperty({
templateUrl:'home.html',
controller: 'mycontroller',
dependencies: [
'mycontroller'
]
})
);
Since mycontroller depends on myFactory and myFactory depends on version, by the time your route is displayed they are all available and hooked up. I put some dummy text in the home.html partial just for kicks, but the controller is assigned by the $routeProvider so you don't actually need to specify it in the template.
couchPotato lives at https://github.com/afterglowtech/angular-couchPotato if you'd like to see a couple of other samples. I shim'ed it in dependent on angular because I designed it to be usable in cases where an entire application doesn't necessarily use requirejs... thus if you are loading angular with requirejs, you need to make couchPotato dependent on angular using the shim/deps technique.
LMK if you have any questions/comments... hope this helps!

How to run code on a route that has access to services

I wonder what's the best way to configure a route which only purpose is to make use of a service call and then redirect.
I'm currently using this hack:
$routeProvider.when('/talks', {
template: '<div></div>',
controller: ['dateService', '$location', function(dateService, $location){
var nextTalk = dateService.getNextTalkDate();
$location.path('talks/' + nextTalk.format('MM') + '/' + nextTalk.format('YYYY'));
}]
});
Currently I set the controller configuration to an inline controller implementation which has dependencies on the services and then does it's thing and redirects.
However, it feels a bit weird, since I have to set template to some dummy value because otherwise the controller would not be invoked at all.
It feels as if I'm stressing the controller property for something it wasn't intended for.
I guess there should be a way to run some view unrelated code that has access to services on a route. I could right my own $routeProvider I guess but that seems to be a bit heavy for something I would consider should be built in.
Looks like '/talks' is a kinda abstract since its just used to redirect to other routes.. So how about setting up a route like this:
''/talks/:month/:year'
Where :month and :year is optional. If no month or year is given, your service returns a default talk. Which is probably the next talk. If params are given you just fetch the requested data.
So there's no redirect required. Now you specifiy the controller and the needed view and expose your data on the scope. Optionally would it be better to wrap your service call in a promise and resolve it at routeProviders resolve property.
This makes sure that the view only change if everything's resolved fine.
Hope that helps!
There's no way to inject services into anywhere into a route. Whether it be redirectTo or template, any code in there is handled on the module level. This means that the module is loaded first and then when the application has boostrapped itself then the services and injection-level code is executed. So the controller is your best bet (since that does support injection).
Controller is used in a lot of areas in AngularJS and it should work fine for what you're trying to do. You can either handle the redirection like you do in there or you can setup three different routes that point to the same controller (where you build the page).
var handler = { controller : 'Ctrl' };
$routeProvider.when('/talks', handler).
when('/talks/:month, handler).
when('/talks/:month/:year', handler);
And if you do end up using redirection, then just use $location.path(url).replace(). This will make the history stack jump back one level and therefore that the redirection triggering URL won't be in your history.

Resources