Starting with single page apps - angularjs

I am a beginner in Angularjs and I have downloaded the template "Angular Start" for angularjs.
It has come with some inbuilt files . Like homecontroller, AppjsFolder etc.
I wonder how I can start with the angularproject if I am with an empty Mvc Project? Moreover When I run the application, I am defaulted with this url in the browser: "http:/somelocalAddress/#/home" . Can anybody tell me how # is attached with the url as I can see the route.config file it does not have any.
I know there are some anchor tags in Layout.cshtml
<li data-ng-class="{active : activeViewPath==='/home'}"><a href='#/home'>Home</a></li>
<li data-ng-class="{active : activeViewPath==='/demo'}">Demo</li>
<li data-ng-class="{active : activeViewPath==='/angular'}"><a href='#/angular'>Learn Angular</a></li>
<li data-ng-class="{active : activeViewPath==='/about'}"><a href='#/about'>About</a></li>
<li data-ng-class="{active : activeViewPath==='/contact'}"><a href='#/contact'>Contact</a></li>
Here , the href of anchortags are attached with # and it makes a point that url gets # on clicking the anchor tag but when I run the application why there is /#/Home
Please guide me.

You are using Angular ngRoute module. Which is a full-ajax navigation controller. That means when you navigate through routes you are not actually going any where, ngRoute downloads partials htmls and renders them inside the div tagged with ng-view attribute creating a "fake" (but high-performance) navigation experience.
Angular uses # in order to avoid browsers load whole page every route change action. That's why the anchor in your code is using # as well.
Your code seems to be an extract of a menu, probably a bootstrap menu, and it wants to add 'active' class to the corresponding li section depending on the current path. The controller is probably using $location service in order to find out which is the current path using $location.path() method. When you call path() it returns the current path without '#' thus it isn't using it either in ng-class attribute.
By the way, you might activate $locationProvider's html5mode and do not use prefix # and simply your life ;). It uses the same technology, the problem is your audience will need to use a modern browser, but I'm pretty sure you use other dependencies that require so too, do you?
How to configure html5mode
angular.module('myApp',[
...
'ngRoute'
...
])
.config(function($routeProvider, $locationProvider){
$routeProvider
.when('/path/to/my/route')
.otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(true);
});

Related

How to get Prerender.io to recognize a unique page when it does not have a route explicitly set?

I plan on using Prerender.io to turn my SPA (built with AngularJS) into a collection of cached pages. The app/website is multilanguage (English and French) and I'm using Angular Dynamic Locale to handle the language switching. Unlike a menu item, a language link in the switcher doesn't contain an href with route path but is bound to a controller that basically updates a variable and reloads the route.
My question is this: How to get Prerender.io to recognize a unique page when it does not have a route explicitly set? Of course my aim is to have the French version of each page cached separately.
Currently I am setting a query parameter in the controller attached to the language switcher (see snippets below). This is triggered by a function on ng-click. Will Prerender.io pick that up as a separate page?
(It is my understanding that Prerender.io does treat a route/path with a query parameter as a separate page unless told to explicitly ignore it; my question is more about whether setting the parameter in the controller rather than in the anchor tag might change things.)
Here's the template mark-up for the language switcher:
<nav class="lang-switcher" ng-controller="LanguageCtrl">
<a href ng-click="changeLanguage('en')" ng-class="{current: language == 'en'}">English</a>
<a href ng-click="changeLanguage('fr_FR')" ng-class="{current: language == 'fr_FR'}">Français</a>
</nav>
The controller (I've removed the code that's not relevant to the question):
.controller('LanguageCtrl', ['$scope', '$route', '$location', function ($scope, $route, $location) {
$scope.changeLanguage = function(lang) {
// Do some stuff.
// If the page is in French, attach query parameter to the URL.
$location.path($location.path()).search({'lang': ( lang == 'fr_FR' ? 'fr' : null )});
$route.reload();
};
}])
Of course then the problem becomes what happens to the switcher links once the page is cached as static. But one hurdle at at time... :)
Thanks in advance!
Crawlers don't click links so you'll want to populate the href tag there. That way they crawlers will parse the link and then make another request to the different link.
<nav class="lang-switcher" ng-controller="LanguageCtrl">
English
Français
</nav>
You are correct that Prerender.io will cache a URL separately with a different query parameter if you don't tell Prerender.io to ignore it.

Angular JS Scrolling to an element in another page

I am building a SPA using Angular 1.4 and Grails. In my home page I have 3 sections namely section1, section2 and section3 with the same ID. I have a dropdown menu on the top and I have used the scroll-to plugin to scroll to the respective sections on click.
<li ><a href scroll-to="section1">History</a></li>
My Problem is that when I move to another route, the dropdown menu will still be at the top and I am not sure how to get this to scroll to that element in the home route from another route. This is what I have tried.
<li >Economic Weight</li>
And in the route config I did this
when('/home/:section', {
templateUrl: '/euribron/js/app/templates/home.html',
controller: 'HomeController'
}).
and in controller I did this
if($routeParams.section) {
var element = document.getElementById($routeParams.section);
smoothScroll(element);
}
But this does not work when I am on the same route and it does not seem elegant. Is there an easier way to achieve this in an efficient way.
Instead of scroll-to plugin, you can use $anchorScroll service to manage autoscrolling. Please see this post's answer How to handle anchor hash linking in AngularJS. Basically, you need to set $location.hash() to the section's id that you can get from $routeParams and call $anchorScroll() function to handle the rest. Use different id for the sections.

angularjs v1.3.15 deep linking / routing / path no longer working with hashtag (Migrated from v1.2.3)

I am very new to angular and working on some code that I wanted to upgrade the angular version from 1.2.3 to 1.3.15 (which I have also never done before). When I did, the navigation links on my index page stopped working; rather it seems like the router.js falls through to my $routeProvider.otherwise case and redirects to /home (you can see the refresh in my jumbotron). However, I also notice that when I click on the links, my url goes from localhost:8888/home to localhost:8888/home# or localhost:8888/home#home or localhost:8888/home#info, but the page routes don't work (the home page gets refreshed instead) - like the route is just appended to /home and it doesn't know what to do.
When I remove the deep linking ( hash / pound / # ) from the links in index.html, the code appears to work again, but I don't really understand why. It's highly likely I'm misunderstanding something in the breaking changes from 1.2 to 1.3, but the documentation made it seem like using # is still supported in 1.3.
By "upgrading" I replaced my angular.js, angular-resource.js, and angular-route.js in my project with their newer versions. I am using Apache karaf, and tested in both Chrome and Firefox with the same results.
Shortened code below:
Here is the index.html code that works in 1.2.3 and breaks in 1.3.15 (I used both # and #home to test if there would be a difference):
<div class="navbar navbar-inverse navbar-fixed-top">
...
My Project<small></small>
...
<div class="collapse navbar-collapse navHeaderCollapse" data-ng-controller="NavController">
<ul class="nav navbar-nav navbar-right">
<li class="{{navData['Home'].css}}">Home</li>
<li class="{{navData['Info'].css}}">Info</li>
</ul>
</div>
</div>
</div>
My router.js looks like:
angular.module("app").config(function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider.when('/home', {
templateUrl: "home.html",
controller: "HomeController",
isPublic: false
});
$routeProvider.when('/info', {
templateUrl: "info.html",
controller: "InfoController",
isPublic: false
});
$routeProvider.otherwise({ redirectTo: '/home' });
});
With 1.3.15, when I replace the hash / pound / # signs in index.html, with "/" for Home and "info" instead of the "#info", everything seems to work again.
I also tried upgrading potential dependencies to the following:
ui-bootstrap-tpls-0.13.0.js (was 0.6.0)
jquery-2.1.4.js (was 2.0.3)
I am using:
bootstrap.js (using 3.0.0)
I also have some additional libraries but didn't know if they are relevant to this. Let me know if I should add them to the list.
I attempted to simulate what I am seeing in plunker here: Angularjs v1.3.15 test # redirect
But the links appear to be working with the exception of when you click a link with just "#" as the href, the page template isn't loaded at all. So I don't know if the issue is related or completely different.
A few suggestions I've looked into (but maybe missed something):
I did see that people are upgrading ui-router, but I'm not currently using it; should I be? (Though plunker seems to be working with angular-route.js)
html5 is not playing nice in all arenas, but it appears configured in my code?
fixing the server to allow #, which I think is already done since the code used to work correctly
Thanks in advance for the help! If you could point me in the right direction that would also be appreciated!
The hash has a specific meaning in the HTML specification. It targets an element on a page. If you are on the page localhost:8888/home and click on the link localhost:8888/home#info the browser would jump to the element with the id info, if there was one. Consequently nothing happens if the element doesn't exist.
Angular intercepts clicks and url changes and allow you to change routes instead. To avoid any confusion (or unintentional behavior) Angular has two conventions. The first is using a prefix. It's optional and the one suggested is !. That's why this mode is also called the Hashbang mode, links to routes would start with #!.
The second, and more important one, is that routes start with /. So it's href="#/" and href="#/info". And this works no matter what version of Angular you are using. Of course, this fixes your plunker.
If you want to use hashes then don't activate the HTML5 mode.
One remark about your last point: The server doesn't care about #, since it only concerns the client.

Linking to external URL with different domain from within an angularJS partial

All I am trying to do is include an anchor tag inside the html of a partial that links to an external site. Were this standard html, the code would simply be:
google
As simple as this is, I cannot seem to find a working solution for getting past angular intercepting the route (or perhaps replacing my anchor with the https://docs.angularjs.org/api/ng/directive/a directive unintentionally?).
I have scoured SO and the rest of the web and seen a myriad of solutions for dealing with: links within the same domain, routing within the SPA, routing within a page (ala $anchorScroll) but none of these are my issue exactly.
I suspect it may having something to do with using $sce but I am an Angular n00b and not really sure how to properly use that service. I tried the following in my view controller:
$scope.trustUrl = function(url) {
return $sce.trustAsResourceUrl(url);
}
with the corresponding:
<a ng-href="{{ trustUrl(item) }}">Click me!</a>
(as described here: Binding external URL in angularjs template)
but that did not seem to do the trick (I ended up with just href="{{" in the rendered page).
Using a plain vanilla anchor link like this:
google
also failed to do the trick (even though some online advised that standard href would cause a complete page reload in angular: AngularJS - How can I do a redirect with a full page load?).
I also tried adding the target=_self" attribute but that seemed to have no effect either.
Do I need to write a custom directive as described here?
Conditionally add target="_blank" to links with Angular JS
This all seems way too complicated for such a simple action and I feel like I am missing something obvious in my n00bishness, at least I hope so because this process is feeling very onerous just to link to another url.
Thanks in advance for any solutions, advice, refs or direction.
It turns out that I did in fact have all anchor links in the page bound to an event listener and being overridden. Since that code was fundamental to the way the page worked I did not want to mess with it. Instead I bypassed it by using ng-click to call the new url as follows:
HTML:
<a class="navLinkHcp" href="{{hcpurl}}" title="Habitat Conservation Plan" target="_blank" ng-click="linkModelFunc(hcpurl)">Habitat Conservation Plan</a>
Controller:
$scope.hcpurl = 'http://eahcp.org/index.php/about_eahcp/covered_species';
$scope.linkModelFunc = function (url){
console.log('link model function');
$window.open(url);
}
And voila! Good to go.
Thanks again to KevinB for cluing me in that this was probably the issue.

AngularJS $routeProvider, fallback to default link navigation

So not 100% of my site is "powered by AngularJS" some of it is just simple static HTML like a landing page or content oriented stuff, which is simple HTML for obvious reasons.
The only way I can seem to get a link to navigate normally is like this:
$routeProvider
.when('/plans', {templateUrl: '<%= asset_path('ng/views/start.html') %>'})
# Catch all
.otherwise({ redirectTo: (p,loc) -> window.location = loc })
It feels like the catch all should be simpler like I could do .otherwise(false) and it would just navigate normally. Same goes for `.when('/something'/, false) but I don't see anything in the docs that suggests this is possible.
Does anyone know of a better way to do this?
Edit 1:
One solution I've found is to use target='_self' in the link.
The other is apparently to set the "base url" of the application as outlined in the docs. Then any links outside of that base should navigate normally. However that doesn't seem to work as outlined and the example doesn't match what the documentation is suggesting either.
just creating a link to it external file
if you are using hashbang urls (e.g. #/plans) then you should be all set, if you are using html5 history api ($locationProvider.html5(true)) then you need to namespace your app (set base[href] properly) and put the content outside of that namespace.
relevant code:
https://github.com/angular/angular.js/blob/4df45b20d460239a0f5001fb0dd59f95e2d0e80d/src/ng/location.js#L560
Another solution is to use target="_self" on that a element. Again this should be an issue only when html5 (history pushState) is being used.

Resources