Hide parent element in DOM when children are empty - javascript

<div>
<h1>Birds</h1>
<ul>
<li ng-if="bird.type === 'bird'"
ng-repeat="bird in creatures">{{bird.name}}</li>
</ul>
</div>
I have a response from the server and I want to put it into the list. But when the list is empty I need to hide this div container. For example if bird.type === 'bird' is not in the array - I want to hide div. But I want to use bird after ng-repeat so I cant make ng-if="bird.type === 'bird'" on div. Can I check if li is empty, then hide the div?
plunkr example
AngularJS ng-repeat handle empty list case - It's not the same. I don't want to hide li if it empty, I want to hide parent where I have h1 - when li is empty.

You could do the following:
<div ng-if="hasBirds(creatures)">
<h1>Birds</h1>
<ul>
<li ng-if="bird.type === 'bird'"
ng-repeat="bird in creatures">{{bird.name}}</li>
</ul>
</div>
And then in controller/directive you can add the hasBirds function.
$scope.hasBirds = function(list){
return list.filter(function(item){return item.type === 'bird'}).length > 0;
}
This hasBirds function would get called often, but would allow you to hide/show the heading of the birds exist.

In your example I advise you to use a filter instead of using "ng-if", you should create a filter like:
angular.module('moduleName').filter(birdsFilter);
function birdsFilter(creature) {
return creature.type == 'bird';
}
With the filter you can rewrite your code like this:
<div ng-hide="birds.length">
<h1>Birds</h1>
<ul>
<li ng-repeat="bird in birds = (creatures | filter:birdsFilter)">{{bird.name}}</li>
</ul>
</div>

IMO, several of these answers will work. But none of them are ideally optimized. I would recommend filtering your data in your controller/postlink function.
$scope.animals = {
dogs: $scope.creates.filter(function(a){return a.type == 'dog'}),
cats: $scope.creates.filter(function(a){return a.type == 'cat'}),
birds: $scope.creates.filter(function(a){return a.type == 'bird'}),
fishes: $scope.creates.filter(function(a){return a.type == 'fish'})
};
This way you would only ever process the array of creatures one time, in one spot. The digest cycle would never have to re-eval the array to see if it needed to update the DOM. Here is what you markup with look like then:
<div ng-if="animals.birds.length">
<h1>Birds</h1>
<ul>
<li ng-repeat="bird in animals.birds">{{bird.name}}</li>
</ul>
</div>

You should filter the list based on the type, store the filtered items in a scope property then use that property to show or hide the div.
<div ng-show="birds.length">
<h1>Birds</h1>
<ul>
<li ng-repeat="bird in creatures | filter:birdType as birds">{{bird.name}} </li>
</ul>
</div>
Then implement the birdType filter function in your controller:
$scope.birdType = function(creature) {
return creature.type === 'bird';
};

Using ng-show="cats.length" to make div's disappear if length is zero.
Filter inline based on object property like cat in creatures | filter:{type: 'cat'} as cats as discussed in this SO post.
WORKING EXAMPLE:
var app = angular.module('App', []);
app.filter(birdsFilter);
function birdsFilter(creature) {
return creature.type == 'bird';
}
app.controller('Ctrl', function($scope) {
$scope.creatures = [
{
name : 'Cho-cho',
type : 'bird'
},
{
name : 'Floo-floo',
type : 'dog'
},
{
name : 'Pou-pou',
type : 'bird'
},
{
name : 'Oop-flup',
type : 'bird'
},
{
name : 'Chio-mio',
type : 'cat'
},
{
name : 'Floo-floo',
type : 'dog'
},
{
name : 'Loo-Li',
type : 'dog'
},
{
name : 'Pops-Mops',
type : 'bird'
},
{
name : 'Boo-Moo',
type : 'dog'
},
{
name : 'Iop-Pio',
type : 'dog'
},
{
name : 'Floop-cho',
type : 'bird'
}
]
});
<!DOCTYPE html>
<html ng-app="App">
<head>
<script data-require="angularjs#1.5.7" data-semver="1.5.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="Ctrl">
<div ng-show="birds.length">
<h1>Birds</h1>
<ul>
<li ng-repeat="bird in creatures | filter:{type: 'bird'} as birds">{{bird.name}} </li>
</ul>
</div>
<div ng-show="dogs.length">
<h1>Dogs</h1>
<ul>
<li ng-repeat="dog in creatures | filter:{type: 'dog'} as dogs">{{dog.name}} </li>
</ul>
</div>
<div ng-show="cats.length">
<h1>Cats</h1>
<ul>
<li ng-repeat="cat in creatures | filter:{type: 'cat'} as cats">{{cat.name}} </li>
</ul>
</div>
<div ng-show="fishes.length">
<h1>Fish</h1>
<ul>
<li ng-repeat="fish in creatures | filter:{type: 'fish'} as fishes">{{fish.name}} </li>
</ul>
</div>
</body>
</html>

Related

Add style to specific element angular 6

I have the following block of code in the front-end:
<li *ngFor = "let cat of this.dataCategory.iconTitleSet" (click)="getTypeFromCategory(cat.title)" class="list-group-item puntero">
<img [src]="cat.icon" alt="icon" title="icon" />{{cat.title}}
</li>
in the component:
getTypeFromCategory(tipo: string) {
this.typeItem = tipo.toLowerCase();
if (this.arrayTipo.includes(this.typeItem)) {
const i = this.arrayTipo.indexOf( this.typeItem );
this.arrayTipo.splice( i, 1 );
} else {
this.arrayTipo.push(this.typeItem);
}
}
in synthesis what up to now does is add a value that I get from FRONTEND in an array in case it is not, and if it eliminates it from the array, but when I add it I also want to put a specific style, for example a yellow background, but this last I do not know how to do it, I do not know how to say to angular that I put a specific style in element "li" specific generated by an "ngfor" loop when I click on the element.
this is the image in the frontend
You can do it in some ways:
1. to set a diffrennt class with [ngClass]="" to each li and style in css
for example:
<li *ngFor = "let cat of this.dataCategory.iconTitleSet;let i=index;" [ngClass]="'title_'+i" (click)="getTypeFromCategory(cat.title)" class="list-group-item puntero">
<img [src]="cat.icon" alt="icon" title="icon" />{{cat.title}}
</li>
in css:
.title_1{}
2. you can set it with [ngStyle] or style.
<li *ngFor = "let cat of this.dataCategory.iconTitleSet;let i=index;" [style.color]="cat.color" (click)="getTypeFromCategory(cat.title)" class="list-group-item puntero">
<img [src]="cat.icon" alt="icon" title="icon" />{{cat.title}}
</li>
You can do the following.
HTML
<li *ngFor = "let cat of this.dataCategory.iconTitleSet; let i = index" (click)="getTypeFromCategory(cat.title, index)" class="list-group-item puntero" [class.changeColor]="i == selectedValue">
<img [src]="cat.icon" alt="icon" title="icon" />{{cat.title}}
Component
selectedValue: any;
getTypeFromCategory(tipo: string, index) {
this.selectedValue = index;
this.typeItem = tipo.toLowerCase();
if (this.arrayTipo.includes(this.typeItem)) {
const i = this.arrayTipo.indexOf( this.typeItem );
this.arrayTipo.splice( i, 1 );
} else {
this.arrayTipo.push(this.typeItem);
}
}
class changeColor will be applied to every list item selected.

How to display following object in angular5 html file?

In the above screenshot console I have an object with 2 values users and tickers. Again each one is array of values.
Now how to display these values in angular5 html template as specified in above screenshot?
I am trying to use ngFor but it is showing errors.
Suppose this is your data:
public data = {
tickers:[
{id:"1",name:"Ticker Name 1", company:"Company 1"},
{id:"2",name:"Ticker Name 2", company:"comapny 2"}
],
users:[
{id:"1",first_name:"User1 ", last_name:"u1last", email:"user1#test.com"},
{id:"2",first_name:"User2", last_name:"u2last", email:"user2#test.com"},
{id:"3",first_name:"User3", last_name:"u3last", email:"user3#test.com"},
{id:"4",first_name:"User4", last_name:"u4last", email:"user4#test.com"}
]
};
public dataKeys; // This will hold the main Object Keys.
The constructor will look something like this:
constructor() {
this.dataKeys = Object.keys(this.data);
}
Here is the simple HTML that you need to write:
<div *ngFor="let key of dataKeys">
<h3>{{ key }}</h3>
<ul>
<li *ngFor="let d of data[key]">{{d.name || d.first_name}}</li>
</ul>
</div>
Here is the complete working plunker for your case:
Click here to view the Working Solution
you can use like that,
<ul>
<P>users</p>
<li *ngFor="let item of object.users; let i = index">
{{i}}. {{item.frist_name}}
</li>
<P>Tickets</p>
<li *ngFor="let item of object.tickers; let i = index">
{{i}}. {{item.name}}
</li>
</ul>
According to the documentation it should be this:
Users:
<ol>
<li *ngFor="let user of users">{{user.first_name}}</li>
</ol>
Tickets:
<ol>
<li *ngFor="let ticket of tickets">{{ticket.name}}</li>
</ol>
You can use json pipe for debug purpose like this:
{{object |json}}
If you want exactly as in picture, look at this solution. With this case, you don't need to manually write first level property names of object in template for using *ngFor

Changing Element Colour on Hover AngularJS

So, I'm just getting started with angularjs and I'm already confused. I want to change the colour of a list element that corresponds to a hex code colour that is in an array. I've tried some stuff but I just can't get it.
Here's my code so far:
HTML
<div id="mainContentWrap" ng-app="newApp">
<div id="personContainer" ng-controller="personController">
<ul id="personList">
<li class="bigBox no_s" ng-style="personColour" ng-repeat="i in persons" ng-hover="changeColor()">< href="#/{{i.person_id}}">{{i.person_name}}</a></li>
</ul>
Javascript:
var app=angular.module('newApp',[]);
app.controller('personController',function($scope,$rootScope){
$rootScope.persons=[
{person_id:'0',person_name:'Jim',colour:"cc0000"},
{person_id:'4',person_name:'Bob',colour:"f57900"},
{person_id:'2',person_name:'James',colour:"4e9a06"},
{person_id:'9',person_name:'Paul',colour:"3465a4"},
{person_id:'3',person_name:'Simon',colour:"77507b"}
];
$scope.changeColor(){
$scope.personColour=$scope.persons.color// not sure what to do here???
}
});
There is no ng-hover directive. You'll want to use ng-mouseenter and ng-mouseleave.
Also, keep in mind that the syntax for ng-style is an object corresponding the CSS key-value pairs.
<li ng-repeat="i in persons" ng-style="personColour" ng-mouseenter="changeColor(i)"></li>
$scope.changeColor = function(person) {
$scope.personColour = {color: '#'+person.colour};
};
If you'd like for the color to change back to what it was before you hovered, you can create two functions, or pass a parameter to $scope.changeColour:
<li ng-repeat="i in persons" ng-style="personColour" ng-mouseenter="changeColor(i,true)" ng-mouseleave="changeColor(i,false)"></li>
$scope.changeColor = function(person, bool) {
if(bool === true) {
$scope.personColour = {color: '#'+person.colour};
} else if (bool === false) {
$scope.personColour = {color: 'white'}; //or, whatever the original color is
}
};
To take it a step further
You could create a directive for each person.
<person ng-repeat="i in persons"></person>
// your module, then...
.directive('person', [function() {
return {
restrict: 'E',
replace: true,
template: '<li class="bigBox no_s">{{i.person_name}}</li>',
link: function(scope, elm, attrs) {
elm
.on('mouseenter',function() {
elm.css('color','#'+i.colour);
})
.on('mouseleave',function() {
elm.css('color','white');
});
}
};
}]);
If you want to hack stay in the view:
<div ng-repeat="n in [1, 2, 3]" ng-style="{ 'background': (isHover ? '#ccc' : 'transparent') }" ng-mouseenter="isHover = true;" ng-mouseleave="isHover = false;">
<span>{{ n }}</span>
</div>
in the code bellow i add easy code to understand how to active style with condition. I hope that help you
<li ng-style="( (isOver == 'true') && (linkToActive == 'm1') ) ? { 'background-color': '#00bdcb' } : {'background-color': '#ededed'}"
ng-mouseenter="vm.changeColorMenu('m1','true')" ng-mouseleave="vm.changeColorMenu('m1','false')">
</li>
<li ng-style="( (isOver == 'true') && (linkToActive == 'm2') ) ? { 'background-color': '#00bdcb' } : {'background-color': '#ededed'}"
ng-mouseenter="vm.changeColorMenu('m2','true')" ng-mouseleave="vm.changeColorMenu('m2','false')">
</li>
</ul>
Javascript Code
function changeColorMenu(indexMenu,bool)
{
$scope.isOver = bool;
$scope.linkToActive = indexMenu;
}
If you check an example here you will see that ng-style directive waits for css style, not just value, so in your case it'll be:
$scope.person.colourStyle={'background-color':$scope.persons.color}
and in html it'll be:
<li class="bigBox no_s" ng-style="i.colourStyle" ng-repeat="i in persons" ng-hover="changeColor()">< href="#/{{i.person_id}}">{{i.person_name}}</a></li>
edit:
And You also need to set colour value to full hex for example: '#cc0000'.
In Angular, there is not ng-hover directive, so you should use ng-mouseenter & ng-mouseleave to simulate it.
<ul id="personList">
<li class="bigBox no_s" ng-style="personColour"
ng-repeat="i in persons" ng-mouseenter="changeColor($index)"
ng-mouseleave="recoverColor($index)">
{{i.person_name}}
</li>
</ul>
And you should use $index to get your element in persons Array
$scope.changeColor = function() {
$scope.personColour = { 'color': '#' + $scope.persons[$index].color };
// or 'background-color' whatever you what
}
$scope.recoverColor = function() {
$scope.personColour = {};
}
See Plunker Demo Here
Use ng-style to conditionally apply CSS styles - I've chosen to name this style 'personStyle'. Next, bind the ng-mouseover event to set the personStyle background-color to the person's colour attribute. Finally, bind the ng-mouseleave event to reset the personStyle when the mouse leaves the element. The changeColor() function is not needed for this solution to work.
<div id="personContainer" ng-controller="personController">
<ul id="personList">
<li class="bigBox no_s" ng-repeat="i in persons" ng-style="personStyle">
<a href="#/{{i.person_id}}" ng-mouseleave="personStyle={}"
ng-mouseover="personStyle={ 'background-color':'#' + i.colour}">
{{i.person_name}}</a>
</li>
</ul>
</div>

AngularJS list items alphanumerically

I've been working on sorting a list of items alphanumerically. I have found that using ng-repeat with orderBy tends to sort either numerically or alphabetically depending on the type of data it is working with. It does not however sort alphanumerically.
JavaScript Code:
function AppCtrl($scope) {
$scope.items = [
{'name':'School Item 1','address':'1920'},
{'name':'Work Item 2','address':'4192'},
{'name':'Ad Item 5','address':'2039'},
{'name':'Cool Item 45','address':'2090'},
{'name':'Cool Item 50','address':'1029'},
{'name':'Cool Item 100','address':'1829'},
{'name':'Cool Item 400','address':'1728'}
];
}
HTML Code:
<ul ng-controller="AppCtrl">
<li ng-repeat="item in items|orderBy:['name','address']">
{{item.name}} {{item.address}}
</li>
</ul>
Here is the fiddle http://jsfiddle.net/roverton/PuYLS/1/
Notice that when ordered the it will show 1 then 100, 45 then 400, 5 then 50 etc. I would like to order these alphanumerically. How would I do that in AngularJS?
One way to do this is to use a function to extract the number.
function nameNumberSorter(item) {
var numberPart = item.name.replace('NamedItem', '');
return parseInt(numberPart);
}
And then alter your filter a bit:
<div ng-app>
<ul ng-controller="AppCtrl">
<li ng-repeat="item in items|filter:deviceBindingFilter|orderBy:nameNumberSorter">
{{item.name}} - {{item.address}}
</li>
</ul>
</div>
Alternatively, you could make an extra field on your model for sorting.
function AppCtrl($scope) {
$scope.items = [
{'name':'NamedItem1','address':'1920', 'sortOrder': 1 },
{'name':'NamedItem2','address':'4192', 'sortOrder': 2 },
{'name':'NamedItem5','address':'2039', 'sortOrder': 5 },
{'name':'NamedItem45','address':'2090', 'sortOrder': 45 },
{'name':'NamedItem50','address':'1029', 'sortOrder': 50 },
{'name':'NamedItem100','address':'1829', 'sortOrder': 100 },
{'name':'NamedItem400','address':'1728', 'sortOrder': 400 }
];
}
And change your sort to look at this field.
<div ng-app>
<ul ng-controller="AppCtrl">
<li ng-repeat="item in items|filter:deviceBindingFilter|orderBy:'sortOrder'">
{{item.name}} - {{item.address}}
</li>
</ul>
</div>

Calling javascript functions in JQuery Template

Say I have the following javascript object hierarchy:
ITEMS is an array with one element in it, FILTER is an array with 3 items in it
description "churches with some restrictions"
ITEMS {...}
[0] {...}
FILTER {...}
[0] {...}
fieldName "CATEGORY"
fieldValue "society%20news,us%20news"
schemaName "all"
[1] {...}
[2] {...}
fieldName "EDUCEDPEOPLE"
fieldValue "barack%20obama"
schemaName "all"
maxResults "10"
name "Save3"
queryText "churches"
schemaName "all"
shareOwner "myuser"
I have the following JQuery template
<script id="TestTemplate" type="text/x-jquery-tmpl">
<div>
<h1>Query</h1>
<ul>
<li>Name <span>${saveName}</span></li>
<li>Text <span>${queryText}</span></li>
<li>Owner <span>${shareOwner}</span></li>
<ol>
{{each ITEMS[0].FILTER}}
<li>
<ul>
<li>Field Name ${$value.fieldName}</li>
<li>Field Value ${$value.fieldValue}</li>
</ul>
</li>
{{/each}}
</ol>
</ul>
</div>
</script>
The template is built from JSON and parsed into the above hierarchy (trimmed down for brevity)
<script type="text/javascript">
var oJSON = JSON.parse(data);
if (oJSON !=null)
{
var alQueries = oJSON.QUERIES.QUERY;
if (alQueries !=null)
{
$('#TestTemplate').tmpl(alQueries).appendTo('#test');
}
}
</script>
<div id="test"></div>
The question is, how do you call a javascript function on the items inside of FILTER? I can't figure out the syntax.
Ah, figured out the syntax :
<li>Field Value ${unescape($value.fieldValue)}</li>
Simple actually :)

Categories

Resources