Accessing controller name outside of ng-view - angularjs

Is there a way to access the name of a controller when the controller is defined seperate from ng-view?
<div ng-controller="Ctrl1">
<!-- Some code -->
</div>
<div ng-view>
<!-- Configured with ngRoute -->
</div>
Within ng-view I'm able to use $route.current.controller to retrive the current controller name. If possible how would I achieve the same thing for "Ctrl1"?
Thanks a bunch!

Unlike the ng-view, it's possible that there are more than one ng-controller in the same page.
Therefore, it's impossible to have something like $route.current.controller to get the current controller.
But if your goal is to just print every controller name that get initialized via ng-controller, you could write a custom directive with the same name ngController.
app.directive('ngController', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
console.log(attrs.ngController);
}
};
});
This will output the value inside the ng-controller attribute whenever any ngController directive has been compiled.
Hope this helps.

Related

Regarding angular js directives usage

i am learning angular so when reading article then some time getting stuck to understand the output. here i confusion of render html output.
code taken from http://www.w3schools.com/angular/tryit.asp?filename=try_ng_directive
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" w3-test-directive></div>
<script>
var app = angular.module("myApp", []);
app.directive("w3TestDirective", function() {
return {
template : "I was made in a directive constructor!"
};
});
</script>
</body>
</html>
when the above code runs then output as follows
<div w3-test-directive="" ng-app="myApp" class="ng-scope">I was made in a directive constructor!</div>
my question is why the directive's template text gets added within start and end div tag ?`
why this attribute is blank w3-test-directive="" in div ?
this text I was made in a directive constructor! could have added in the attribute of w3-test-directive so the html output may look like
<div w3-test-directive="I was made in a directive constructor!" ng-app="myApp" class="ng-scope"></div>
please help me to understand why the directive's template text gets added within start and end div tag ?` thanks
How you use directives depends on the 'restrict' property.
If you add restrict: 'E', then you can use it as a element, ex:
<foo></foo>
If you do restrict: 'A', now its:
<div foo></div>
More info:
http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-i-the-fundamentals
Example:
angular.module('moduleName')
.directive('foo', function () {
return {
restrict: 'EA', //E = element, A = attribute, C = class, M = comment
template: 'Foo'
}
});
The template or the content from templateURL is always inserted between the enclosing tags on which the directive is used. Hence the template text added inside the div tags. This happen because untill it gets to html, it will never be displayed.
w3-test-directive="" : this is because of the fact that this as a attribute has no value. Since it not a known attribute in html, it has no default value, so it will be parsed like this.
I have added an example where i am using the same directive as an element tag instead of using it as a attribute
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp">
<w3-test-directive></w3-test-directive>
</div>
<script>
var app = angular.module("myApp", []);
app.directive("w3TestDirective", function() {
return {
template : "I was made in a directive constructor!"
};
});
</script>
</body>
</html>
A directive renders its template within the element upon which it is declared. The w3-test-directive directive is being declared (as an attribute) on your div element:
<div ng-app="myApp" w3-test-directive></div>
It will therefore render your template within that element's opening and closing tags, and won't (by default) affect anything higher in the DOM tree.
Also, about valueless attributes - they don't need values. For example, the disabled or async attributes do not normally have values. That a particular attribute is merely present is enough, in many cases. In Angular, the presence of a directive, declared as an attribute, is often all that is needed. If you provide values, they will be interpreted as references to model data (handled by the your scope declaration within the directive definition).
why this attribute is blank w3-test-directive="" in div ?
blank space is because it show that attributes as Html5 compliance no html5 errors or warnings. same as if we write disable or readonly property.
ng-directives have menu types to declare for example :
<div ng-app="myApp" w3-test-directive></div>
above example of 'A' means Attribute it can be write as follows:
following example with 'E' Element
<div ng-app="myApp">
<w3-test-directive></w3-test-directive>
</div>
following example with 'C' Class
<div ng-app="myApp">
<span class='w3-test-directive'></span>
</div>
for more info please refer following link:
[angular directives][1]
[1]: https://docs.angularjs.org/guide/directive

Asynchronously issue

Use case:
In my index.html. I have two ng-include directives and two custom directives.
Each directive has its src attribute defined the address to load some html snippet from another server (the custom directives have its template attribute loading the html)
I need to make sure that all directives (see the code 1,2,3) have fully loaded the html snippets before I call the fifth ng-include directive (4) to load the java-script from another server.
Right now all directives are running asynchronously and I can't be sure that the directive which loads the Javascript should always be the last one to load. I need this because if Javascript is loaded and executed before the html it wouldn't go through the html and linking it to javascript/jQuery.
I have solved this issue using a very classic way. I update a global variable whenever the html snipts are loaded and define the an ng-include directive to load the Javascript conditionally when the global variable is true indicating all other directives have loaded their html. But this is not the Angular way. I need the Angular way and btw I'm new starter on Angular.
Index.html
<div ng-controller="navigationController" >
**(1)**<agilesites-navbar></agilesites-navbar>
<div class="container-fluid">
<div class="row row-offcanvas row-offcanvas-right">
<div id="offcanvas-curtain" class="hidden-lg"></div>
<div id="contentContainer" class="col-sm-12 col-md-12 col-lg-12">
<div ui-view="content" id="content"></div>
</div>
**(2)**<agilesites-sidebar></agilesites-sidebar>
</div>
</div>
**(3)**<ng-include src="''+API_HOST+'api/cms/footer'" onload="cmsResourceLoadingStatus(true)"></ng-include>
**(4)**<ng-include src="''+API_HOST+'api/cms/javascript'" ng-if="cmsResourceLoadingCompleted"></ng-include>
</div>
Custom directive (agilesites-sidebar):
angular.module('newhorizonsApp')
.directive('agilesitesSidebar', ['ProductTypes','$compile', function (ProductTypes,$compile) {
return {
restrict: 'E',
scope: true,
template: '<ng-include src="\'\'+API_HOST+\'api/cms/sidebar\'" onload="executeOnLoad();"></ng-include>',
link: function (scope,elem){
scope.executeOnLoad = function() {
scope.cmsResourceLoadingStatus(true);
};
}
};
}]);

Can an angular directive contain a controller?

Or perhaps a better question is, should a directive contain a controller?
For reasons of separation, my index.html is a simple file. Everything is rendered into it via templates. So my index.html is real simple:
<body ng-app="myapp"><mainmenu></mainmenu><div ng-view></div></body>
I don't really need a directive for mainmenu, but it allows me to put the menu in a separate template file. The main menu itself contains user info, login/logout, and a search box.
<div class="leftmenu" ng-show="isLogin()">
<ul class="menu">
<li>Part1</li>
<li>Part2</li>
<li>Part3</li>
</ul>
<div ng-controller="Search" class="Search><input type="text" ui-select2="s2opts" style="width:250px;" ng-model="search" data-placeholder="search"></input></div>
</div>
<div class="rightmenu">
<ul ng-show="isLogin()" class="menu">
<li>My Account</li>
<li>Logout</li>
</ul>
<ul ng-show="!isLogin()" class="menu">
<li>Login</li>
<li>Register</li>
</ul>
</div>
So there is the menu part, with its own controller, and the search, with its own, embedded between the two parts.
Of course, my mainmenu directive unit tests fail because SearchController isn't defined. But this leaves me wondering if I am going about this wrong. Should I even have it like this, a section of html with an explicit ng-controller defined inside it? Doesn't this create all sorts of weird dependencies?
How should I better structure this? A search directive that is included so I can unit test it separately? Something feels wrong here structurally...
UPDATE:
As requested, a basic fiddle: http://jsfiddle.net/nj4n44zx/1/
As specified by the Angular documentation, the best practice is to define a controller inside a directive only to expose an API to another directive. Otherwise the link function is sufficient.
See at the bottom of :
Angular directives
By experience using controllers inside a directives shadow what you are doing in your scope. It does not help to have a easy readable code.
I do prefer using the main controller where the directive is included. With a non isolated scope you have access to everything from the link function.
I usually deal with it like that:
app.directive('topMenu', function() {
return {
restrict: 'E', // or whatever You need
templateUrl: '/partials/topmenu', //url to Your template file
controller: function($scope) {
$scope.foo = "bar";
}
};
});
Then, in that template You don't have to add ng-controller.
sure your directive can contain a controller because you declare a directive like this
myApp.directive('mainMenu', function() {
return {
restrict: 'E',
replace: true,
scope: true,
templateUrl: 'menu.html',
controller:['$scope', function($scope) {
//define your controller here
}]
};
});

ng-repeat inside compiled html for directive

I have two directives:
window.app.directive('placeholder', function ($compile, $route, $rootScope) {
return {
restrict: 'AC',
link: function (scope, element, attr) {
// Store the placeholder element for later use
$rootScope["placeholder_" + attr.placeholder] = element[0];
// Clear the placeholder when navigating
$rootScope.$on('$routeChangeSuccess', function (e, a, b) {
element.html('');
});
}
};
});
window.app.directive('section', function ($compile, $route, $rootScope) {
return {
restrict: 'AC',
link: function (scope, element, attr) {
// Locate the placeholder element
var targetElement = $rootScope["placeholder_" + attr.section];
// Compile the template and bind it to the current scope, and inject it into the placeholder
$(targetElement).html($compile(element.html())(scope));
element.html('');
}
};
});
I use them to basically swap out one section with html in another.
If I have the following html:
<div placeholder="footer"></div>
<div section="footer">
<ul ng-model="items">
<li ng-repeat="item in items"> {{item.Description}}</li>
</ul>
</div>
The ng-repeat doesn't seem to be working. If I simply output {{items}} below the , it displays fine. Also, I know binding is working because I can change items and it will update.
Lastly, if I move the ul outside the section it works fine.
So, my question is why does this not work (compile ng-repeat inside directive).
Am I missing something?
EDIT:
What is confusing me, is I can do this:
<div section="footer">
<!-- This Works -->
{{items}}
<!-- This also works -->
<input type="text" ng-model="items[0].Description" />
<!-- This doesn't work -->
<ul ng-model="items">
<li ng-repeat="item in items"> {{item.Description}}</li>
</ul>
</div>
This isn't going to work. It can't evaluate something from another scope without having an exact copy of it in its scope. If you want two directives to communicate use require and setup a way for them to do that if they aren't in a parent child relationship.
A couple of things you should think about. Essentially what you are doing is called transclusion. Section directive would use ng-transclude to capture the client's defined code. Use transclusion and maybe you can evaluate the template into html in the scope of section then using directive communication allow it to pass the HTML block (already evaluated) to the other directive. The only problem is making sure this happens when things change through binding. You're probably going to need some $watches on variables in section in order for placeholder to be notified when things change.
You will probably need a 3rd directive so allow section and placeholder to communicate through. In this example say I have a 3rd directive called broadcaster. Then section and placeholder will require broadcaster (ie require: '^broadcaster'), and it will define some interface for each of the directives to send HTML from section -> placeholder. Something like this:
<div broadcaster>
<div placeholder="footer"></div>
<div section="footer">
<ul>...transcluded content</ul>
</div>
</div>

Howto set template variables in Angular UI Bootstrap? (accordion)

I try to build a accordion with Angular UI Bootstrap (http://angular-ui.github.io/bootstrap/#/accordion). On How do I set model according to chosen accordion group. UI Bootstrap i found a working solution to use a template.
In my code i add the template with <script type="text/ng-template" id="template/accordion/accordion-group.html">
In this template a can use {{heading}} set by <accordion-group heading="{{group.title}}" content="{{group.content}}" ng-repeat="group in groups"></accordion-group>.
But how do i set other custom template variables?
I tried to set content="{{group.content}}" in the accordion tag too. Even if set, i don't know how to use it, tried {{content.group}} {{content}} and {% content %}.
Complete code on: http://plnkr.co/dSIVGg64vYSTAv5vFSof
See the edited plunker: http://plnkr.co/edit/8YCUemqILQy3knXqwomJ
You were trying to the nest a controller within the template of a directive. I might be mistaken, but that doesn't work or at least not in a very nice manner.
Instead of nesting controllers I would recommend converting CollapseDemoCtrl into a directive too. You can then pass the {{group.content}} or any other content to this directive.
EDIT: Example of how to pass data to directive scope
The HTML will be something like this:
<span ng-repeat="group in groups">
<div testDirective content="group.content" title="group.title"> </div>
</span>
The directive will look something like this:
angular.module('testModule', [])
.directive('testDirective', function(){
return {
restrict: 'A',
scope: {
content:'=content',
title: '=title'
},
template: '<h1>{{title}}<h1> <div>{{content}}</div>',
link: function(scope, element, attrs) {
}
}
});

Resources