Why does AngularJS break self-referencing anchor links? - javascript

If you're at a location like this...
http://www.domain.com/index.html
... and you have a link that points to the same location...
My Link
... then clicking on the link does nothing. Normally you would be redirected to the page as normal; a handy way to refresh the page (without doing a full refresh).
I've traced the culprit of this odd behaviour to AngularJS.
Observe the following example:
<body>
Sample Link
<script>
var SampleApp = angular.module("SampleApp", []);
</script>
</body>
http://jsfiddle.net/7vqD9/
By clicking on the link the browser tries to go to the same location (because of a blank href). This is normal.
Now let's activate Angular:
<body ng-app="SampleApp">
Sample Link
<script>
var SampleApp = angular.module("SampleApp", []);
</script>
</body>
http://jsfiddle.net/7bEp3/
Clicking on the link does nothing.
Why does AngularJS break links in this way? Is there any obvious reason that I'm missing?

Why does Angular prevent classic behavior of href?
From Mastering web component with AngularJs:
AngularJS comes pre-bundled with the a directive, which prevents
default actions on links when the href attribute is omitted. This
allows us to create clickable elements using the a tag and the
ng-click directive. For example, we can invoke the atag as follows:
<a ng-click='showFAQ()'>Frequently Asked Questions</a>
Having the a tags without a default navigation action is handy, as
several CSS frameworks use the a tags to render different types of
visual elements, where a navigation action doesn't make much sense.
For example the Twitter's Bootstrap CSS framework uses the a tags to
render headers in tabs and accordion components.
Keyword to retain is: "handy"

Angular overrides the a tag: https://github.com/angular/angular.js/blob/master/src/ng/directive/a.js
The lines to note here are:
// if we have no href url, then don't navigate anywhere.
if (!element.attr('href')) {
event.preventDefault();
}
Angular does this because of ngHref, which sets the href only after angular and scope are fully loaded, thus preventing the user from accidentally going to /{{pageUrl}}/.
If you want to reload the page, you should look at the $location service provided by Angular.

Related

External link is not working in Next.js when you want to use Link component

I was very surprised that a simple Link component is not working in Next.js when you want to use an external URL and HTML Button tag inside it.
Below you can see how I tried to solve the problem:
Approach number 1:
<Link href="https://stackoverflow.com/">
<button>StackOverflow</button>
</Link>
Approach number 2 (link without protocol):
<Link href="//stackoverflow.com/">
<button>StackOverflow</button>
</Link>
Approach number 3 (link without protocol and with Link attribute prefetch set to false or even true):
<Link href="//stackoverflow.com/" prefetch={false}>
<button>StackOverflow</button>
</Link>
IMPORTANT NOTE
Of course, mentioned case it's working when the URL is internal, like that:
<Link href="/stackoverflow">
<button>StackOverflow</button>
</Link>
or when I will change HTML button tag into HTML A tag, like that:
<Link href="//stackoverflow.com/">
<a>StackOverflow</a>
</Link>
In my case, I want to use the HTML button tag or any other UI component inside the Next.js Link component.
1. Solution for UI components inside Next.js Link component.
I have study Next.js documentation in more details and I found a very useful attribute to make an external link for any internal UI components (Semantic UI, Material UI, Reactstrap, etc.) inside Link component.
Let's take as an example a simple Semantic UI button component.
To add an external link to the Next.js Link component we should use attribute passHref. This attribute is set to false by default. This attribute forces Link to send the href property to its child.
import { Button } from 'semantic-ui-react';
import Link from 'next/link';
const Example = () => (
<Link href="https://stackoverflow.com/" passHref={true}>
<Button>StackOverflow</Button>
</Link>
)
export default Example;
2. Solution for HTML elements (different that tag A)
Inside Next.js documentation you can find below sentences:
External URLs, and any links that don't require a route navigation
using /pages, don't need to be handled with Link; use the anchor tag
for such cases instead.
And I have to write that it is obvious, so in that case, if you need to use any other tag, for example, HTML button, you should use onClick event on it without Link component.
The above code will look like this:
const clickHandle = () => {
document.location.href = 'https://stackoverflow.com/';
}
const Example = () => (
<button onClick={clickHandle}>StackOverflow</button>
)
export default Example;
UPDATE:
Of course, I agree with devs who are writing that for external links we should not use the Link component. The best solution here is to use just pure HTML a tags or JS redirect solution on click event as it has been shown in point 2 (or any similar way). Worth to mention, that you can build your own component and based on the passed href attribute you can switch between Link component and HTML a tag, like that:
// custom simple smart Link component
import Link from 'next/link';
const SmartLink = (link, url) => {
const regEx = /^http/;
return regEx.test(url) ? <Link href={url}>{link}</Link> : <a href={url}>{link}</a>;
}
export default SmartLink;
// ways to call the component
import SmartLink from 'path/to/SmartLink'; // set correct path
// somewhere inside the render method
// the below will use HTML A tag
<SmartLink href="https://stackoverflow.com" link="external StackOverflow website" />
// the below will use Next.js Link component
<SmartLink href="/stackoverflow" link="internal StackOverflow page" />
The Link component is only for linking between pages within your Next app. Passing an external URL is not supported behaviour, and should give you an error that links to this page, which includes this section:
Why This Error Occurred
Next.js provides a router which can be utilized via a component
imported via next/link, a wrapper withRouter(Component), and now a
hook useRouter(). When using any of these, it is expected they are
only used for internal navigation, i.e. navigating between pages in
the same Next.js application.
Either you passed a non-internal href to a next/link component or you
called Router#push or Router#replace with one.
Invalid hrefs include external sites (https://google.com) and mailto:
links. In the past, usage of these invalid hrefs could have gone
unnoticed but since they can cause unexpected behavior. We now show a
warning in development for them.
If you render an <a> inside, the href gets passed on to that and works as expected using native browser behaviour, but other elements can't use that so you would have to handle that case yourself.
I'd suggest looking at what you're trying to achieve though -- what's wrong with using an <a> tag? It seems like the right tool for the job.
In my opinion, the accepted answer is wrong. passHref is used when the <a> tag is not a child of <Link>. For external URLs, simply use <a> without <Link>, see below.
const link = props.link.charAt(0) === '/' ? <Link as={stripUrlPlaceholder(props.link)} href="/">
<a>{image}</a>
</Link> : {image};
return <div className="banner">
{link}
</div>;
Simply stated, use the <a> tag instead of the next <Link>. The next <Link> is for internal navigation.
Example 1.
<a
href='https://www.facebook.com/queueunderstop/'
target={"_blank"}
rel={"noreferrer"}>
<Image
className='gb'
src='/images/icons/fb.png'
alt='facebook'
width={25}
height={25}
/>
</a>
This resolves the issue of the link opening a new link while simultaneously closing the main site. The main things to note are the attributes:
target={"_blank"}
rel={"noreferrer"}
I tried various combinations as well and landed on this after reading the documents carefully even though the documents do not say it implicitly. The documents cover more of does and not all the hypotheticals.
I had the same issue, somehow I tried the above answers, it wasn't really helpful. What I found is that if you add https or HTTP:// in, it will surely automatically allow you to open external web. Here for a sample:
<a href={`https://${Your link}`}> Open external Link </a>
There is no need to use the next/link for external links, as it's only for client-side transitions between routes. Link is not intended for linking outside your app.

<a href> Anchor remains in URL and in page after refreshing

I have this little issue here with my page, where if I reload it while being anchored, the anchor remains and there is a problem to it. I.E
http://localhost/public/product/1#mod1
The anchor is #mod1, and while the anchor remains active after refresh, my CSS code is saying that this element:
.overlay:target
is active. Which is a very big issue, because then it doesn't allow me to explore the functionallity I have implemented on this anchor, unless I remove the #mod1 from the end of the page manually by hand. Because this CSS element makes this div visible when it should be not unless activated with the a href element.
(?)
<div id="mod{{$key}}" class="overlay">
content
</div>
Any ideas on how could I solve it? I tried catching whether the user has refreshed the page and redirecting him to an action/route/url, but the page stays blank then and URL unchanged.
You cannot use href with angularJS because it will misdirect the target link. AngularJS is a markup language for HTML, it is not HTML. Because angularJS is not HTML, we're provided a special set of directives to write angularJS values inline into HTML markup. The answer to solve your issue would be to replace the href tag in the anchor element with the angularJS directive ngHref. You can find more information about how to use ngHref and other directives at the link below. Good luck.
https://docs.angularjs.org/api/ng/directive/ngHref
Well I wanted to purely solve this without JS, but here's what I did, HTML:
<a ng-href="mod{{$key}}" class="button">(?)</a>
<div id="mod{{$key}}" class="overlay">
Then replaced the CSS of overlay:target to -> overlay:active, and implemented JS:
var curmod;
$('a.button').on('click', function(e)
{
curmod = document.getElementById($(this).attr('ng-href'));
$(curmod).addClass('active');
});
$('.popup a.close').on('click', function(e)
{
$(curmod).removeClass('active');
curmod = null;
});

Can I change the URL without updating the page?

This may seem like a dumb question. But I have no idea if it can be done. So before I start the process of making a portfolio site, I would like some pointers. Otherwise, I will just go with another design.
My question:
When using the ascencor.js plugin, everything on my site is in one file. I will therefor never go to a new url, like /contact or /about.
But then I wondered, what about google?
All of my content would be put inside different different classes, but in the same file:
<div class="floor floor-1">
<span class="text">Floor 1</span>
</div>
<div class="floor floor-2">
<span class="text">Floor 2</span>
</div>
Check the example here: http://rplambech.dk/ascencor/
So yeah, with this method, I will never change the url, so I can therefor only index one page.
Is there a way that I can change the URL without updating the site? And will I be able to go to http://rplambech.dk/ascencor/floor5 for example?
In case it's not possible, can I then at least overwrite the title of the page, each time I click to a new "page". With some PhP for example.
Or should I just go with a completely different approach? :)
From the context of your question, and its comments, you're looking for the term single-page-application.
There are many ways of doing this, some of them make use of the history object in order to support the browser's "back" and "forward" buttons.
I'd recommend you to do a search of the term "single page application" and in the meantime examine some (or all) of the following frameworks (they will ease your development and make your life easier instead of dealing with # and nasty low-level ajax calls:
backbone.js
angularJS
ember.js
It can be done using hash links, originally designed to jump to a certain div on the page are now often used to load dynamic pages on a single page
so you could link to http://rplambech.dk/ascencor/index.html#floor5 for example
and then have some javascript like
var loc = location.hash.split("#")[1];
then
if(loc == 'floor5'){
//execute goto floor 5 code
}
Using history.pushState with HTML5 as stated here.
<script type="text/javascript">
var stateObj = { foo: "bar" };
function change_my_url()
{
history.pushState(stateObj, "page 2", "bar.html");
}
var link = document.getElementById('click');
link.addEventListener('click', change_my_url, false);
</script>
URL:
<a href="#" id='click'>Click to change url to bar.html</a>
No you can't change the URL without going to the server.
JQuery does however have a cool page loader that will load a page.
How to put a whole html page in a div using jquery?
You cannot change the url "/ascencor" to "/ascensor/floor5" without refreshing the page.
But you can change to "/ascensor/#floor5" (added hash sign). I suggest you try out angularjs for more information.

Social sharing with ember.js

I have single page app made with ember.js and I have some problems with implementing social sharing.
I need to implement into hbs template something like this:
Link
However when this is rendered into browser there are additional script tags concatenated within the href string, eventually the link works and I am redirected but instead title I get something like this:
<script id='metamorph-190-start' type='text/x-placeholder'></script>
MyTitle
<script id='metamorph-19...
Where I need just MyTitle.
More specifically I use the following hbs template for facebook sharing, the model is initialized into the router:
<a href="https://www.facebook.com/login.php?next=http%3A%2F%2Fwww.facebook.com%2Fsharer%2Fsharer.php%3Fs%3D100%26p%255Btitle%255D%3D{{model.title}}%26p%255Burl%255D%3D{{model.url}}%26p%255Bsummary%255D%3D{{model.summary}}%26p%255Bimages%255D%255B0%255D%3D%2540Model.EventImage%2527%253EShare&display=popup"
target="_blank">
<img src="http://www.simplesharebuttons.com/images/somacro/facebook_img.png" alt="Facebook" />
</a>
I also tried third party libraries, like janrain, ShareThis or AddThis, but with them I had initialization problems, buttons were not shown at all when placed into template, and also had problems with messages customization from the model.
Thanks,
Igor
Approach 1 - Using unbound
To get rid of the metamorph tags surrounding your model value, try using the unbound option which does exactly that:
Link
Approach 2 - Computing the URL
In the case you need the model property to be bound and reevaluating when the model changes, then a different approach might be better like for example generating the URL in the controller backing up your template.
Assuming your controller is e.g. ApplicationController and the links live in the correspondent application template then you could do the following:
App.ApplicationController = Ember.ObjectController.extend({
url: function() {
var url = this.get('model.url');
var title = this.get('model.title');
// grab here as many properties you need from your model
var href = 'http://someaddres.com?u=%#&title=%#'.fmt(url, title);
return href;
}.property('model')
});
And then use the computed url property like this in your template:
<a {{bind-attr href=url}} target="_blank">Link</a>
Hope it helps.
This actually doesn't work that well since this will open a new browser tab\window instead of the desired popup window you get when using the suggested js code form facebook # https://developers.facebook.com/docs/plugins/share-button/
Unfortunately, you also need to create an 'action' within a Ember.View (for example) that calls window.open(url,"blah","width=300,height=500")

angularjs routing conflict with jquery functions

I'm having a problem with angular routing mechanism:
$routeProvider
.when("/", { templateUrl: "/*some MVC calls to return a partial view*/"})
.when("/somewhere/:someParams", { templateUrl: "/*some MVC calls to return a partial view*/"})
...
.otherwise({redirectTo: "/"})
The problem is I'm using a wrapbootstrap's theme (more accurately the Ace) and it has lots of functionality based on <a> tags and href="#" attributes and whenever the user clicks an element (like the dropdown menus which also have a <a href="#"> tag), angularjs jumps in and tries to parse the url which looks like this most of the time: http://localhost/site/somewhere/someParams#
any suggestions on how to separate these two functionality?
thanks in advance
You can take a look at the answer here. The approach is to have a directive that will prevent the default action (in your case, navigating to "/#").
You can modify the directive to navigate only when the location is not "/#". Using the attrs parameter in the function (see the accepted answer for the above question) identify the value of the href attribute. Based on its value you can then chose to proceed normally or to prevent the browser from navigating to the URL.
Actually I found my answer today while reading AngularJS's docs. the <a> tag is already a directive in angular and it's written so that when given an empty href (<a href="">) it prevents the default behavior of the a tag

Categories

Resources