Reading <script> value from html/view - javascript

Please see the code here http://plnkr.co/edit/FqfkcyZSqPkA7JjMMLrb?p=preview
I am embedding a javascript object/value in html, which needs to be read by angular. It reads value in index.html, but not in partial. (_global_link is read properly, but not _global_link_partial). Is it because _global_link_partial not available at $routeChangeSuccess, if so which event I need to listen to. I could provide the value as const in module definition, or directly in controller etc, but this value is very view specific and better to maintain there.
Thanks.
error:
_global_link Object {link: "abc"} controllers.js:3
ReferenceError: _global_link_partial is not defined
code:
function Test1Ctrl($scope) {
$scope.$on('$routeChangeSuccess', function ($event, current) {
console.log('_global_link', _global_link);
console.log('_global_link_partial', _global_link_partial);
});
}
function Test2Ctrl($scope) {
$scope.$on('$routeChangeSuccess', function ($event, current) {
console.log('_global_link', _global_link);
console.log('_global_link_partial', _global_link_partial); });
}
index.html
<div class="container">
<ul>
<li>test1
<li>test2
</ul>
<div ng-view></div>
</div>
<h2>in index.html</h2>
<script>
_global_link = {link: 'abc'}
</script>
partials (test1, test2)
<h2>In test1</h2>
<script>
_global_link_partial = {link: 'link1'}
</script>
<h2>In test2</h2>
<script>
_global_link_partial = {link: 'link2'}
</script>

I short, you need to include jQuery before angular.js
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.js"></script>
Take a look at #igor's answer at https://groups.google.com/forum/?fromgroups=#!topic/angular/H4haaMePJU0 :
long story short: it's because the script tag is special and angular doesn't treat it as such. if you include jquery on this page, the code should work.
When angular detects jquery, it will use it for dom manipulation and jquery is smart enough to treat the script tag as special.
Here is a plunker: http://plnkr.co/edit/UhKx8QWGTExLgdQMJuyi?p=preview
I changed your example plunker to latest version of angular.js (1.2.13) and also changed the routes a little.

Related

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 replace body class in Angular [ BEST WAY ]

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.

How to print to console.log from inside Angular.js inline-template's script tag?

I'm trying out the inline-template of Angular.js. I would like to have a way to debug Angular objects by printing to the console whenever an html page is rendered.
The inline-template puts html templates inside script tags. For example:
<script type="text/ng-template" id="/htmlpage.html">
<div class="page-header">
<h1>Title</h1>
</div>
<!-- everything else here is html too -->
</script>
It's tricky because the stuff inside the script tags is not really JavaScript anymore. So I don't know how to printing to the console inside the htmlpage.html with inline-template.
I have tried but failed with nesting a script tag:
<script type="text/ng-template" id="/htmlpage.html">
<!-- html page template Angular stuff before is okay -->
<script>console.log("this line DOESN'T SHOW UP anywhere");</script>
<!-- html page template Angular stuff AFTERWARDS ALL FAIL-->
</script>
I also tried just throwing in a bare console.log, since it's inside a script tag.
<script type="text/ng-template" id="/htmlpage.html">
<!-- rest of html page template is okay -->
console.log("this entire line gets output as text on the html page");
<!-- rest of html page template is okay -->
</script>
but the entire line, console.log("this entire line gets output as text on the html page");, gets printed out to the html page, not the console!
You can achieve this by calling some debugging function defined in the controller scope with ng-init in the template definition. See this example.
Let's say the template is defined by
<script type="text/ng-template" id="myTemplate.html">
<div ng-init="log('In template: '+$index)">{{greet}} Melissa<div>
</script>
and you have a controller defined as
var app = angular.module('myApp', [])
.controller('myController', ['$scope', '$log', function($scope, $log) {
$scope.greetings = ["Hello", "Bonjour", "Guten tag"];
$scope.log = function(message) {
$log.debug(message);
}
}]);
then
<ul ng-controller="myController">
<li ng-repeat="greet in greetings">
<div ng-include src="'myTemplate.html'"></div>
</li>
</ul>
should print in the console
In template: 0
In template: 1
In template: 2
The ng-init is called each time a template is instantiated. I just log some values available in the scope, like $index which is the index in the ng-repeat loop.
See this example.
Using the above answer, I found the following simpler.
The easiest solution for me was to temporarily set $scope.console = console in my controller, letting the template have access to the window.console global variable and its associated functions as normal, through the $scope binding
Because the templates are tightly scoped, they do not have access to global and window variables, as a result console.X() is not available from the template. And, like you probably experienced, attempting to reference undefined values from within the template did not result in an error, just... nothing. (Cue tearing hair out trying to figure out why events aren't firing)

Javascript inside Mustache.js template

I have a list of users coming from back-end and I append each one of them to my HTML page like below. My goal is to have javascript rating system for every user.
<head>
<link href="/css/rateyo.css"/>
<script src="/js/rateyo.js"/>
<script type="text/template" id="mustache-template">
{{#user}}
<li>
{{name}}
<div id="rating"></div>
</li>
{{/user}}
</script>
<script>
$("#rating").rateYo().on("rateyo.set", function (e, data) {
});
</script>
</head>
<body>
<ul>
<!-- All the individual users will be in their own li element here -->
</ul>
</body>
Everything is working except my rating script. It should make five stars next to each user. But I heard that you can't put scripts inside templates, is it correct? If I move that <div id="rating"/> to somewhere else it works like it's meant and shows the stars.
What should I do? I can't really put that script outside of my templates.
You can add your function to the object you are trying to parse with the template, and then call the function in your template.
var userlist = {
user: [
{name: 'name nameson', doRating: function() {someOtherFunction();}},
{name: 'some otherguy', doRating: function() {someOtherFunction();}}
]
};
var someOtherFunction = function() {
$("#rating").rateYo().on("rateyo.set", function (e, data) {
//Do stuff?
});
}
<script type="text/template" id="mustache-template">
{{#user}}<li>{{name}} <div id="rating"></div></li>{{/user}}{{doRating}};
</script>
Or you could of course calculate the rating on beforehand, and implement some sort of {{user.rating}} and use that to generate in your template.
Or as I look closer, you could just add the listener after mustache is done rendering. Since the div#rating would then be available (which should really be a class btw).
This would be my approach at least.

Angularjs on page load call function

I am learning AngularJS. I have some article tag and on clicking on a button each article page is showed without any page refresh. This is one page website. What I want is that when article id "showSelector" is loaded I want to call myFunction() and in this function I want to show an alert. But the alert is not showing.
How can I do that?
<article id="showSelector" class="panel" ng-controller="CinemaCtrl" onload="myFunction()">
<header>
<a ng-click="back()" class="icon fa-arrow-circle-left"></a><h2>Shows in {{getSelectedCinema()}}</h2>
</header>
<p>
These shows are played in our single room theatre. Select one to reserce a ticket for it.
</p>
<section>
<div class="row">
<div class="4u" ng-repeat="show in shows">
<div class="movieCard">
<a ng-click="selectShow(show)"></a>
<h3>{{show.nameOfShow}}</h3>
<h4>{{show.timeOfShow | date:'MMM d'}}</h4>
<h4>{{show.timeOfShow | date:'HH:mm'}}</h4>
<p>Free seats: {{show.reservations | freeSeatFilter}}</p>
</div>
</div>
</div>
</section>
<script>
function myFunction() {
alert("Page is loaded");
};
</script>
</article>
You should call this function from the controller.
angular.module('App', [])
.controller('CinemaCtrl', ['$scope', function($scope) {
myFunction();
}]);
Even with normal javascript/html your function won't run on page load as all your are doing is defining the function, you never call it. This is really nothing to do with angular, but since you're using angular the above would be the "angular way" to invoke the function.
Obviously better still declare the function in the controller too.
Edit: Actually I see your "onload" - that won't get called as angular injects the HTML into the DOM. The html is never "loaded" (or the page is only loaded once).
Instead of using onload, use Angular's ng-init.
<article id="showSelector" ng-controller="CinemaCtrl" ng-init="myFunction()">
Note: This requires that myFunction is a property of the CinemaCtrl scope.
<section ng-controller="testController as ctrl" class="test_cls" data-ng-init="fn_load()">
$scope.fn_load = function () {
console.log("page load")
};
It's not the angular way, remove the function from html body and use it in controller, or use
angular.element(document).ready
More details are available here: https://stackoverflow.com/a/18646795/4301583
you can also use the below code.
function activateController(){
console.log('HELLO WORLD');
}
$scope.$on('$viewContentLoaded', function ($evt, data) {
activateController();
});
you can use it directly with $scope instance
$scope.init=function()
{
console.log("entered");
data={};
/*do whatever you want such as initialising scope variable,
using $http instance etcc..*/
}
//simple call init function on controller
$scope.init();
var someVr= element[0].querySelector('#showSelector');
myfunction(){
alert("hi");
}
angular.element(someVr).ready(function () {
myfunction();
});
This will do the job.

Categories