I am attempting to create a SPA using AngularJS as the main view for my website. I'm using ServiceStack on the server-side and can therefore cleanly serve HTML or JSON requests depending on what's accessing it. My main concern is the use of script blockers, preventing AngularJS from rendering the page properly. So far my main way of working is to render static pages, and inject a small script that redirects to the AngularJS-powered pages if it detects if Javascript is enabled. This works great since every URL works fine when the user begins at the static pages, but I've ran into a couple of snags.
Browsing to a link which includes the "?View=SPA" breaks the page if JavaScript is disabled
This causes the first page loaded to be loaded twice.
I'm looking for an alternative, but so far I haven't found any clean solutions. I was thinking about including the "?View=SPA" as a POST variable, but I'm still iffy on that implementation.
Any thoughts?
Instead of redirecting to an other page, I would implement both cases in the same HTML File as follows:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<style>.hideIfNoScript {display: none}</style>
</head>
<body ng-app ng-init="msg = 'hello world'">
<input class="hideIfNoScript" ng-model="msg" />
<p class="hideIfNoScript">{{msg}}</p>
<noscript>
<p>Content without javascript</p>
</noscript>
<script type="text/javascript">
var myEl = angular.element( document.querySelectorAll( '.hideIfNoScript' ) );
myEl.removeClass('hideIfNoScript');
</script>
</body>
</html>
The CSS Class hideIfNoScript in the head section makes sure that all HTML Tags with this class are never shown to the user, if javascript is disabled.
The noscript tag shows the alternative content.
If javascript is enabled, the little script at the end of the body section makes those elements visible. And in this case, the contents of the noscript tag are hidden.
Browsing to a link which includes the "?View=SPA" breaks the page if JavaScript is disabled
Hide those links by default:
a[href*='SPA'] { display: none; }
This causes the first page loaded to be loaded twice
Use a cookie on a browser-check page which loads the first page in an iframe or redirects to it to avoid this.
References
Track Non-JavaScript Visits In Google Analytics
Related
I am using angular with sightly. So I have angular html template surrounded by script tag, which also has sightly attributes like data-sly-resource.
Below example code will give you clear idea.
<script type="text/ng-template" id="example.html">
<section data-sly-resource="${ #path='textOverImage', resourceType='example/components/textOverImage'}" id="textOverImage" >
<div ng-include="'private/textOverImage.html'" data-sly-test="${!wcmmode.edit}"></div>
</section>
</script>
It works fine in non-edit mode , but in edit mode, I can not author data-sly-resource part. It looks like <script> tag is not letting it work roperly because when I remove <script> tag ,than I can author it.
And removing script tag is not an option as well.
So how can I stop script tag form breaking sightly functionality in edit mode?
I ended up doing repetition of code , one for author mode and other for non edit mode.
Below is close resemblance of my solution.
<section data-sly-resource="${ #path='textOverImage', resourceType='example/components/textOverImage'}" id="textOverImage" data-sly-test="${wcmmode.edit}" >
<div ng-include="'private/textOverImage.html'"></div>
</section>
<script type="text/ng-template" id="example.html" data-sly-test="${!wcmmode.edit}">
<section data-sly-resource="${ #path='textOverImage', resourceType='example/components/textOverImage'}" id="textOverImage" >
<div ng-include="'private/textOverImage.html'"></div>
</section>
</script>
As you can see in above code, what to show and when works via data-sly-test="${wcmmode.edit}".
I also tried to to create sightly template for redundant code and than try data-sly-use but now, it works in author mode but sightly can't put template inside <script> tag even though I used # context='unsafe'
There is a workaround based on the Sightly Reference
Put the markup inside a separate html file say mymarkup.html parallel to mycomponent.html
In Component HTML file (e.g mycomponent.html) use <script type="text/ng-template" data-sly-include="mymarkup.html"></script>
In mymarkup.html we can use Sightly tags normally and those would be evaluated/executed normally, we would not even need to specify the # context for variables we would read using use API. The final markup rendered by component mycomponent.html when dragged to page would render something like this below
<script type="text/ng-template">
//mymarkup.html evaluated content here
</script>
In your script tag you could add data-sly-unwrap="${wcmmode.edit}"
This will remove script tag in edit mode allowing you to edit included components but in any other mode the script tag gets rendered.
I found the following mention in Netcentric's AEM Sightly Style Guide:
Then, because the HTML grammar ignores elements located inside a
< script > or < style > elements, no block statement can be used within
them.
Although it's not explicitly stated in the Sightly spec, it makes sense. So your fix is right.
AngularJS rendering the content fine into title and meta tags, but when I share it with facebook or google, in the popup window it shows angular {{ }} there.
Code:
<div ng-controller="MyCtrl">
<title>{{mDetails.display1}} - Subtitle</title>
<meta name="description" content="{{mDetails.display1}} - {{mDetails.address1}} , {{mDetails.city}}, {{mDetails.state}}, {{mDetails.country}}">
.
.
Note: I am already using ng-cloak.
Thanks for help.
Two ways, as mentioned on another question :-
og meta tags, social buttons and angularjs
Method 1 :-
This can't be done using javascript. Some people think that Facebook is reading what's currently on the page. It's not. It makes a separate request to your server using the same url (from window.location.href) using it's Scraper, and the Facebook Scraper does not run javascript. That's why you get {{page_title}} when clicking on something like a Facebook share button. Your content will have to be generated by the server so when Facebook goes to hit the url it gets the content it needs up front without the need for javascript. You can tackle the server side rendering in a fews ways.
You can allow your server side technology to render the content.
You can use the PhantomJS approach https://github.com/steeve/angular-seo.
Method 2 :-
There's also a possibility that you can re-render Facebook widgets. Use their parse method:
FB.XFBML.parse();
after your angular stuff has completed. It's not working for my share button (yet!!), but I tested it on likes, and it's cool. Basically it re-scans the DOM and renders the Facebook widgets. You can also pass it a single element, something like this directive:
'use strict';
angular.module('ngApp')
.directive("fbLike", function($rootScope) {
return function (scope, iElement, iAttrs) {
if (FB && scope.$last) {
FB.XFBML.parse(iElement[0]);
}
};
});
This snippet would rescan the DOM for html5 facebook fb-like widgets when creating the last element in angular repeater.
Another accepted answer in the same context :-
https://stackoverflow.com/a/24086652/1366216
Edit:
I implemented json on server side just for the meta tags, however its an overhead because for on page data, there is still ajax call.
$mid=$_GET['id'];
$mJSON = file_get_contents($homeurl."/json/getdetail.php?mid=".$mid);
$mObject = json_decode($mJSON, true);
if ($mObject['ID'] != undefined && $mObject['ID'] != '') {
<meta property="og:title" content="<?php echo $mObject['display1'];?>"/>
<meta property="og:description" content="<?php echo .$mObject['display2']; ?>"/>
}
A possibility that worked for me is to have a "fallback" for FB description (so it would be a generic description that shows when angular hasn't loaded). I achieved this using two ng-if.
<meta ng-if="mDetails.display1" property="og:title" content="{ mDetails.display1 + "- Subtitle"}}">
<meta ng-if="!mDetails.display1" property="og:title" content="A generic title">
Whenever there are two meta descriptions with the same title, FB will take the last one. And after rendering, googlebot will only see the non-default one.
I have my angularjs app that has a topbar navigation, a left sidebar navigation and the rest is for the content (using data-ng-view I get different views).
My question is, if I have a view called login and a route that redirects me to the login page, how can I show that view actually in the full screen of the page?
Now if I navigate to http://applicationurl.com/#/login the view is like this:
I want it to look like this:
I cannot figure it out how can I achieve this in AngularJS?
The html is something like this:
<html>
<head>
</head>
<body>
<header>...</header> // top navigation bar
<div id="container">
<nav>...</nav> //left bar navigation
<div id="content">
<div id="wrap" data-ng-view="">
// here are loaded all the views ...
</div>
</div>
</div>
</body>
</html>
The correct solution would be to move your header/footer/sidebar into Angular Views and then use the Angular UI Router to build the pages with multiple named views where needed.
If you do not want to modify your code, it is not possible to achieve what you want to accomplish. By virtue of your login page being rendered by ng-view, it will be inserted within that tag on the page. You could hide the header and navbar with ng-hide set on some scope variable that would be set when the login page was rendered, and then the variable could be reset upon successful login/traversal to another page. This is a bit hacky, and is certainly not ideal, but is a quick and dirty fix. If you want to learn and do it correctly, Justin's suggestion is a great place to start.
I have a <div> in my HTML page. What I need to do is, add <ul> element inside it from my Entry point class. I have tried from onModuleLoad function using below code,
UListElement ul=Document.get().createULElement();
ImageElement img=Document.get().createImageElement();
img.setSrc("\\images\\personas");
LIElement li=Document.get().createLIElement();
li.appendChild(img);
ul.appendChild(li);
Document.get().getElementById("divPhotos").appendChild(ul);
but my <div> is empty/has no childs when i run it. what am I missing here?
Your code is okay assuming you have correctly assigned the "divPhotos" id to a div in your HTML DOM. I suspect that you are viewing the page source and expecting to see the newly appended DOM elements. What you want to view is the pages live DOM. How you inspect the DOM in depends on the web browser you are using. if your using Google Chrome, press F12 to access the debugging tools which will allow you to transverse the live DOM. For Firefox, you need to install Firebug. Other browsers provide their own debugging mechanisms.
I have forgot to add nocache.js in html page.
<script type="text/javascript" language="javascript" src="test/test.nocache.js"></script>
Its working now...
I've added the required tags, but it is still not showing. I've added this in head:
<script type="text/javascript" src="https://apis.google.com/js/plusone.js">
{lang: 'lt', parsetags:'explicit'}
</script>
and this in the body:
<g:plusone href="http://www.fanuspinta.lt/katalogas/lietuva-mar%C5%A1kin%C4%97liai-31"></g:plusone>
At first I thought it didn't show up because I was using a local server, but now it's in production, and still not showing.
URL in case: http://www.fanuspinta.lt/katalogas/lietuva-mar%C5%A1kin%C4%97liai-31
Thanks for any ideas.
I used a html5 tag like shown here without the data-href attribute, dynamically added it afterwards (when the lightbox was loaded) and explicitely loaded the div containing the plusone code at the end.