Angular access controller scope from nested directive - angularjs

this fiddle represents what i am trying to do:
http://jsfiddle.net/d1001001/dwqw6/.
The grid directive needs to grab some data from controller, but since it's nested in the modal directive, which has isolated scope, it doesn't have access to controller's scope. If i
put it like this
<div ng-controller="MyCtrl">
<grid data="data" cols="cols"></grid>
</div>
it works.
Is there a solution to this? I don't feel like passing the data and cols variables to the modal directive as well. Thanks

Use
<div ng-transclude></div>
instead of
<ng-transclude></ng-transclude>
in the template of modal directive.

Related

AngularJS controller communication with nested isolate scope directives

In my AngularJS application, I have to create an accordion with each section containing a slider control. I want both the accordion and the slider to expose APIs to the outer controller, for example:
accordion.collapseAll()
accordion.openNextPane()
or
slider.resetValue()
And I would like to be able to do in the controller something similar to:
$scope.clickNext = function() {
$scope.accordion.openNextPane();
};
$scope.clickResetAll = function() {
$scope.items.forEach(function(item, ind) {
$scope.slider[ind].resetValue();
// or:
$scope.accordion.pane[ind].slider.resetValue();
});
}
I'm thinking of creating isolate scope directives for the accordion and the slider, with a two-way binding to allow them to expose their methods to a parent controller. The page template would then look like this:
<div ng-controller="myController">
<accordion data="accordion">
<div ng-repeat="c in collection">
<accordion-pane>
<slider data="slider"></slider>
</accordion-pane>
</div>
</accordion>
</div>
But at this point I'm lost in a maze of isolate scopes and limited visibility on children, and it seems that there is no way to access the inner elements from the outer controller. Many solutions I've googled around seem to consider normal to have the directive talking to the controller instead of the other way round, but this seems to break encapsulation.
Any idea on how to do this? What is the correct AngularJS pattern for this problem?

ng-show not working inside ng-if section

ng-show is not working in following code
<button ng-click="itemIndex=0;showHome=true;" class="btn btn-link">Home</button>
<div ng-if="itemIndex==0">
<div ng-show="showHome">{{showHome}}
<h3>Home Section</h3>
<img ng-click="showHomeItems=showHomeItems?false:true;showHome=false;" ng-src="images/home.png"/>
</div>
<div ng-show="showHomeItems">
Home Items{{showHome}}
</div>
</div>
Without ng-if it is working fine but with ng-if its not working.
Read this article.
There are some directives in Angular that create a child scope, like ng-repeat, ng-if.
Inside those scopes, the booleans (such as showHome) are searched only within that new scope, and not in the parent scope.
In order to avoid such bugs, it's considered best practice to place the logic in the controller (or service, just not in the HTML) inside an object, which is not a primitive variable and will be looked up in the scope prototypical chain.
Try declaring a variable like this:
$scope.switch = {showHome : true}
and then use it in the html like this:
<div ng-show = "switch.showHome" >

AngularJS : isolated scope + two-way binding + ng-repeat not working

the ng-repeat outputs nothing. in the link function, i can see $scope.clients is an array. if i remove the isolate scope, and use the parent scope, the ng-repeat works.
html with directive "clients".
<div container
ng-cloak
ng-app="summaryReportApp"
ng-controller="summaryReportController as summaryReport">
<fieldset clients="summaryReport.clients">
<legend>Clients</legend>
<div align="left">
<div ng-repeat="client in clients track by $index">
{{client}}
</div>
</div>
</fieldset>
</div>
directive
var clients = function(){
var definition = {
restrict: "A",
scope: {
clients:"=clients"
},
link: function($scope,$element,attributes){
}
}
return definition;
}
This is a common question I seem to answer frequently. Directives can have other HTML Elements nested in them, in the same way that an <input> can be nested inside a <div>. However, the Elements nested inside the Directive are not part of the directive, and are not scoped to the directive, they are scoped to the HTML they are in. The only items that have access to the Isolated Scope are the compile, link, controller, and template items in the directive definition. If you moved your inner html from inside the fieldset into a template, it would function as expected.
You can also reference http://angular-tips.com/blog/2014/03/transclusion-and-scopes/ for more examples and ways to test this.

How can i get rid of $parent in angular

Here's Plunker
I have an external template within in a controller with ng-include. It is shown and hidden based on click event of Button.It is working as required but with $parent in ng-include Template.Is there any other better way of doing this ?
Html
<body ng-controller="MainCtrl">
<div data-ng-include="'terms.html'" data-ng-show="otherContent"></div>
<div ng-show="mainPage">
<p>Hello {{name}}!</p>
<button data-ng-click="mainPage=false; otherContent=true">Link to some Content</button>
</div>
</body>
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.mainPage=true;
});
External Template
<p>Some content here </p>
<button data-ng-click="$parent.mainPage=true; $parent.otherContent=false">Back</button>
Option1 - Set property on an object in the scope
In the main controller add an object on the scope.
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.page={mainPage:true};
});
and in the ng-click do:-
<div data-ng-include="'terms.html'" data-ng-show="page.otherContent"></div>
<div ng-show="page.mainPage">
<button data-ng-click="page.mainPage=true; page.otherContent=false">Back</button>
<!-- -->
<button data-ng-click="page.mainPage=true; page.otherContent=false">Back</button>
Demo - setting property on an object in the scope
Option2 - Use function
Instead of setting properties on the view (Which is anyways a good idea to abstract out too much logic from the view), Do your set operations in the controller exposed as a function that can be invoked from the view, which also gives extensibility when you need to add more logic for that particular action. And in your case you could even use the same function and call it from both the button clicks and flipped based on a boolean argument.
Demo - with function
Option3 - Use Controller Alias
Using an alias for the controller, which is nothing but instance of the controller is set as a property on the scope with the property name same as the alias provided. This will make sure you are enforce to use dot notation in your bindings and makes sure the properties you access on the child scopes with the controller alias are inherited as object reference from its parent and changes made are reflected both ways. With angular 1.3, it is also possibly to set the isolate scoped directive properties are bound to the controller instance automatically by setting bindToController property in the directive configuration.
Demo - With Controller alias
ControllerAs is the recommend way of avoiding this problem.
Using controller as makes it obvious which controller you are accessing in the template when multiple controllers apply to an element.
If you are writing your controllers as classes you have easier access to the properties and methods, which will appear on the scope, from inside the controller code.
Since there is always a . in the bindings, you don't have to worry about prototypal inheritance masking primitives.
<body ng-controller="MainCtrl as main">
<div data-ng-include="'terms.html'" data-ng-show="main.otherContent"></div>
<div ng-show="mainPage">
<p>Hello {{main.name}}!</p>
<button data-ng-click="main.mainPage=false; main.otherContent=true">Link to some Content</button>
</div>
</body>
Here are some resources for controller as:
http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/
http://toddmotto.com/digging-into-angulars-controller-as-syntax/
https://docs.angularjs.org/api/ng/directive/ngController#example

using ng elements outside of ng-controller

I am inexperienced with angular.
I am using angular to create a series of nested divs (a form) on a webpage. The top div has ng-controller="controllername" as an attribute. Within the nested divs is a div with ng-show="showvar" as an attribute.
It looks like this.
<div class="page">
<div ng-controller="controllername">
<div ng-show="showvar">Hidden Stuff</div>
</div>
</div>
When I perform functions on showvar to make it true, the div appears (and disappears when false) as intended.
I also have a completely separate div 'outside' the the original nest of divs with the ng-controller attribute. As such, there is no ng-controller attribute in this seperate hierarchy BUT I have nested another div inside with the ng-show="showvar" attribute.
Updated HTML structure is as such
<div class="page">
<div ng-controller="controllername">
<div ng-show="showvar">Hidden Stuff</div>
</div>
<div class="seperate">
<div ng-show="showvar">More Hidden Stuff</div>
</div>
</div>
When the page loads, both divs with ng-show="showvar" in the separate nests are hidden as ng-hide has been appended by angular. When I perform functions on showvar after the page load to make it true, only the div within the ng-controller div gets shown.
I (think I) understand this is because the ng elements are evaluated at page load (and appended with ng-hide, even outside the controller?) but only the ng elements within the div with the ng-controller attribute are evaluated when functions are performed after page load. Is this correct?
How can I get the other ng-show to be evaluate 'outside' of the ng-controller div?
I was thinking one option is to append ng-controller to the overall 'page' div instead of the nested div. But what other options do I have?
EDIT: I also tried simply adding ng-controller="controllername" to the separate div. I guess angular 'ignores' the duplicate ng-controller div?
The problem your facing is that the showvar resides in your controller's scope, your second usage of the showvar is not within that scope.
What you need to do is make sure the variable is available where needed.
Say you add the variable to the parentController (you don't have one in your example so I'll add one)
<div class="page" ng-controller="parentController">
<div ng-controller="controllername">
<div ng-show="showvar">Hidden Stuff</div>
</div>
<div class="seperate">
<div ng-show="showvar">More Hidden Stuff</div>
</div>
</div>
app.controller('ParentController', function($scope){
$scope.showvar = false;
});
problem with this is when you set showvar to true within your controllername controller it will set it in the innerscope and not the outer. When making sure you have the right scope by accessing it through another object you should be safe.
So try it like this:
<div class="page" ng-controller="parentController">
<div ng-controller="controllername">
<div ng-show="obj.showvar">Hidden Stuff</div>
</div>
<div class="seperate">
<div ng-show="obj.showvar">More Hidden Stuff</div>
</div>
</div>
app.controller('ParentController', function($scope){
$scope.obj = {
showvar: false
}
});
Quick demo
Your issue here is that you ended with 2 "showvar" variables: one within the "controllername" scope and another one on the app scope (as you have a ng-app declaration somewhere in your html parent of the "page" div).
When you load your page, you get the value of "showvar" in the controller scope for the first div, and for the "separate" one, you get the "showvar" variable in the app scope, which doesn't exist, therefore it is resolved to "false" (even though angular declares it for you in your app scope and you can even modify its value later).
When you change the value of "showvar" in the controller scope, it doesn't change the one in the app scope, making the "separate" div stay hidden forever =)

Resources