I am trying to conditionally display a directive based on a boolean value stored in the parent scope. I can't figure out why the below does not work. By, "not work" I mean neither directives are displayed.
<ul class="nav navbar-nav pull-right" ng-switch="userIsAuthenticated">
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in anonymousMenuItems" ng-switch-when="false"></account-item>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in authenticatedMenuItems" ng-switch-when="true"></account-item>
</ul>
Neither directives are shown even thought "userIsAuthenticated" is set to 'false' in my test case. If I add {{userIsAuthenticated}} above the directives 'false' is output as expected.
I've also tried this:
<ul class="nav navbar-nav pull-right" ng-switch={{userIsAuthenticated}}>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in anonymousMenuItems" ng-switch-when={{false}}></account-item>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in authenticatedMenuItems" ng-switch-when={{true}}></account-item>
</ul>
If I remove the conditional ng-switch-when attribute from either of the directives they will display. So I'm know the problem is my ng-switch.
Your usage of ng-switch works in this simplified demo, of course without your account-item directive:
http://plnkr.co/AppN8xmFeIwjaP631lj7
Without seeing the code for account-item, it is hard to guess what might be interfering with it. You might consider using ng-if to handle displaying one item or another.
<ul>
<div ng-if="!userIsAuthenticated">Content when not authenticated</div>
<div ng-if="userIsAuthenticated">Content when authenticated</div>
</ul>
Update
Also make sure you bind to an object property, instead of a primitive boolean. Like: user. authenticated
Since ngSwitchWhen has a priority of 800, you need to set a higher priority to your custom directive (i.e. account-item) in order for it to be compiled before being process by the ngSwitchWhen directive. E.g.:
.directive('accountItem', function () {
return {
...
priority: 900,
...
};
});
Related
I'm learning Angular 12 and I have some issues about the framework operation.
I've created a new project, added Bootstrap 5 and created some components.
When I nest a component inside another like this :
<div class="container">
<div class="row">
<div class="col-xs-12">
<h2>Mes appareils</h2>
<ul class="list-group">
<app-appareil [appareilName]="appareilOne"></app-appareil>
<app-appareil [appareilName]="appareilTwo"></app-appareil>
<app-appareil [appareilName]="appareilThree"></app-appareil>
</ul>
</div>
</div>
</div>
I don't understand why I still see the custom selectors in the browser inspector view :
Angular browser view
It breaks several things in my Boostrap style.
Did you know if it's possible to hide/remove these custom components of my browser view to get in this case only the <li> tags directly inside the <ul> instead of these <app-appareil> ?
Thanks :)
Change
#Component({
selector: "app-appareil"
})
to
#Component({
selector: "li[appAppareil]"
})
and then use it as
<ul class="list-group">
<li appAppareil [appareilName]="appareilOne"></li>
</ul>
By using an attribute selector we can avoid the wrapping component tag (which you cannot "remove"), and we preserve semantics of the DOM itself.
Likely to get better semantics you'd want to make further changes and use content projection, but that's unclear from the limited information and beyond the scope of the question anyway.
To make it the "Angular way", the approach needs to be changed.
Supposing that you have a collection of device names (appareilNames) returned from your component:
public get deviceNames(): Array<string> { ... }
The appropriate tags structure can be achieved as follows:
<ul class="list-group">
<li *ngFor="let deviceName of deviceNames"> <!-- iterate on each device name -->
<app-appareil [appareilName]="deviceName"></app-appareil> <!-- use each name to create a component with it -->
</li>
</ul>
I have an ng-class where class is dynamically loaded i.e evaluating $rootscope in expression, late binding is happening
<li role="presentation" style="width:200px" ng-class="{active: true, inactive: false}[{{vm.$rootScope.currentPage}}==1]">Create</li>
Here {{vm.$rootScope.currentPage}} expression is evaluating after class for the control is loaded in place of that if give 1 it is working or and if I check in inspect element I can see [1==1] i.e {{vm.$rootScope.currentPage}} this is returning correct value but class is not getting applied
Can refer this Condition in ng- class not working
you should use [vm.$rootScope.currentPage===1] instead of [{{vm.$rootScope.currentPage}}==1] . no need to use {{}} in []
and need to change
ng-class="{true: 'active', false:'inactive' }" instead of ng-class="{'active': true , 'inactive':false }"
for ng-class="{true: 'active', false:'inactive' }[vm.$rootScope.currentPage==1]" class applied way
if expression value is true then apply class active
if expression value is false then apply class inactive
so use:
<li role="presentation" style="width:200px" ng-class="{true: 'active', false:'inactive' }[vm.$rootScope.currentPage===1]">Create</li>
Also can use ternary operator in ng-class like: ng-class="vm.currentPage===1? 'active': 'inactive'"
<li role="presentation" style="width:200px" ng-class="vm.currentPage===1? 'active': 'inactive'">
Create
</li>
PLUNKER DEMO
Try this- ng-class="vm.$rootScope.currentPage==1?'active':'inactive'".
You're adding multiple extra layers of work for Angular, just for a simple true/false test. Most notably, you had a {{ }} expression in an ng directive. AFAIK, all of these already track the expression you put in, and adding a {{ }} just adds more that it needs to watch.
You may also be able to refer it it simply as currentPage, instead of vm.$rootScope.currentPage, but that depends if you've set up the scoping for whatever component this is.
Good morning,
I'm trying to change the limitTo filter on a certain list, my issue is:
when I click to the trigger who change the filter limit the filter changes on all ng-repeated categories.
my function inside the main controller
$scope.showMore = function(limit) {
if($scope.limitItems === $scope.itemsPerList) {
$scope.limitItems = limit;
$scope.switchFilterText = 'less';
} else {
$scope.switchFilterText = 'more';
$scope.limitItems = $scope.itemsPerList;
}
}
my scenario (I rewrote it in a simplified version)
<li ng-repeat="item in category.items | limitTo: limitItems ">
{{item.title}}
</li>
<li ng-if="limitItems < (category.items.length)">
<a ng-click="showMore(category.items.length)" >Show {{ switchFilterText }}</a>
</li>
Could you explain me what's wrong with me?
I searched how to select a single element to apply the function but I didn't find anything useful
Update:
I found the way to solve my issue in this way:
No functions inside the controller are involved to make this functionality works properly:
<li ng-repeat="category in maincategories" ng-init="limitItems = maxItemsPerList">
{{category.title}}
<ul>
<li ng-repeat="item in category.items | limitTo: limitItems "> {{item.title}}
</li>
</ul>
<a ng-click="limitItems = category.items.length" href>
<b ng-if="category.items.length > maxItemsPerList && limitItems != category.items.length "> Show more </b>
</a>
I'm not really convinced about Angular (I used it in my past and I was impressed by the performance but now I can see logics senseless):
What I learned:
ng-if and ng-click cannot be used in the same content because ng-if creates new scopes so if you put ng-if on top of the "show more" link it will break the code
ng-init cannot be used in the same element of the ng-repeat otherwise the var initialised will not be available inside the ng-repeat block
I think there is another way to do that, maybe more clean but in this specific case I can't do a lot.
ng-if and ng-click cannot be used in the same content because ng-if
creates new scopes so if you put ng-if on top of the "show more" link
it will break the code
Yes, ng-if creates a new scope, but it is possible to mix ng-if and ng-click (and most other directives). To do that, you'll be safer if you always write to atributes of another object instead of a simple variable. It is plain JavaScript prototypal inheritance in play.
<li ... ng-init="category.limitItems = maxItemsPerList">
ng-init cannot be used in the same element of the ng-repeat otherwise
the var initialised will not be available inside the ng-repeat block
True, in the sense that variables are created in the local scope. But again, refer to an object.
I think there is another way to do that, maybe more clean but in this
specific case I can't do a lot.
You don't need to do a lot, it is quite simple to do it right actually.
Some advices:
Use ng-init with care. I know it will tempt us but always try to put logic inside controllers and services;
Avoid assignments inside templates;
Learn how to use controllerAs syntax. It gives you an object to write your models to (the controller), so solves most problems related to scope inheritance;
Do not inject $scope, put your view models inside controllers.
Full code goes like this:
<li ng-repeat="category in maincategories" ng-init="category.limitItems = maxItemsPerList">
{{category.title}}
<ul>
<li ng-repeat="item in category.items | limitTo: category.limitItems "> {{item.title}}
</li>
</ul>
<a ng-if="category.items.length > maxItemsPerList && category.limitItems != category.items.length" ng-click="category.limitItems = category.items.length" href>
<b> Show more </b>
</a>
I'm learning AngularJS and noticed that Angular inserts template or partial HTML as a child of angular elements such as ng-include or custom directives. In my case this breaks styling because Bootstrap styles do not select elements that are children of the directive. So, I believe I'm either misunderstanding how to use directives or perhaps lack the terms for managing this behavior.
For example:
I want to specify just my custom directive and not associate a particular HTML type.
Input:
<ul class="nav navbar-nav">
<my-dropdown-messages/>
</ul>
Result:
<ul class="nav navbar-nav">
<my-dropdown-messages>
<li class="dropdown my-intended">content</li>
</my-dropdown-messages>
</ul>
Desired result:
<ul class="nav navbar-nav">
<li my-dropdown-messages class="dropdown my-intended">content</li>
</ul>
or
<ul class="nav navbar-nav">
<form my-dropdown-messages class="my-intended">content</form>
</ul>
Is it possible to configure ng-include or custom directives so that the insertion sets the native html type, and does not put the partial html as a child of the angular directive?
You can use an Attribute directive instead of a Element directive
app.directive('myDropdownMessages', function() {
return {
restrict: 'A', // Attribute directive
templateUrl: 'my-dropdown.html'
};
});
From AngularJS Docs
When should I use an attribute versus an element? Use an element when you are creating a component that is in control of the template. The common case for this is when you are creating a Domain-Specific Language for parts of your template. Use an attribute when you are decorating an existing element with new functionality.
https://docs.angularjs.org/guide/directive
using replace: true for your directive
but this property will be removed soon as very few scenarios where element replacement is required
refer to directive
I have been learning angular and found this when trying to access the parent controller
http://jsfiddle.net/eqb23s8t/
I was expecting to access the same variable from the parent controller from inside the ng-repeat using the $parent so when one of the checkbox is pressed, all should be updated, but this is not true. Why ?.
<div ng-app ng-controller="ParentCtrl">
<ul>
<li ng-repeat="city in cities">{{city}}<input type="checkbox" ng-checked="$parent.somevar" /></li>
</ul>
</div>
First, your jsFiddle has a ChildCtrl defined but it will have no effect because you never use it. You can delete it.
Second, as described in the ngChecked documentation, there is a difference between ngChecked and ngModel:
https://docs.angularjs.org/api/ng/directive/ngChecked
If what you're expecting to happen is have all the checkboxes check/uncheck together, you probably want ngModel rather than ngChecked.
It's not clear from your question what you're actually trying to do, but here's a fork of your jsFiddle illustrating data sharing through a $parent variable:
http://jsfiddle.net/7jzyk7f6/
It just does the following:
<li ng-repeat="city in cities">{{city}}<input type="checkbox" ng-model="$parent.somevar" /></li>
to illustrate both concepts.
You are not bind a model for the view, which can reflect the changes. The current code just reads the model (in this case the somevar) state. Use ng-model instead of ng-checked. The angular will handle the rest:
HTML
<div ng-app ng-controller="ParentCtrl">
<ul>
<li ng-repeat="city in cities">{{city}}<input type="checkbox" ng-model="$parent.somevar" /></li>
</ul>
</div>
JS
function ParentCtrl($scope) {
$scope.cities = ["NY", "Amsterdam", "Barcelona"];
$scope.somevar = true;
}
http://jsfiddle.net/eqb23s8t/4/
In this case, you need to use ngModel (two-way binding) instead of ngChecked (one-way binding):
ng-model="$parent.somevar"
See JSFiddle
I'm also totally newbie in Angular so I can be wrong. But I see few... things in your code. First of all ChildCtrl is not used at all. Second, it looks like only ng-model directive applied two-way binding to checkboxes. Yet ng-checked used just to add|remove checked attribute.
And js-fiddle
Sir please use ng-model to refer scope of the parent.
here is what i have created demo for you [demo][1]
[1]: http://jsfiddle.net/nwg7bwLx/