How to replace body class in Angular [ BEST WAY ] - javascript

in my index.html I have my <header> then <body> with some subelements and <footer> elements.
Rest of the code is in template for each page like templates/home.html, templates/login.html
For one of this page I need to replace:
<body class="page-homepage" ng-app="historyApp" id="body">
to
<body class="page-subpage" ng-app="historyApp">
Is there some nice way how to do it?
I find some ways with jquery like (take it as example):
var el = document.querySelector('#body');
el.classList.remove('page-homepage');
el.classList.add('page-subpage');
but I believe that many of you already had to deal with this is kind of problem.
Can someone suggest me how to do this in angular?

In my opinion, the best way to do this is to add a variable to your $rootScope
$rootScope.isSub = false;
And use ngClass directive on your body element :
<body ng-class="{'page-subpage':isSub,'page-homepage':!isSub}" ng-app="historyApp">
And for each controller specify the $rootScope.isSub value.

Related

Best practices when dealing with angulars and tag attribute change (and DOM)

Premise: I'm new to AngularJS and trying to learn it. In the following example I'm monitoring text lenght and cursor position in a textarea. It does work, indeed.
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.pos = function() {
var id = document.getElementById("myArea");
return id.selectionStart;
}
$scope.len = function() {
return $scope.myTextarea.length;
}
$scope.myTextarea = "Ciao, sono una textarea";
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="myCtrl">
<textarea id="myArea" ng-click ng-keyup ng-model="myTextarea" ng-trim="false"></textarea>
<br/>
<br/>
<div>Length: {{ len() }}</div>
<div>Cursor position: {{ pos() }}</div>
</div>
</body>
My question is: is there a more "angular" way to do it (I mean without using JQuery, or getElementById or similar)? Is there a better way to access the "selectionStart" property of the "myArea" textarea, without having to search the DOM for an ID?
In general, is there a way in AngularJS to access the element bound to an ngModel (something like "$scope.myTextarea.boundElement")?
In a previous version of this code I used to define two event handlers to get the value of selectionStart. Then I noticed that ng-click and ng-keyup simply need to be there, even without being associated to an actual function, to make the code work.
In any case they're necessary to get the correct value of selectionStart. On the other hand they're not necessary to make "length" work. I would have expected that changes to "selectionStart" were monitored "automagically" like "length" and that something like a "return $scope.myTextarea.selectionStart" would do.
Is this possible, somehow?

How multiple ng-app worked in angular (Why such behaviour?)

I tried giving two ng-app in an application , when i gave two ng-app like
<body>
<div ng-app="A">
<div ng-controller="AC">
</div>
</div>
<div ng-app="B">
<div ng-controller="BC">
</div>
</div>
</body>
Then second ng-app does not work.
But when i change the scope of first ng-app="A" from div to body then both works fine like
<body ng-app="A">
<div>
<div ng-controller="AC">
</div>
</div>
<div ng-app="B">
<div ng-controller="BC">
</div>
</div>
</body>
Can anyone let me know why this behavior as i am quite new to angular.
I wanted to know why it worked as i didn't called angular.bootstrap on the second one.I tried searching but i didn't got how it is working when changing the scope of ng-app from div to body.
Find the fiddle for the same https://jsfiddle.net/maddyjolly2112/wyfd0djp/1/ and mind copying the js into the same .
Docs say: Don't use ngApp when instantiating multiple angular applications.
The reason you can't do this is laid out in the docs for the ngApp directive.
Only one AngularJS application can be auto-bootstrapped per HTML document. The first ngApp found in the document will be used to define the root element to auto-bootstrap as an application. To run multiple applications in an HTML document you must manually bootstrap them using angular.bootstrap instead. AngularJS applications cannot be nested within each other.
But Bootstraping multiple Angular apps is possible...
To bootstrap multiple Angular apps, you have to reference each, and they logically can't be nested, or sharing an element; they need to be separate from each other. Because of this, you cannot use the directive, ngApp:
<html>
<head></head>
<body>
<script src="angular.js"></script>
<div id="appElementA"></div>
<div id="appElementB"></div>
<script>
var app = angular.module('A', [])
.controller('AB', function($scope) {
$scope.greeting = 'Welcome!';
});
var appElementA = document.getElementById('divAppA');
angular.bootstrap(appElementA, ['A']);
var bApp = angular.module('B', [])
.controller('BB', function($scope) {
$scope.greeting = 'Welcome to B app!';
});
var appElementB = document.getElementById('divAppB');
angular.bootstrap(appElementB, ['B']);
</script>
</body>
</html>
The above code would be how you'd do it for your apps. You'd then have to be sure you're assigning the controllers to the right angular application (app vs bApp, in the above example.)
But don't nest them!
You claim it 'works' when you nest them, but you should be aware that it doesn't work, it just doesn't crash hard. Don't have multiple angular applications nested. You'll encounter weird issues, especially if you have multiple variables named the same bound to the $rootScope.
But you can nest them without ill effects, right?
If you're intent on having two Angular apps nested; it's possible but extremely version specific and liable to break in weird ways. This Stack Overflow answer talks about it.
From Angular's official docs :
Only one AngularJS application can be auto-bootstrapped per HTML document. The first ngApp found in the document will be used to define the root element to auto-bootstrap as an application. To run multiple applications in an HTML document you must manually bootstrap them using angular.bootstrap instead. AngularJS applications cannot be nested within each other.
Source : https://docs.angularjs.org/api/ng/directive/ngApp
If the question is : why the second example works and not the first, the answer is in the link above > Angular needs the first ngApp to be placed near the root element of the page (html or body).
As George mentioned, manual bootstrapping will work.
In html, use id instead of ng-app.
In script
var dvFirst = document.getElementById('dvFirst');
var dvSecond = document.getElementById('dvSecond');
angular.element(document).ready(function() {
angular.bootstrap(dvFirst, ['firstApp']);
angular.bootstrap(dvSecond, ['secondApp']);
});
Here is a working plunker
http://plnkr.co/edit/1SdZ4QpPfuHtdBjTKJIu?p=preview

How to find active tag formats using jQuery?

I have a situation with sample code as follows:
<html>
<head>
<title>Untitled Document</title>
</head>
<body>
<p>
<h1>The header</h1>
<div>
matter ia always matter matter ia <strong>bold matter</strong> matter matter <em>italics matter</em>matter ia <em><strong>bold italics matter</strong></em>lways matter
</div>
</p>
</body>
</html>
I am just trying to retrieve the specific tags like body->p->div->em->strong when I click on "bold italics matter" using jQuery. Is there any standard method to retrieve as per the click event?
If you wan to get the tag name of the element which is clicked, then you can use:
$('*').click(function(e) {
e.stopPropagation();
console.log($(this).prop('tagName'));
});
Fiddle Demo
I'm not completely sure about what you are trying to accomplish. If you are trying to retrieve the tag itself that the text is contained in, i would recommend that you put a <span> tag in around the the text in question and do an onclick="function()" or simply put the onclick right on the <strong> tag.
As far the the JQuery/Javascript goes, if you want to retrieve the content, it looks like
var foo = document.getElementById.innerHTMl("id");
However, this requires you to have an id in your tags which is probably the best, if not
'standard' method of retrieving the content that is within the tag.
After reading your comments, i am editing this post:
The best way to get the parent elements is to use the JQUery .parent() function. I'd imagine that you would just recursively state something like this:
var foo = $("nameofelement").parent();
I hope this is more of what your looking for.
Thanks for contributing everybody. At last I made it myself with the following code.
$(document.body).click(function(e){
var Tags=[], Target=e.target, stat_msg="";
Tags.push(Target.tagName);
while($(Target).parent().get(0).tagName!=="BODY")
{
Tags.push($(Target).parent().get(0).tagName);
Target=$(Target).parent();
}
Tags.push("BODY");
for(i=Tags.length;i>0;i--)
stat_msg=stat_msg+Tags[i-1]+" ";
alert(stat_msg);
});

Extract all classes from body element of AJAX-ed page and replace current page's body classes

I am in the process of AJAX-ing a WordPress theme with a persistent music player. Wordpress uses dynamic classes on the <body> tag. The basic structure is as follows:
<html>
<head>
</head>
<body class="unique-class-1 unique-class-2 unique-class-3">
<div id="site-container">
<nav class="nav-primary">
Other Page 01
Other Page 02
</nav>
<div class="site-inner">
<p>Site Content Here</p>
</div>
</div>
<div id="music-player"></div>
</body>
</html>
I am currently successfully loading the content of /other-page-01/, /other-page-02/, etc, using load('/other-page-01/ #site-container'). However, I need to extract all <body> classes from the AJAX loaded page and replace the current page's <body> classes with them dynamically.
Note: Replacing the entire <body> element is not an option due to the persistent <div id="music-player">. I've tried jQuery.get(), but couldn't get it to work.
How do I extract the <body> classes from the AJAX requested page and replace the current page's <body> classes with them?
I am not very familiar with jQuery or Javascript, so the exact code would be extremely helpful. Any help is greatly appreciated.
Thanks,
Aaron
My typical solution would have been to tell you to throw the AJAX code in to a jQuery object and then read it out like normal:
$(ajaxResult).attr('class');
Interestingly though, it appears you can't do this with a <body> element.
I'd say the easiest solution (if you have control over the resulting HTML) is to just use some good ol' regex:
var matches = ajaxResult.match(/<body.*class=["']([^"']*)["'].*>/),
classes = matches && matches[1];
I say "if you have control over the resulting HTML", because this relies on the HTML being reasonably well formed.
The other method would involve parsing it as a DOMDocument and then extracting what you need, but this would take a lot more and is usually overkill in simple cases like this.
Convert the body within your returned html to a div with a specific ID, then target that id to get the classes of the body (which is now a div.)
modifiedAjaxResult = ajaxResult.replace(/<body/i,'<div id="re_body"')
.replace(/<\/body/i,'</div');
$(modifiedAjaxResult).filter("#re_body").attr("class");
Of course, if the body has an id, this will conflict with it, so an arbitrary data attribute might be less likely to break.
modifiedAjaxResult = ajaxResult.replace(/<body/i,'<div data-re-id="re_body"')
.replace(/<\/body/i,'</div');
$(modifiedAjaxResult).filter("[data-re-id=re_body]").attr("class");
http://jsfiddle.net/N68St/
Of course, to use this method, you'll have to switch to using $.get instead.
$.get("/other-page-01/",function(ajaxResult){
var modifiedAjaxResult = ajaxResult.replace(/<body/i,'<div data-re-id="re_body"')
.replace(/<\/body/i,'</div');
alert($(modifiedAjaxResult).filter("[data-re-id=re_body]").attr("class"));
// the following line replicates what `.load` was doing.
$(someElement).append( $("<div>").html(ajaxResult).find("#site-container") );
});

Is it possible to do reusable snippets within AngularJS templates?

I'm finding myself repeating the same snippets of code again and again, is it possible to do something like this in AngularJS:
<div ng-snippet="mySnippet">
This is a snippet
</div>
<div ng-snippet="anotherSnippet">
Yet another snippet!!
</div>
<ng:include src="anotherSnippet">
<ng:include src="anotherSnippet">
<ng:include src="mySnippet">
and the output of the above would be:
Yet another snippet!!
Yet another snippet!!
This is a snippet
I'm not necessarily looking for this exact "ng:include" solution or pattern but something that would reduce the repetition in my templates.
<script type='text/ng-template' id="mySnippet">
This is a snippet
</script>
<script type='text/ng-template' id="anotherSnippet">
Yet another snippet!!
</script>
<ng-include src="'anotherSnippet'"></ng-include>
<ng-include src="'anotherSnippet'"></ng-include>
<ng-include src="'mySnippet'"></ng-include>
This should be what you want.
Docs for script and ng-include.
This sounds like you want to use directives. Here is a simple example: http://jsfiddle.net/gyF6V/1/

Categories