I have a json:
{"sectionTitle":"Account Information","sectionItems":[{"itemTitle":"Balance","url":"/account/balance","selected":true},{"itemTitle":"Account Statement","url":"/account/statementsearch","selected":false},{"itemTitle":"Deposit","url":"/account/deposit","selected":false},{"itemTitle":"Withdrawal","url":"/account/withdraw","selected":false},{"itemTitle":"Edit Profile","url":"/account/editprofile","selected":false},{"itemTitle":"Change Password","url":"/account/changepassword","selected":false}]}
Now I just want to check if there is an item (child) inside sectionTitle where selected is true.
Something like this in SQL
SELECT * FROM sectionItems WHERE selected=true
Can I do something similar in angular js, so I can check if the the parents has children?
I hope you understood my question.
This is my html
<nav class="sidebar-nav">
<ul class="nav metismenu" id="side-menu-help">
<li ng-repeat="menuItem in accountCtrl.menuStructure">
<a class="{{ (menuItem.sectionItems.length > 0) ? 'metisHasChildren' : '' }}" href="/en/help-area/poker-help/poker-rules/">
<span ng-if="menuItem.sectionItems.length > 0" class="fa arrow fa fa-angle-double-down"></span>
{{ ::menuItem.sectionTitle }}
{{ ::menuItem }}
</a>
<ul class="nav nav-second-level collapse in">
<li ng-repeat="subMenuItem in menuItem.sectionItems" ng-click="accountCtrl.changePage(subMenuItem.url)">
<a ng-class="(subMenuItem.selected) ? 'page-active' : ''">{{ ::subMenuItem.itemTitle }}</a>
</li>
</ul>
</li>
</ul>
</nav>
You do not need to anything fancy to get this working. Simply convert the json to an object and access the property you want using dot notation. So for example:
var json = JSON.parse(json);
var selectedItems = [];
angular.forEach(json.sectionItems, function(sectionItem) {
if (sectionItem.selected) {
selectedItems.push(sectionItem);
}
});
Would convert the json string to an object and then loop over each sectionItem child, check for selected is true and create an array of matching items.
You can use a forEach loop. This example will return an array of all sectionItems where selected equals true, but you can return whatever you'd like.
$scope.items =[];
angular.forEach(sectionItems, function(item){
if (item.selected === true){
$scope.items.push(item);
}
})
UPDATE
Here's a working plunk
To make this work inline with ng-repeat you will use it in a filter, like this:
app.filter('menuFilter', function() {
return function(menuItems) {
var filtered = [];
angular.forEach(menuItems, function(menuItem) {
angular.forEach(menuItem.sectionItems, function(item) {
if (item.selected === true) {
filtered.push(menuItem);
}
});
});
return filtered;
}
});
And change your markup, like this:
ng-repeat="menuItem in accountCtrl.menuStructure | menuFilter "
First, you don't need to do any custom filter or anything like that, just use the standard filter, as below:
<li ng-repeat="menuItem in accountCtrl.menuStructure | filter: { sectionItems: { selected: true } }"> {{ menuItem.sectionTitle }}
Working demo:
angular.module('app', [])
.controller('accountController', function() {
var vm = this;
vm.menuStructure = [
{
"sectionTitle":"Account Information",
"sectionItems":[
{
"itemTitle":"Balance",
"url":"/account/balance",
"selected":true
},
{
"itemTitle":"Account Statement",
"url":"/account/statementsearch",
"selected":false
},
{
"itemTitle":"Deposit",
"url":"/account/deposit",
"selected":false
},
{
"itemTitle":"Withdrawal",
"url":"/account/withdraw",
"selected":false
},
{
"itemTitle":"Edit Profile",
"url":"/account/editprofile",
"selected":false
},
{
"itemTitle":"Change Password",
"url":"/account/changepassword",
"selected":false
}
]
},
{
"sectionTitle":"Account Information 2",
"sectionItems":[
{
"itemTitle":"Balance",
"url":"/account/balance",
"selected":false
},
{
"itemTitle":"Account Statement",
"url":"/account/statementsearch",
"selected":false
},
{
"itemTitle":"Deposit",
"url":"/account/deposit",
"selected":false
},
{
"itemTitle":"Withdrawal",
"url":"/account/withdraw",
"selected":false
},
{
"itemTitle":"Edit Profile",
"url":"/account/editprofile",
"selected":false
},
{
"itemTitle":"Change Password",
"url":"/account/changepassword",
"selected":false
}
]
}
];
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
</head>
<body ng-controller="accountController as accountCtrl">
<ul>
<li ng-repeat="menuItem in accountCtrl.menuStructure | filter: { sectionItems: { selected: true } }"> {{ menuItem.sectionTitle }}
<ul class="nav nav-second-level collapse in">
<li ng-repeat="subMenuItem in menuItem.sectionItems" ng-click="accountCtrl.changePage(subMenuItem.url)">
<a ng-class="{ 'page-active': subMenuItem.selected }" ng-bind="::subMenuItem.itemTitle"></a>
</li>
</ul>
</li>
</ul>
</body>
</html>
Tips:
Instead of using ngClass with ternary operator, you can simply use this way:
ng-class="{ 'page-active': subMenuItem.selected }"
Even if works in this way that you're using, I'd recommend you to take a look on the special-repeats, it fits really well in your situation.
I hope it helps!!
var JSONStr=[{"sectionTitle":"Account Information","sectionItems":[{"itemTitle":"Balance","url":"/account/balance","selected":true},{"itemTitle":"Account Statement","url":"/account/statementsearch","selected":false},{"itemTitle":"Deposit","url":"/account/deposit","selected":false},{"itemTitle":"Withdrawal","url":"/account/withdraw","selected":false},{"itemTitle":"Edit Profile","url":"/account/editprofile","selected":false},{"itemTitle":"Change Password","url":"/account/changepassword","selected":false}]}];
var result = JSONStr.where({ selected: true });
Related
I have a code that filters through a field and marks the words that relate to the input search. I need this filtered me not only by the title, but also by the name. How can I modify the code so that when I write, I filter both title and name?
<li ng-repeat="item in data | filter:search.title"
ng-bind-html="item.title | highlight:search.title">
</li>
http://jsfiddle.net/sgo3wxwc/
you can send two or more options to filter by gathering them in object:
<li ng-repeat="item in data | filter:{title:search.title,name:search.name}"
ng-bind-html="item.title | highlight:search.title">
</li>
it's recommended to use filters in controller to avoid $digest cycle on them
<div ng-app="myApp" ng-controller="myCtrl">
<input type="text" placeholder="Search" ng-model="search.title">
<ul>
<!-- with filter -->
<li ng-repeat="item in data | filter:{title:search.title,name:search.name}"
ng-bind-html="item.title | highlight:search.title">
{{item}}
</li>
</ul>
</div>
script code is here:
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.data = [
{ title: "Bad",name:'bill' },
{ title: "Good",name:'Goe' },
{ title: "Great",name:'Brad' },
{ title: "Cool",name:'yan' },
{ title: "Excellent",name:'mikle' },
{ title: "Awesome",name:'mosa' },
{ title: "Horrible",name:'morteza' },
]
}).filter('highlight', function($sce) {
return function(text, phrase) {
if (phrase) text = text.replace(new RegExp('('+phrase+')', 'gi'),
'<span class="highlighted">$1</span>')
return $sce.trustAsHtml(text)
}
})
I'm trying to ignore a property called title in my angular filter. I have a dataset like the below example:
const data = [
{
title: 'Title 1'
groups: [
{...},
{...},
{...}
]
},
{
title: 'Title 2'
groups: [
{...},
{...},
{...}
]
},
{
title: 'Title 3'
groups: [
{...},
{...},
{...}
]
}
];
And i'm using the ng-repeat with filter to iterate over the objects, and other loop to iterate over the groups:
<input ng-model="search">
<div ng-repeat="item in data | filter:search">
<h1>{{item.title}}</h1>
<ul>
<li ng-repeat="group in item.group | filter:search">
<span>{{group.something}}</span>
</li>
</ul>
</div>
Is working fine, but now i would like to ignore the title in the search. I did try several things, like: filter:search:item.title (in the first ng-repeat), or remove the first filter:search, but all tries failed. What i'm missing? Do i need a custom search or something like that?
Thank you.
You can specifically enter properties you want to filter and leave out title:
<li ng-repeat="group in item.groups | filter: { something: search }">
The above code will only filter based on the something property.
More answers and explanations here: AngularJS filter only on certain objects
If you type and no filtering the title property, just remove the first filter. This way when you type the li's isnt match will hide, but their h1's will stay the same place.
You should create custom filter, where you can specify which property should be excluded(ignore parameter) from consideration:
angular.module('app', []).controller('MyController', ['$scope', function($scope) {
$scope.data = [
{name:"Tom", title:'London'},
{name:"Max", title:'Moscow'},
{name:"Henry", title:'NY'},
{name:"Paul", title:'NY'},
{name:"Sam", title:'Paris'}
];
}]).filter('myfilter',function(){
return function(input, search, ignore){
if(!search)
return input;
var result = [];
for(var item of input)
for(var prop in item)
if(prop != ignore && item[prop].indexOf(search) != -1)
{
result.push(item) ;
break;
}
return result;
}
});
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="MyController">
search: <input type='text' ng-model='search' ng-init='search="a"'/>
ignore: <input type='text' ng-model='ignore' ng-init='ignore="title"'/>
<ul>
<li ng-repeat='item in data | myfilter: search: ignore'>
{{item.name}} {{item.title}}
</li>
</ul>
</div>
</body>
I have list of links:
<ul id="menu">
<li v-for="item in items">
<a href v-bind:href=link>{{item.message}}</a>
</li>
</ul>
var example1 = new Vue({
el: '#menu',
data: {
items: [
{ message: 'Link1' },
{ message: 'Link2' }
]
},
computed: {
link: function() {
return f($index) // how do I access current array index ??
}
}
})
I can achieve the desired result by using mustache markup in a href attribute but it must be possible to access this $index variable from within a computed function?
You can't.
This is what methods are for:
<a href v-bind:href="link($index)">{{item.message}}</a>
methods: {
link: function(index) {
return f(index)
}
}
I have a a collection of panels each with a simple list of items that needs to either be sorted by 'computedLoad' or 'Name'. I have the following objects and methods to accomplish this generically over all of the panels (only showing one panel among many).
scope.orderBy = {
name: {
displayName: "Name",
sort: "Name",
reverse: false
},
load: {
displayName: "Load",
sort: "-computedLoad",
reverse:false
}
};
scope.selectOrder = function (panel, order) {
timeout(function () {
panel.activeOrder = order;
});
};
scope.panels = {
methods: {
activeOrder: scope.orderBy.name
}
};
I have the following html:
<div>
<ul class="nav nav-pills">
<li class="list-label"><a>Order By:</a></li>
<li ng-repeat="order in orderBy">{{order.displayName}}</li>
</ul>
<ul class="nav nav-pills nav-stacked">
<li ng-repeat="item in suite.Methods | orderBy:panel.methods.activeOrder.sort"><span class="text">{{item.Name}}</span></li>
</ul>
</div>
The selectOrder method doesn't seem to work. Any ideas? Am I missing something?
Here is an example: http://jsbin.com/puxoxi/1/
Setting panel.activeOrder happens asynchronously, so it is outside of angulars so called "digest cycle".
To make angular re-evaluate your scope, use the $apply function:
It could look like this:
scope.$apply(function() {
panel.activeOrder = order;
});
I'm having an issue creating an object in the correct format when adding to it from a loop.
e.g. I loop through some lists
<ul class="pdmenu">
<ul class="pdmenu">
<li class="menu-top" id="vmen-1">1</li>
<li class="menu-item">aaa</li>
</ul>
<ul class="pdmenu">
<li class="menu-top" id="vmen-2">2</li>
<li class="menu-item">aaa</li>
<li class="menu-item">bbb</li>
</ul>
<ul class="pdmenu">
<li class="menu-top" id="vmen-3">3</li>
<li class="menu-item">aaa</li>
<li class="menu-item">bbb</li>
</ul>
I use jQuery to loop through the top list item, .menu-top, and push the id and visibility to an object.
jsonmenu = $(); // Set empty object.
$('.menu-top').each(function(index) {
jsonmenu.push({
menu: $(this).attr('id'),
visible: "" + $(this).next().is(':visible') + ""
});
});
This creates the object with member for each item like so
{
"0": {
"menu":"vmen-1",
"visible":"false"
},
"length":4,
"1": {
"menu":"vmen-2",
"visible":"false"
},
"2":{
"menu":"vmen-3",
"visible":"false"
},
"3": {
"menu":"vmen-4",
"visible":"true"
}
}
All I need is a simple format like the following;
{
"menu":"vmen-1",
"visible":"false"
},
{
"menu":"vmen-2",
"visible":"false"
},
{
"menu":"vmen-3",
"visible":"false"
},
{
"menu":"vmen-4",
"visible":"true"
}
How can I change this to get the object in this simple format?
Use a native array and not a JSON object to hold your values:
var jsonmenu = [];
$('.menu-top').each(function(index) {
jsonmenu.push({menu: $(this).attr('id'), visible: ""+$(this).next().is(':visible')});
});
Or, as you just got something like key-value pairs, you could do it like this:
var jsonmenu = {};
$('.menu-top').each(function(index) {
jsonmenu[ $(this).attr('id') ] = $(this).next().is(':visible');
});
which would result in something like this:
{
'vmen-1': false,
'vmen-2': false,
...
}
You've already had an answer, to use a native array rather than an empty jQuery object. However, for completeness, there is another convenient method in jQuery to do things like "enumerate a bunch of DOM elements and turn them into an array" .map()
You would use it like so:
var jsonmenu = $('.menu-top').map(function(i,e) {
var $this = $(e);
return {
id: $this.attr('id'),
visible: "" + $this.next().is(':visible') + ""
}
});
Live example: http://jsfiddle.net/8szrG/