I want to know the number of visible elements shown after being subject to an ng-if and ng-show. Here is a simplified example:
$scope.fruits = [
{ name: 'apple', shape: 'round', color: 'red' },
{ name: 'stop sign', shape: 'pointy', color: 'red' },
{ name: 'orange', shape: 'round', color: 'orange' },
{ name: 'tomato', shape: 'round', color: 'red'}
];
<ul>
<li ng-repeat="fruit in fruits" ng-if="fruit.color=='red'" ng-show="fruit.shape=='round'">{{fruit.name}}</li>
</ul>
Is there a way to count the resulting number of items being shown?
I ended up not using the ng-if or ng-show at all, and just filtered the ng-repeat. This way, I did not have to hide anything and was easily able to get the length of the results.
<ul>
<li ng-repeat="fruit in fruits | filter:myFilter | filter:{shape:'round'} as filteredFruits">{{fruit.name}}</li>
{{filteredFruits.length}} fruits
</ul>
$scope.myFilter = function(x) {
if (x.color == 'red') {
return x;
}
}
With this you can remove your custom filter and use as keyword to get the li length.
var app = angular.module('myApp', []);
app.controller('Ctrl', function($scope) {
$scope.fruits = [
{ name: 'apple', shape: 'round', color: 'red' },
{ name: 'stop sign', shape: 'pointy', color: 'red' },
{ name: 'orange', shape: 'round', color: 'orange' },
{ name: 'tomato', shape: 'round', color: 'red'}
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="Ctrl">
<ul>
<li ng-repeat="fruit in fruits| filter:{'shape':'round', 'color':'red'}" >{{fruit.name}}</li>
</ul>
</div>
try this
<ul>
<li ng-repeat="fruit in fruits | notEmpty as Items" ng-if="fruit.color=='red'" ng-show="fruit.shape=='round'">{{fruit.name}}</li>
</ul>
<p>Number of Items: {{Items.length}}</p>
You can count number of items,
$scope.fruits = [
{ name: 'apple', shape: 'round', color: 'red' },
{ name: 'stop sign', shape: 'pointy', color: 'red' },
{ name: 'orange', shape: 'round', color: 'orange' },
{ name: 'tomato', shape: 'round', color: 'red' }
];
$scope.FindVisible = function () {
var visibleObject=0;
$scope.fruits.forEach(function (v, k) {
if (v.shape == 'round' && v.color == 'red') {
visibleObject = visibleObject + 1;
}
});
alert(visibleObject);
}
<ul>
<li ng-repeat="fruit in fruits" ng-if="fruit.color=='red'" ng-show="fruit.shape=='round'">{{fruit.name}}</li>
<button ng-click="FindVisible()">Find</button>
</ul>
Related
I have an array of objects:
[{
title: 'foo'
id: 1,
name: 'anne'
},
{
title: 'example',
id: 2,
name: 'anne'
},
{
title: 'ex',
id: 3,
name: 'deb'
}]
The page is already showing the first object:
{
title: 'foo'
id: 1,
name: 'anne'
},
<div class="essay">
<p class="title" >{{ selectedItem.title }}</p>
<p class="id">{{ selectedItem.id }}</p>
<p class="name"> {{ selectedItem.name }} </p>
</div>
selectedItem comes from the created() life cycle
What is the best way to display the object with the same name?
I want to have a button which if clicked, will show another title from the person name 'anne'.
Try to get the filtered array.
created() {
this.selectedItem = arr.filter(item => item.name === 'anne');
// arr is the array source variable.
}
<div v-for="item in selectedItem" :key="item.id" class="essay">
<p class="title" >{{ selectedItem.title }}</p>
<p class="id">{{ selectedItem.id }}</p>
<p class="name"> {{ selectedItem.name }} </p>
</div>
I've made a snippet, maybe it would help You :-)
We need to store state somewhere, so I've created owners variable with shown counter.
There is also a computed property that groups items by owners.
Maybe there is an easier method but...
new Vue({
el: '#app',
data: {
items: [
{ owner: "John", name: "Box" },
{ owner: "John", name: "Keyboard" },
{ owner: "John", name: "Plate" },
{ owner: "Ann", name: "Flower" },
{ owner: "Ann", name: "Cup" }
],
owners: {},
},
methods: {
more(owner) {
if (this.owners[owner].shown < this.items.filter(i => i.owner == owner).length) {
this.owners[owner].shown++;
}
}
},
computed: {
ownersItems() {
let map = {};
this.items.forEach(i => {
map[i.owner] = map[i.owner] || [];
if (this.owners[i.owner].shown > map[i.owner].length) {
map[i.owner].push(i);
}
});
return map;
},
},
created() {
let owners = {};
this.items.forEach(i => {
owners[i.owner] = owners[i.owner] || {};
owners[i.owner].shown = 1;
});
this.owners = owners;
}
})
#app {
font-family: sans-serif;
padding: 1rem;
}
.header {
font-weight: bold;
margin-bottom: .5rem;
}
sup {
background: #000;
color: #fff;
border-radius: .15rem;
padding: 1px 3px;
font-size: 75%;
}
.btn {
padding: .25rem .5rem;
border: 1px solid #ddd;
background: #eee;
border-radius: .15rem;
margin: 2px;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="header">Owners</div>
<template v-for="(cfg, owner) in owners">
<strong>{{ owner }} <sup>{{cfg.shown}}</sup>:</strong>
<a v-for="(item, i) in ownersItems[owner]" :key="`${owner}_${i}`" #click="more(owner)" class="btn">{{ item.name }}</a>
|
</template>
</div>
Is it possible to add/change the computed property inside the "v-for" with a click event?
I would like to be able to display the list of food based on their category when I click on the proper button.
If I click on the "fruit" button it will be
<div v-for="food in fruitCategory" :key="food.name" class="card">
<p>{{ food.name }}</p>
</div>
and "candy" button it would be
<div v-for="food in candyCategory" :key="food.name" class="card">
<p>{{ food.name }}</p>
</div>
Is this a good approach?
Actual code below
<template>
<div class="layout">
<button>all</button>
<button>fruit</button>
<button>candy</button>
<div v-for="food in foods" :key="food.name" class="card">
<p>{{ food.name }}</p>
</div>
</div>
</template>
export default {
name: 'Food',
data() {
return {
foods: [
{ name: 'banana', category: 'fruit' },
{ name: 'orange', category: 'fruit' },
{ name: 'strawberry', category: 'fruit' },
{ name: 'gum', category: 'candy' },
{ name: 'fuzzy peach', category: 'candy' },
]
}
},
computed: {
fruitCategory: function() {
return this.foods.filter(function(food) {
return food.category === 'fruit'
})
}
},
candyCategory: function() {
return this.foods.filter(function(food) {
return food.category === 'candy'
})
}
}
You just need to store the category in data and then you can use the filter dynamically.
here as a working example with a cat array that checks if the category is included.
new Vue({
el: "#app",
name: 'Food',
data() {
return {
cat: ['fruit','candy'],
foods: [
{ name: 'banana', category: 'fruit' },
{ name: 'orange', category: 'fruit' },
{ name: 'strawberry', category: 'fruit' },
{ name: 'gum', category: 'candy' },
{ name: 'fuzzy peach', category: 'candy' },
]
}
},
computed: {
filtered: function() {
var cat = this.cat;
return this.foods.filter(function(food) {
return cat.indexOf(food.category) >= 0;
})
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<div class="layout">
<button #click="cat=['fruit', 'candy']">all</button>
<button #click="cat=['fruit']">fruit</button>
<button #click="cat=['candy']">candy</button>
<div v-for="food in filtered" :key="food.name" class="card">
<p>{{ food.name }}</p>
</div>
</div>
</div>
I have implemented select control with select all option, When I open the control, it focuses on the first option. I would like to focus on the select option or at least disable the focus.
HTML
<div ng-app="selectDemoOptGroups" ng-controller="SelectOptGroupController" >
<md-select md-selected-text="selectedText" ng-model="selectedToppings" multiple>
<div class="select-all-div" >
<md-checkbox class="select-selectAll"
>Select all</md-checkbox > </div>
<md-option ng-value="topping.name" ng-repeat="topping in toppings">{{topping.name}}</md-option>
</md-select>
JS
angular
.module('selectDemoOptGroups', ['ngMaterial'])
.controller('SelectOptGroupController', function($scope) {
$scope.toppings = [
{ category: 'meat', name: 'Pepperoni' },
{ category: 'meat', name: 'Sausage' },
{ category: 'meat', name: 'Ground Beef' },
{ category: 'meat', name: 'Bacon' },
{ category: 'veg', name: 'Mushrooms' },
{ category: 'veg', name: 'Onion' },
{ category: 'veg', name: 'Green Pepper' },
{ category: 'veg', name: 'Green Olives' }
];
$scope.selectedToppings = [];
});
https://jsfiddle.net/AlexLavriv/ya6eu8kz/5/
You can't use a div for single opotion and options for the rest. You can use the same option to display the option select all.
<md-select>
<md-option>Select all</md-option>
<md-option ng-value="topping.name" ng-repeat="topping in toppings">{{topping.name}}</md-option>
</md-select>
Working Demo: JSFiDDLE
Working Demo
Try this below way
angular
.module('selectDemoOptGroups', ['ngMaterial'])
.controller('SelectOptGroupController', function($scope) {
$scope.toppings = [
{ category: 'meat', name: 'Pepperoni' },
{ category: 'meat', name: 'Sausage' },
{ category: 'meat', name: 'Ground Beef' },
{ category: 'meat', name: 'Bacon' },
{ category: 'veg', name: 'Mushrooms' },
{ category: 'veg', name: 'Onion' },
{ category: 'veg', name: 'Green Pepper' },
{ category: 'veg', name: 'Green Olives' }
];
$scope.toppings.unshift( { category: 'select', name: 'Select all' })
$scope.selectedText = $scope.toppings[0].name;
});
.select-selectAll{
text-align: left;
padding-left: 10px;
/* padding-right: 16px; */
display: flex;
cursor: pointer;
position: relative !important;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
width: 95%;
-webkit-transition: background .15s linear;
transition: background .15s linear;
/* padding: 0 16px; */
height: 48px;
}
.select-selectAll div.md-icon{
left:10px;
}
.select-all-div:hover{
background: rgb(238,238,238);
}
<div ng-app="selectDemoOptGroups" ng-controller="SelectOptGroupController" >
<md-select md-selected-text="selectedText" ng-model="selectedToppings" multiple>
<md-option ng-value="topping.name" ng-repeat="topping in toppings">{{topping.name}}</md-option>
</md-select>
</div>
I would like to filter the results by multiple properties.
My code:
(function () {
'use strict';
angular.
module('myApp', []).
filter('byFeatures', byFeatures).
controller('MyCtrl', MyCtrl);
function byFeatures() {
return function (items, conditions) {
if (Object.getOwnPropertyNames(conditions).length === 0) {
return items;
}
var filtered = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
angular.forEach(item.features, function (feature) {
if (conditions[feature.name] !== undefined && conditions[feature.name][feature.value] !== undefined) {
if (conditions[feature.name][feature.value]) {
//filtered.push(item);
filtered[item.id] = item;
}
}
});
}
//console.log(filtered);
return filtered;
};
};
function MyCtrl($scope, $filter) {
$scope.allProducts = [{
id: 0,
name: "Product A",
features: [{
name: "Brand",
value: "Apple"
},
{
name: "Memory",
value: "16"
},
{
name: "Color",
value: "Black"
}
]
},
{
id: 1,
name: "Product B",
features: [{
name: "Brand",
value: "Apple"
},
{
name: "Memory",
value: "32"
},
{
name: "Color",
value: "Black"
}
]
},
{
id: 2,
name: "Product C",
features: [{
name: "Brand",
value: "Nokia"
},
{
name: "Memory",
value: "16"
},
{
name: "Color",
value: "Black"
}
]
},
{
id: 3,
name: "Product A",
features: [{
name: "Brand",
value: "Samsung"
},
{
name: "Memory",
value: "16"
},
{
name: "Color",
value: "Black"
}
]
},
];
$scope.filters = [{
name: "Brand",
values: [
"Apple",
"Nokia",
"Samsung",
]
},
{
name: "Memory",
values: [
"16",
"32",
]
},
];
$scope.currentFilter = {};
$scope.$watch('currentFilter', function (newValue, oldValue, scope) {
//console.log(newValue);
$scope.products = $filter('byFeatures')($scope.allProducts, newValue);
}, true);
};
}());
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="robots" content="noindex, nofollow">
<meta name="googlebot" content="noindex, nofollow">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<title></title>
</head>
<body ng-app="myApp" class="ng-scope">
<div ng-controller="MyCtrl" style="width: 100%;">
<div style="width: 50%; float: left;">
<h2>Available Phones</h2>
<div ng-repeat="product in products">
<h3>
{{ product.name }}
</h3>
<ul>
<li ng-repeat="feature in product.features">
{{feature.name}} : {{feature.value}}
</li>
</ul>
</div>
</div>
<div style="width: 50%; float: left;">
<h2>Filters</h2>
<pre>{{currentFilter|json}}</pre>
<div ng-repeat="filter in filters">
<span>{{filter.name}}</span>
<ul>
<li ng-repeat="value in filter.values">
<input type="checkbox" ng-model="currentFilter[filter.name][value]"> {{value}}<br>
</li>
</ul>
</div>
</div>
</div>
</body></html>
If i filter by brand "Apple" are displayed 2 phones with brand "apple" (16 and 32 memory) - its ok. But if i add second filter by memory "16" - will must display only 1 phone apple with memory "16".
How to do it? . Link to JSFiddle
The problem is that you are doing the opposite from what you want: You are checking that the product contains a valid property for one of the items (Brand or Memory). What we need to do is check if the product has a valid property for all the items (Brand and Memory).
It would be enough to change the loop inside the function byFeatures in order to find every relevant condition (When its value is true!) and make sure that our product has one of the selected properties for each, Brand and Memory. Here you can see the right snippet:
(function () {
'use strict';
angular.
module('myApp', []).
filter('byFeatures', byFeatures).
controller('MyCtrl', MyCtrl);
function byFeatures() {
return function (items, conditions) {
if (Object.getOwnPropertyNames(conditions).length === 0) {
return items;
}
var filtered = [];
for (var i = 0; i < items.length; i++) {
var item = items[i],
hasProperties = true;
angular.forEach(conditions, function (value, key) {
for (var i = 0; i < item.features.length; i++) {
if (item.features[i].name === key && (!value.hasOwnProperty(item.features[i].value) || !value[item.features[i].value])) {
for (var j in value) {
if (value[j]) {
hasProperties = false;
}
}
}
}
});
if (hasProperties) {
filtered.push(item);
}
}
//console.log(filtered);
return filtered;
};
};
function MyCtrl($scope, $filter) {
$scope.allProducts = [{
id: 0,
name: "Product A",
features: [{
name: "Brand",
value: "Apple"
},
{
name: "Memory",
value: "16"
},
{
name: "Color",
value: "Black"
}
]
},
{
id: 1,
name: "Product B",
features: [{
name: "Brand",
value: "Apple"
},
{
name: "Memory",
value: "32"
},
{
name: "Color",
value: "Black"
}
]
},
{
id: 2,
name: "Product C",
features: [{
name: "Brand",
value: "Nokia"
},
{
name: "Memory",
value: "16"
},
{
name: "Color",
value: "Black"
}
]
},
{
id: 3,
name: "Product A",
features: [{
name: "Brand",
value: "Samsung"
},
{
name: "Memory",
value: "16"
},
{
name: "Color",
value: "Black"
}
]
},
];
$scope.filters = [{
name: "Brand",
values: [
"Apple",
"Nokia",
"Samsung",
]
},
{
name: "Memory",
values: [
"16",
"32",
]
},
];
$scope.currentFilter = {}; //$scope.filters;
$scope.$watch('currentFilter', function (newValue, oldValue, scope) {
$scope.products = $filter('byFeatures')($scope.allProducts, newValue);
}, true);
};
}());
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="robots" content="noindex, nofollow">
<meta name="googlebot" content="noindex, nofollow">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="js/script.js"></script>
<title></title>
</head>
<body ng-app="myApp" class="ng-scope">
<div ng-controller="MyCtrl" style="width: 100%;">
<div style="width: 50%; float: left;">
<h2>Available Phones</h2>
<div ng-repeat="product in products">
<h3>
{{ product.name }}
</h3>
<ul>
<li ng-repeat="feature in product.features">
{{feature.name}} : {{feature.value}}
</li>
</ul>
</div>
</div>
<div style="width: 50%; float: left;">
<h2>Filters</h2>
<pre>{{currentFilter|json}}</pre>
<div ng-repeat="filter in filters">
<span>{{filter.name}}</span>
<ul>
<li ng-repeat="value in filter.values">
<input type="checkbox" ng-model="currentFilter[filter.name][value]"> {{value}}<br>
</li>
</ul>
</div>
</div>
</div>
</body>
</html>
I also changed the way you were adding the new product to the filtered array. Otherwise, if the ID of the only item is 2, the array would keep the first 2 positions as 'undefined' and this would cause troubles through the ngRepeat iterations
Simple problem, probably discussed many times, but I can't find a proper solution on this simple issue.
The problem:
Why are the Daltons selectable, but the selected Daltons don't show up in the select-element?
Controller:
var myApp = angular.module('myApp', ['ui.select2']);
function MyCtrl($scope) {
$scope.daltons = [
{ id: 10, name: 'Joe' },
{ id: 20, name: 'William' },
{ id: 30, name: 'Jack' },
{ id: 40, name: 'Averell' },
{ id: 50, name: 'Ma' }
];
$scope.selectedDaltons = [40]; // Averell is preselected
};
View:
<div ng-controller="MyCtrl">
<label for="1">Please select items:</label>
<select id="1"
ui-select2
multiple
ng-model='selectedDaltons'
ng-options="dalton.id as dalton.name for dalton in daltons">
</select>
<label for="2">Selected Items:</label>
<ul id="2">
<li ng-repeat="dalton in selectedDaltons">{{dalton}}</li>
</ul>
</div>
Here it is as a jsfiddle
I think the issue is that ng-options is not supported in ui-select2. I reworked your fiddle using the option tag with an ng-repeat:
http://jsfiddle.net/u48j0yyc/1/
<div ng-controller="MyCtrl">
<label>Please select items:</label>
<select ui-select2 multiple ng-model='selectedDaltons'>
<option ng-repeat="d in daltons" ng-bind="d.name" value="{{ d.id }}"></option>
</select>
<label>Selected Items:</label>
<ul>
<div ng-bind="selectedDaltons"></div>
</ul>
</div>
var myApp = angular.module('myApp', ['ui.select2']);
function MyCtrl($scope) {
$scope.daltons = [
{ id: 10, name: 'Joe' },
{ id: 20, name: 'William' },
{ id: 30, name: 'Jack' },
{ id: 40, name: 'Averell' },
{ id: 50, name: 'Ma' }
];
$scope.selectedDaltons = [40]; // Averell is preselected
};