Uncertainty about the use of ngInclude - angularjs

I render recursive template with ngInclude. Here is my code:
<!--
definitionsRenderer.html
-->
<ul data-ng-if="currentLoc.definitions && templateIsloaded">
<li ng-repeat="definition in currentLoc.definitions">
{{definition.text}}
</li>
</ul>
<ul data-ng-if="templateIsloaded">
<li ng-repeat="loc in currentLoc.loc" ng-include="'./partials/definitionsRenderer.html'"></li>
</ul>
I need code to be executed asynchronous. So for scope of every included template
I want to have a variable - templateIsloaded with value false by default. When template is loaded and rendered it value to be true. How do that?
Best regards.

To have separate $scope for each of recursive called includes you can simply add ng-controller.
<li ng-repeat="loc in currentLoc.loc" ng-controller="definitionsRendererCtrl" ng-include="'./partials/definitionsRenderer.html'"></li>

Related

Angular 1.5.8 Component, get parent context in ng-repeat

I am creating a dynamic menu with ng-click action template is configured in DB.
<ul class="dropdown-menu dropdown-menu-right" id="widget-{{$ctrl.id}}-context-menu">
<li ng-repeat="Menu in $ctrl.menu">
{{Menu.Name}}
</li>
</ul>
How can I get reference to the parent context from ng-repeat so that I can parse the template string (eg. '$ctrl.handleClick('abc')') to make a click event? $parent and $ctrl are not working inside ng-repeat.
What is the correct way to convert a string to an ng-click event?
What should I do in
The $scope with components is always isolated, I am not sure you can access it that way and you shouldn't. What you need to do is bind the function from your parent controller/component.
Bind the function from the parent: ($ctrl is the parent here)
<my-component action="$ctrl.evaluateFn">
Add the binding to your component:
bindings: {
action: '&'
}
Use it in your markup:
<ul class="dropdown-menu dropdown-menu-right" id="widget-{{$ctrl.id}}-context-menu">
<li ng-repeat="Menu in $ctrl.menu">
{{Menu.Name}}
</li>
</ul>
You can use whatever you want instead of action.
https://docs.angularjs.org/guide/component

Submit and disable all buttons in an ng-repeat without adding a new property on the scope

I have a list of items where each item is clickable and should trigger the submit(id) function. I would like to disable all items (buttons) when one of them is clicked.
<ul>
<li ng-repeat="item in items">
<button ng-click="submit(item.id)">Submit</button>
</li>
</ul>
I could define $scope.submitted variable in a controller and then set ng-disabled="submitted" in my view. I could also wrap it into a <form> and use frm.$submitted. Well... I would like to define everything inside view.
I search for an elegant solution where a don't have to define a $scope variable to achieve this. What do you propose?
If you do not want to define a property in the controller, and assuming that submit function never fails to submit. You could utilize the items array itself, by adding a property when click happens and after submit function is run.
<ul>
<li ng-repeat="item in items">
<button ng-click="submit(item.id); items.submitted=true"
ng-disabled="items.submitted">Submit</button>
</li>
</ul>
With this you are adding a property in the array items (so that it is available across child scopes of ng-repeat as well as items is set on its parent scope) so essentially you are not defining a new scope variable for this.
angular.module('app',[]).controller('ctrl', function($scope){
$scope.items = [{id:1},{id:2},{id:3},{id:4}]
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul>
<li ng-repeat="item in items">
<button ng-click="submit(item.id); items.submitted=true"
ng-disabled="items.submitted">Submit</button>
</li>
</ul>
</div>

Parse:syntax error running angular code inside directives

The following code is giving me Error: [$parse:syntax] in console.
<ul class="nav navbar-nav" ng-repeat="webpage in first.webpages">
<li><a href ng-click="tab = {{webpage.id}}">{{webpage.name}}</a></li>
</ul>
ng-repeat is properly working for {{webpage.name}} only, not for {{webpage.id}} which is placed inside ng-click directive. When I try to print {{tab}} nothing shows up.
I believe you don't need double braces inside angular elements. And you need to call the parent scope when in ng-repeat :
ng-click="$parent.tab = webpage.id"
It should be:
<ul class="nav navbar-nav" ng-repeat="webpage in first.webpages">
<li><a href ng-click="tab = webpage.id">{{webpage.name}}</a></li>
</ul>
You don't need to interpolate anything when you are inside ng-click since it does it for you.

Angular ng-class code

I am very new to Angular and I have this piece of code:
<nav ng-controller="navController">
<ul>
<li ng-class="{'active': location('/') || location('/homepage')}">
view puppies
</li>
</ul>
</nav>
With the corresponding controller:
obApp.controller('navController', function($scope, $location) {
$scope.location = function(loc){
if(loc == $location.path()){
return true;
}else{
return false;
}
}
})
Now everything works fine. When I'm on the homepage the list element gets the class active added to it, HOWEVER there is also the angular ng-class directive code shown in the source, like so (this is the HTML source output of the browser):
<li ng-class="{'active': location('/') || location('/homepage')}" class="active">
view puppies
</li>
Should the list element not look like: <li class="active">view puppies</li> ? Logic would dictate that it should look like it is now, because ng-controller="x" shows in the source as it is as well. However that's a hook if you will. The other one's an expression. It looks funky and I'm not sure if it's ok or not. Please, if you answer, detail your answer.
There is nothing wrong with your code, ng-class directive doesn't remove the attribute ng-class from the element when the expression evaluates to true, since you could have more than one expression/class in your ng-class attribute value.
Additionally if the directive did remove the attribute (ng-class) angular would have no way of knowing that you have applied that directive to the element. For example, your element had ng-if on it, it would be removed from the DOM and appended conditionally.
Example
ng-class="{'active': location('/') || location('homepage'), 'disabled': location('private'))}"
Such usage of ng-class is non-effective because $scope.location method will be called at least twice (for location('/') and location('homepage')) each digest loop. There could be a lot of digest loops due to balancing mechanism.
Better approach is to calculate if element is active on location change.
Controller:
$scope.$on('$locationChangeSuccess', function(){
$scope.isHomePage = location('/') || location('/homepage');
});
Markup:
<nav ng-controller="navController">
<ul>
<li ng-class="{'active': isHomePage}">
view puppies
</li>
</ul>
</nav>

Call another controller from inside ng-repeat

I am new to angular and I am trying to call a function from another Controller. I do not want to define an addIssue function in ItemsController which calls IssuesController.addIssue or share a service, but directly reference the IssuesController. Is there any way to do that?
Here is my sample code:
<div ng-controller="ItemsController">
<ul ng-repeat="item in items">
<li><a href ng-click="addIssue(item)">{{item.name}}</a></li>
</ul>
</div>
<div ng-controller="IssuesController">
<ul ng-repeat="issue in issues">
<li>{{issue.name}}</li>
</ul>
</div>
<script>
angular.module('app').controller("IssuesController", function () {
$scope.issues = [];
$scope.addIssue = function (item) {
// Add Issue
}
});
</script>
UPDATE:
What if I have a third base controller to help them share $scope, how will that work?
<div ng-controller="OrdersController">
<div ng-controller="ItemsController">
<ul ng-repeat="item in items">
<li><a href ng-click="addIssue(item)">{{item.name}}</a></li>
</ul>
</div>
<div ng-controller="IssuesController">
<ul ng-repeat="issue in issues">
<li>{{issue.name}}</li>
</ul>
</div>
</div>
As EliteOctagon mentioned, there are other and probably more "best practice" ways to achieve your goal but you can always do this the "dirty" way, you can use $rootScope in your ItemsController to call the addIssue(item) function on IssuesController, and since all other scopes are descendant scopes of the $rootScope you can use it in any controller.
Example: http://jsfiddle.net/choroshin/5YDJW/2/
There is, but it's such a terrible idea that you should think about it thoroughly. A much better solution would be to put that functionality into a parent controller. Obviously you have functionality that is much broader in scope (pun intended) then just that div with the IssuesController.
Provided your code is a realistic sample, then addIssue() is not even used there. So it wouldn't even make sense to put it there.
One way to directly access addIssue is:
angular.element(/*select your div*/).scope().addIssue(item);
How you select your div depends. If i has an id you can use that. If you use jQuery then you can use [ng-controller= ItemsController].
Why don't you nest the controllers? You can read the Scope Inheritance Example at http://docs.angularjs.org/guide/controller
<div ng-controller="ItemsController">
<ul ng-repeat="item in items">
<li><a href ng-click="addIssue(item)">{{item.name}}</a></li>
</ul>
<div ng-controller="IssuesController"> <!-- nested and will inherit scope -->
<ul ng-repeat="issue in issues">
<li>{{issue.name}}</li>
</ul>
</div>
</div>
so if you have a function in ItemsController
$scope.addIssue = function() {do something}
then the IssuesController will inherit the $scope and you'll be able to use that function.

Resources