How to use the value in ngRepeat as a dynamic way to access a scope? - angularjs

I have a set of value that I would like to dynamically generate in my view. How can I go about doing it in the following way?
$scope.mainKeyMapping = [
{
name: 'category1',
otherThings: ''
},
{
name: 'category2',
otherThings: ''
},
{
name: 'category3',
otherThings: ''
},
{
name: 'category4',
otherThings: ''
},
{
name: 'category5',
otherThings: ''
}
];
$scope.category1 = {something....}
$scope.category2 = {something....}
$scope.category3 = {something....}
$scope.category4 = {something....}
$scope.category5 = {something....}
HTML
<div ng-repeat="cat in mainKeyMapping">
{{category1}} // outputs obj
{{cat.name}} // outputs string "category1" <----- how do I output the obj?
</div>

First, put your categories into a collection:
$scope.categories = {
category1: {something....},
category2: {something....}
};
Now simply access the right category in your html:
<div ng-repeat="cat in mainKeyMapping">
{{categories.category1}} // outputs obj
{{categories[cat.name]}} // outputs obj
</div>

Related

can I assign multiple $watchCollection programmaticaly?

Imagine that I have an array called buckets. that array will contain objects called bucket where each bucket has a property called images which is an array. Example:
vm.buckets = [
{name: "bucket1", images: []},
{name: "bucket2", images: []},
{name: "bucket3", images: []}
]
I am dynamically adding new buckets to vm.buckets , but also dynamically adding new images to each bucket. Is there a way to create a new $watchCollection and assign it to every new bucket so that i can watch for new images being inserted into each bucket?
angular.module('app', []).controller('ctrl', function($scope) {
var vm = this;
vm.buckets = [
{ name: "bucket1", images: [] },
{ name: "bucket2", images: [] },
{ name: "bucket3", images: [] }
];
var counter = vm.buckets.length;
vm.Addbucket = function() {
var temp = {
name: 'bucket' + (++counter),
images: []
};
vm.buckets.push(temp);
AddWatch(temp);
}
vm.DeleteLastBucket = function(){
vm.buckets[vm.buckets.length - 1].unregisterWatch();
vm.buckets.splice(vm.buckets.length - 1, 1);
}
function AddWatch(x) {
var index = vm.buckets.indexOf(x);
var id = `buckets[${index}].images`;
x.unregisterWatch = $scope.$watchCollection(id, function() {
console.log(`Changes at ${x.name} images`);
});
}
$scope.buckets = vm.buckets;
vm.buckets.forEach(AddWatch);
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js">
</script>
<div ng-app='app' ng-controller='ctrl as vm'>
<ul>
<li ng-repeat='item in vm.buckets'>
{{item.name}}: {{item.images.length}} images
<button ng-click='item.images.push("temp")'>Add image</button>
</li>
</ul>
<button ng-click='vm.Addbucket()'>Add bucket</button>
<button ng-click='vm.DeleteLastBucket()'>Delete last bucket</button>
</div>

Vue.js filtering on array

I am trying to filter an array using a computed property in vue.js. I would like to search on on multiple fields, name, state, tags etc.
My data:
events: [
{
id: 1,
name: 'Name of event',
url: '#',
datetime: '2017-05-10T00:00:00Z',
description: 'The full text of the event',
state: 'VIC',
tags: [
'ordinary',
'advanced'
]
},
{
id: 2,
name: 'Another event',
url: '#',
datetime: '2017-05-12T00:00:00Z',
description: 'The full text of the event',
state: 'VIC',
tags: [
'beginner'
]
},
{
id: 3,
name: 'Great event',
url: '#',
datetime: '2017-05-18T00:00:00Z',
description: 'The full text of the event',
state: 'NSW',
tags: [
'beginner'
]
}
]
},
The following function works as expected, however I cant work out how to have it search the items in 'tags' (commented out).
searchevents: function(){
let result = this.events
if (this.filterValue){
result = result.filter(event =>
event.name.toLowerCase().includes(this.filterValue.toLowerCase()) ||
event.state.toLowerCase().includes(this.filterValue.toLowerCase())
// event.tags.toLowerCase().values().includes(this.filterValue.toLowerCase())
)
}
return result
}
The following returns a blank array, this method works ok when i have done it in angular but not in vue.
searchevents2: function(){
var searchRegex = new RegExp(this.filterValue,'i')
this.events.filter(function(event){
return !self.filterValue || searchRegex.test(event.name) || searchRegex.test(event.state)
})
}
Ideally I would either like to be able to list array items to filter by or just filter by the entire array.
Appreciate any help, first post here so be gentle. I have a lot more experience with Python than Javascript so i may also use incorrect terminology at times.
You weren't too far off.
For your searchEvents filter, you just needed to add the tag filter. Here's how you might do that.
searchevents: function(){
let result = this.events
if (!this.filterValue)
return result
const filterValue = this.filterValue.toLowerCase()
const filter = event =>
event.name.toLowerCase().includes(filterValue) ||
event.state.toLowerCase().includes(filterValue) ||
event.tags.some(tag => tag.toLowerCase().includes(filterValue))
return result.filter(filter)
}
Array.some() is a standard array method that returns true if any element of the array passes your test.
searchevents2: function(){
const searchRegex = new RegExp(this.filterValue,'i')
return this.events.filter(event =>
!this.filterValue || searchRegex.test(event.name) || searchRegex.test(event.state))
}
With searchEvents2 you really only left an errant self in there. Either you needed to set self before you executed the filter, or, as I have done here, turned it into an arrow function.
Example.
const app = new Vue ({
el: '#app',
data: {
search: '',
userList: [
{
id: 1,
name: "Prem"
},
{
id: 1,
name: "Chandu"
},
{
id: 1,
name: "Shravya"
}
]
},
computed: {
filteredAndSorted(){
// function to compare names
function compare(a, b) {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
}
return this.userList.filter(user => {
return user.name.toLowerCase().includes(this.search.toLowerCase())
}).sort(compare)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div class="search-wrapper">
<input type="text" v-model="search" placeholder="Search title.."/>
<label>Search Users:</label>
</div>
<ul>
<li v-for="user in filteredAndSorted">{{user.name}}</li>
</ul>
</div>

Comparing objects from two scopes to provide a value

I'll try to simplify the problem as much as I can.
Let's say I have 2 scopes
$scope.section1 = [
{label: 'label1'},
{label: 'label2'}
];
$scope.section2 = [
{value: 'one'},
{value: 'two}
];
Those scopes are used to generate buttons with ng-repeat
<button ng-repeat="item in section1 type="button">{{item.label}}</button>
and
<button ng-repeat="item in section2 type="button">{{item.value}}</button>
Now what I would like to do it to create a third scope that would attach values to the combinations of objects from the two previous ones, say:
$scope.combo = [
{ section1.label:label1 + section2.value: one = 'result1' },
{ section1.label:label2 + section2.value: one = 'result2' },
{ section1.label:label1 + section2.value: two = 'result3' },
{ section1.label:label2 + section2.value: two = 'result4' }
];
Now here comes the tricky part. What I would need to do, is to add a function that would take the values of clicked ng-repeat buttons from each section and then display the results based on the third scope in an input field or something.
So, if you click the button with label:label1 and the one with value:two the input field would show result3.
I'm very green when it comes to Angular and I have no idea how to approach it, especially that all values are strings.
If I understand correctly you could setup your combo something like ...
$scope.combo = {
"label1": {
"one": "result1",
"two": "result2"
},
"label2": {
"one": "result3",
"two": "result4"
}
}
You can then reference the correct value as combo[valueFromButton1][valueFromButton2] where valueFromButton1 and valueFromButton2 point at a model that contains the result of the clicked buttons. Your controller function then just needs to tie everything together by updating the model when the buttons are clicked.
See this plunkr ... https://embed.plnkr.co/GgorcM/
Without changing much you can also try like below provided code snippet.Run it to check the demo.
var app = angular.module('app', []);
app.controller('Ctrl',['$scope' ,function($scope) {
var key1, key2;
$scope.click = function(type, item) {
if (type == 'label') {
key1 = item;
} else if (type == 'val') {
key2 = item;
}
$scope.key = key1 + '+' + key2;
angular.forEach($scope.combo, function(val, key) {
if(val[$scope.key]){
$scope.finalVal = val[$scope.key];
}
});
};
$scope.section1 = [{
label: 'label1'
}, {
label: 'label2'
}];
$scope.section2 = [{
value: 'one'
}, {
value: 'two'
}];
$scope.combo = [{
'label1+one': 'result1'
}, {
'label2+one': 'result2'
}, {
'label1+two': 'result3'
}, {
'label2+two': 'result4'
}];
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller='Ctrl'>
<button ng-repeat="item in section1" ng-click="click('label',item.label)" type="button">{{item.label}}</button>
<button ng-repeat="item in section2" ng-click="click('val',item.value)"type="button">{{item.value}}</button>
<input type="text" ng-model="finalVal"/>{{key}} {{finalVal}}
</div>

angularjs custom filter vs selection lists

I have 6 select lists as the number of options i have and each has 6 options , when i select one options the rest of the section list must not contain the selected anymore
html
<div ng-repeat="filter in filters">
<select ng-model="selected" ng-options="item as item.type for item in filters|filterFnt:selected" ng-click="addProperty(selected)"></select>
js
$scope.selectionList = [] ;
$scope.selected = $scope.filters[0];
$scope.addProperty = function (obj) {
var index = $scope.selectionList.indexOf(obj);
if( index == -1) {
$scope.selectionList.push(obj);
}
};
$scope.filters = [
{
type : 'select one',
inputType : '111',
},
{
type : 'A',
inputType : '111',
},
{
type: 'B',
Xtype : '222'
},
{
type: 'C',
Xtype: '444' ,
},
{
type: 'D',
Xtype : '555'
},
{
type: 'E',
Xtype : '6666'
},
{
type: 'F',
inputType : '777'
}
];
app.filter('filterFnt', function() {
return function(input,x) {
angular.forEach($scope.selectionList, function(item) {
var index = input.indexOf(item);
if( index != -1 && item != x) {
input.splice(index, 1);
}
});
return input
};
});
issue
when i select an option from select list i want to keep that option in the list , what is happening it is being removed from the current one as well
Thank you
Your problem in splice function inside filter: it function change source array, so on every filter changed data for all selects.
Instead splice you can use simple filter function.
Your filter can be like
app.filter('filterFnt', function() {
return function(input, selectionList, selectionObj, filterType) {
//get elements that selected in current select, or not already selected
return input.filter(function(el) {
return selectionObj[filterType] === el || (selectionList.indexOf(el) == -1);
});
};
});
I add selectionObj, this object contain selected item for current select.
Before adding element in selectionList you need remove already added item from current select like
function removeFromSelected(currType){
var o = $scope.selectionObj[currType],
index = $scope.selectionList.indexOf(o);
if (index != -1)
$scope.selectionList.splice(index, 1);
}
var app = angular.module('app', []);
app.controller('ctrl', function($scope) {
function removeFromSelected(currType){
var o = $scope.selectionObj[currType],
index = $scope.selectionList.indexOf(o);
if (index != -1)
$scope.selectionList.splice(index, 1);
}
$scope.addProperty = function(obj, currType) {
removeFromSelected(currType);
if (!obj) {
delete $scope.selectionObj[currType];
} else {
var index = $scope.selectionList.indexOf(obj);
$scope.selectionList.push(obj);
$scope.selectionObj[currType] = obj;
}
};
$scope.filters = [{
type: 'A',
inputType: '111',
}, {
type: 'B',
Xtype: '222'
}, {
type: 'C',
Xtype: '444',
}, {
type: 'D',
Xtype: '555'
}, {
type: 'E',
Xtype: '6666'
}, {
type: 'F',
inputType: '777'
}];
$scope.selectionList = [];
$scope.selectionObj = {};
});
app.filter('filterFnt', function() {
return function(input, selectionList, selectionObj, filterType) {
return input.filter(function(el) {
return selectionObj[filterType] === el || (selectionList.indexOf(el) == -1);
});
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-repeat="filter in filters">
<select ng-model="selected" ng-options="item as item.type for item in filters|filterFnt:selectionList:selectionObj:filter.type" ng-change="addProperty(selected,filter.type)">
<option value="">select one</option>
</select>
</div>
selectionList - {{selectionList}}
</div>

How to check if any Checkbox is checked in Angular

Is there any function or ng-something to check if any of the displayed Checkboxes are checked?
I have the values through the ng-click="function()" and pass the values through. I can go by foot and check my array if any value is inside.
I want to activate/deactivate the "next"-button if any Checkbox is
checked.
What's the easiest way?
If you don't want to use a watcher, you can do something like this:
<input type='checkbox' ng-init='checkStatus=false' ng-model='checkStatus' ng-click='doIfChecked(checkStatus)'>
You can do something like:
function ChckbxsCtrl($scope, $filter) {
$scope.chkbxs = [{
label: "Led Zeppelin",
val: false
}, {
label: "Electric Light Orchestra",
val: false
}, {
label: "Mark Almond",
val: false
}];
$scope.$watch("chkbxs", function(n, o) {
var trues = $filter("filter")(n, {
val: true
});
$scope.flag = trues.length;
}, true);
}
And a template:
<div ng-controller="ChckbxsCtrl">
<div ng-repeat="chk in chkbxs">
<input type="checkbox" ng-model="chk.val" />
<label>{{chk.label}}</label>
</div>
<div ng-show="flag">I'm ON when band choosed</div>
</div>
Working: http://jsfiddle.net/cherniv/JBwmA/
UPDATE: Or you can go little bit different way , without using $scope's $watch() method, like:
$scope.bandChoosed = function() {
var trues = $filter("filter")($scope.chkbxs, {
val: true
});
return trues.length;
}
And in a template do:
<div ng-show="bandChoosed()">I'm ON when band choosed</div>
Fiddle: http://jsfiddle.net/uzs4sgnp/
If you have only one checkbox, you can do this easily with just ng-model:
<input type="checkbox" ng-model="checked"/>
<button ng-disabled="!checked"> Next </button>
And initialize $scope.checked in your Controller (default=false). The official doc discourages the use of ng-init in that case.
Try to think in terms of a model and what happens to that model when a checkbox is checked.
Assuming that each checkbox is bound to a field on the model with ng-model then the property on the model is changed whenever a checkbox is clicked:
<input type='checkbox' ng-model='fooSelected' />
<input type='checkbox' ng-model='baaSelected' />
and in the controller:
$scope.fooSelected = false;
$scope.baaSelected = false;
The next button should only be available under certain circumstances so add the ng-disabled
directive to the button:
<button type='button' ng-disabled='nextButtonDisabled'></button>
Now the next button should only be available when either fooSelected is true or baaSelected is true and we need to watch any changes to these fields to make sure that the next button is made available or not:
$scope.$watch('[fooSelected,baaSelected]', function(){
$scope.nextButtonDisabled = !$scope.fooSelected && !scope.baaSelected;
}, true );
The above assumes that there are only a few checkboxes that affect the availability of the next button but it could be easily changed to work with a larger number of checkboxes and use $watchCollection to check for changes.
This is re-post for insert code also.
This example included:
- One object list
- Each object hast child list.
Ex:
var list1 = {
name: "Role A",
name_selected: false,
subs: [{
sub: "Read",
id: 1,
selected: false
}, {
sub: "Write",
id: 2,
selected: false
}, {
sub: "Update",
id: 3,
selected: false
}],
};
I'll 3 list like above and i'll add to a one object list
newArr.push(list1);
newArr.push(list2);
newArr.push(list3);
Then i'll do how to show checkbox with multiple group:
$scope.toggleAll = function(item) {
var toogleStatus = !item.name_selected;
console.log(toogleStatus);
angular.forEach(item, function() {
angular.forEach(item.subs, function(sub) {
sub.selected = toogleStatus;
});
});
};
$scope.optionToggled = function(item, subs) {
item.name_selected = subs.every(function(itm) {
return itm.selected;
})
$scope.txtRet = item.name_selected;
}
HTML:
<li ng-repeat="item in itemDisplayed" class="ng-scope has-pretty-child">
<div>
<ul>
<input type="checkbox" class="checkall" ng-model="item.name_selected" ng-click="toggleAll(item)"><span>{{item.name}}</span>
<div>
<li ng-repeat="sub in item.subs" class="ng-scope has-pretty-child">
<input type="checkbox" kv-pretty-check="" ng-model="sub.selected" ng-change="optionToggled(item,item.subs)"><span>{{sub.sub}}</span>
</li>
</div>
</ul>
</div>
<span>{{txtRet}}</span>
</li>
Fiddle: example
I've a sample for multiple data with their subnode
3 list , each list has attribute and child attribute:
var list1 = {
name: "Role A",
name_selected: false,
subs: [{
sub: "Read",
id: 1,
selected: false
}, {
sub: "Write",
id: 2,
selected: false
}, {
sub: "Update",
id: 3,
selected: false
}],
};
var list2 = {
name: "Role B",
name_selected: false,
subs: [{
sub: "Read",
id: 1,
selected: false
}, {
sub: "Write",
id: 2,
selected: false
}],
};
var list3 = {
name: "Role B",
name_selected: false,
subs: [{
sub: "Read",
id: 1,
selected: false
}, {
sub: "Update",
id: 3,
selected: false
}],
};
Add these to Array :
newArr.push(list1);
newArr.push(list2);
newArr.push(list3);
$scope.itemDisplayed = newArr;
Show them in html:
<li ng-repeat="item in itemDisplayed" class="ng-scope has-pretty-child">
<div>
<ul>
<input type="checkbox" class="checkall" ng-model="item.name_selected" ng-click="toggleAll(item)" />
<span>{{item.name}}</span>
<div>
<li ng-repeat="sub in item.subs" class="ng-scope has-pretty-child">
<input type="checkbox" kv-pretty-check="" ng-model="sub.selected" ng-change="optionToggled(item,item.subs)"><span>{{sub.sub}}</span>
</li>
</div>
</ul>
</div>
</li>
And here is the solution to check them:
$scope.toggleAll = function(item) {
var toogleStatus = !item.name_selected;
console.log(toogleStatus);
angular.forEach(item, function() {
angular.forEach(item.subs, function(sub) {
sub.selected = toogleStatus;
});
});
};
$scope.optionToggled = function(item, subs) {
item.name_selected = subs.every(function(itm) {
return itm.selected;
})
}
jsfiddle demo

Resources