In this fiddle :
http://jsfiddle.net/9fR23/187/
The table elements are not been hidden when I hit div element "Flip!". The elements should become hidden as I'm changing the state of the structure which is determined by ng-show
The map is being updated but does not seem to be applied ?
I tried adding $scope.$apply to block when flip is invoked but same result. How to update the state of ng-show when the underlying map data structure changes ?
fiddle code :
<div ng-app="myapp" ng-controller="FirstCtrl">
<table class="table table-striped">
<tr ng-repeat="person in people">
<td ng-show="errorMap([1])">{{ person.first + ' ' + person.last }}</td>
</tr>
</table>
<div ng-click="flipView()">Flip!</div>
</div>
var myapp = angular.module('myapp', []);
myapp.controller('FirstCtrl', function ($scope) {
var errorMap = new Object()
errorMap['1'] = 'true'
errorMap['2'] = 'false';
$scope.errorMap = errorMap
$scope.people = [
{ id: 1, first: 'John', last: 'Rambo' },
{ id: 2, first: 'Rocky', last: 'Balboa' },
{ id: 3, first: 'John', last: 'Kimble' },
{ id: 4, first: 'Ben', last: 'Richards' }
];
$scope.flipView = function(){
alert('flipped')
$scope.errorMap['1'] = 'false'
$scope.$apply
}
});
Update : I changed the json to contain values true , false instead of 'true , 'false'. But same result. Updated fiddle : http://jsfiddle.net/9fR23/188/
Updated code :
errorMap['1'] = true
errorMap['2'] = false;
I've edited your fiddle:
HTML
<div ng-app="myapp" ng-controller="FirstCtrl">
<table class="table table-striped">
<tr ng-repeat="person in people">
<td ng-show="errorMap">{{ person.first + ' ' + person.last }}</td>
</tr>
</table>
<div ng-click="flipView()">Flip!</div>
</div>
Javascript
var myapp = angular.module('myapp', []);
myapp.controller('FirstCtrl', function ($scope) {
$scope.errorMap=true;
$scope.people = [
{ id: 1, first: 'John', last: 'Rambo' },
{ id: 2, first: 'Rocky', last: 'Balboa' },
{ id: 3, first: 'John', last: 'Kimble' },
{ id: 4, first: 'Ben', last: 'Richards' }
];
$scope.flipView = function(){
alert('flipped');
$scope.errorMap = !$scope.errorMap; //this is actual flipping (show/hide)
}
});
Waiting for your feedback
It was just a little syntax erro. You have ( ) in errorMap([1]). Without this one, all is fine.
<td ng-show="errorMap[1]">{{ person.first + ' ' + person.last }} </td>
Moreover, I think a simple boolean could do the trick.
There is the forked Fiddle
In your view you are treating errorMap as a function but it is an object literal. Also you are passing an integer but the keys for the object are strings
Try
<td ng-show="errorMap['1']">{{ person.first + ' ' + person.last }}</td>
DEMO
Related
I'm looking for a solution to show dynamic header in the angular table for some of its <td>
my data looks like
let data = [
{
id: 1,
name: 'name',
fields: {
field 1: { value: '123'},
field 2: {value: 'macx'}
}
},
{
id: 2,
name: 'name2',
fields: {
field 1: { value: '456'},
field 2: {value: '3333'}
}
}
]
it should show in one table, I mean fields attr's should show as extra columns in the same table
note: fields are dynamic and I can't know it exactly so I need to do something like this in code
if any idea how I can get that work or any other idea to get the view as explained
<tr ng-repeat="data in $data">
<td data-title="'id'|translate"
sortable="'id'">
{{data.id}}
</td>
<td ng-repeat="(key, value) in data.fields track by $index"
ng-show="columnsHash[key]"
data-title="customFieldsTitles[$index]"
filterable="{field:'fields', type:'text', align:'LEFT'}"
data-title-text="customFieldsTitles[$index]">
{{value && value.value || ''}}
</td>
<td ng-show="columnsHash.totalBenefitTarget"
data-title="'target_total_benefit' | translate"
sortable="'total_benefit_target'"
style="text-align:center;"
filterable="{field: 'total_benefit_target', type:'number_range', options: {min: Number.MIN_VALUE, max: Number.MAX_VALUE}}">
{{data.total_benefit_target | number: 0}}
</td>
<td ng-show="columnsHash.totalBenefitActual"
data-title="'actual_total_benefit' | translate"
sortable="'total_benefit_actual'"
style="text-align:center;"
filterable="{field: 'total_benefit_actual', type:'number_range',
options: {min: Number.MIN_VALUE, max: Number.MAX_VALUE}}">
{{data.total_benefit_actual | number: 0}}
</td>
<tr>
showing columns order is important so writing it like code above
thanks in advance
angular table use scope.$column to render tb cols so I solved that by using scope binding
<table ng-table="tableParams" ng-init="initTable()">
<td ng-repeat="(key, value) in data.fields"
data-title="'Custom Field'"
sortable="'fields'"
filterable="{field:'fields', type:'text', align:'LEFT'}">
{{value && value.value || ''}}
</td>
</table>
in controller
var tableColumns;
$scope.initTable = function(){
var scope = this;
$timeout(function(){
tableColumns = scope.$columns;
});
};
after loading data for table call this function to update columns title
function updateCustomFields(){
var columnTemplate, index;
var colCount = 0;
if (!tableColumns) {
return;
}
tableColumns.map(function(col, i){
if (col.title() === 'Custom Field'){
columnTemplate = col;
index = i;
tableColumns.splice(index, 1);
return true;
}
});
for(var fieldLabel in $scope.customFieldsHash){
(function (label) {
var column = angular.copy(columnTemplate);
column.id = column.id+colCount/10;
column.title = function(){ return label; };
tableColumns.splice(index+colCount, 0, column);
colCount++;
})(fieldLabel);
}
}
I'm using typeahead on an input to send suggestions.. every thing work fine but know i want to create HTML table dynamically and assign values in a table so in my java script I declare HTML variable to store the table and than i'm using this variable when i return values. The problem arose when I save the html in a variable than It starting giving error that my the variable is not defined and when I put it in quotes than on return it just shows me the variable. .. I don't know what to do or which methodology should I've to use but I'm stuck at that Point here is my HTML working
<input class="typeahead" placeholder="Enter to Search" />
Here is my javascript working
var jsonData = [{
country: "Holland",
city: "Amsterdam"
}, {
country: "Belgium",
city: "Brussel"
}, {
country: "Germany",
city: "Berlin"
}, {
country: "France",
"city": "Paris"
}];
var dataSource = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('country', 'city'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: jsonData
});
dataSource.initialize();
var html='<div><table width="100%" border="1"><tr><td width="50%" align="right">\'+data.country+\'</td><td width="50%">' + 'data.city' + '</td></tr></table></div>';
$('.typeahead').typeahead({
minLength: 1,
highlight: true
}, {
name: 'countries',
display: function(item){ return item.country+'–'+item.city},
source: dataSource.ttAdapter(),
templates: {
empty: [
'<div class="empty">No Matches Found</div>'].join('\n'),
header: '<div><h5><table width="100%" border="1"><thead><tr><th width="50%" align="center">Item Name</th><th width="50%" align="center">City Name</td></th></table></h5></div>',
suggestion: function (data) {
return html
}
}
});
Here is my Working jsfiddle link
Any Suggestion ... ? ? ?
Update 1
Now I'm able to generate HTML dynamically .. . with the help of your suggestions.
So now I'm up to generate array keys dynamically in my updated fiddle but the problem is when i generate dynamically than it gives me undefined result and when I hard code the array key than It return me the true values which are required Take a look at my Updated fiddle below is my javascript code
var jsonData = [{
a: "Holland",
b: "Amsterdam"
}, {
a: "Holland",
b: "Amsterdam"
}, {
a: "Holland",
b: "Amsterdam"
}, {
a: "Holland",
b: "Amsterdam"
}, {
a: "Belgium",
b: "Brussel"
}, {
a: "Germany",
b: "Berlin"
}, {
a: "France",
b: "Paris"
}];
var dataSource = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('a', 'b'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: jsonData
});
dataSource.initialize();
var generateHTML= function(data) {
var key='a';
var html='<div><table width="100%" border="1"><tr>';
for(var i=0; i<2; i++)
{
html+='<td width="50%" align="right">' + data. key + '</td>';
//key++;
}
//'<td width="50%">' + data.city + '</td></tr></table></div>';
html+='</tr></table></div>';
return html;
//return '<div><table width="100%" border="1"><tr><td width="50%" align="right">' + data.country + '</td><td width="50%">' + data.city + '</td></tr></table></div>'
};
$('.typeahead').typeahead({
minLength: 1,
highlight: true
}, {
name: 'countries',
display: function(item){ return item.country+'–'+item.city},
source: dataSource.ttAdapter(),
templates: {
empty: [
'<div class="empty">No Matches Found</div>'].join('\n'),
header: '<div><h5><table width="100%" border="1"><thead> <tr><th width="50%" align="center">Item Name</th><th width="50%" align="center">City Name</td></th></table></h5></div>',
suggestion: function (data) {
return generateHTML(data)
}
}
});
Update 3 Task Done by Doing RND on Javascript Array Objects
Thank You All for your Help and Suggestions. I'm able to do it Successfully. When we call Java Script arrays by objects than we have to pass our dynamically generated keys in the following pattern
obj[key];
following is my working JSFiddle Link
As your HTML is out of the scope of the function suggestion the variable html can't see the variable called data.
One way to solve this would be creating a function which takes as parameter the data variable and then returns to you an HTML with the data on it.
var generateHTML= function(data) {
return '<div><table width="100%" border="1"><tr><td width="50%" align="right">' + data.country + '</td><td width="50%">' + data.city + '</td></tr></table></div>'
};
And then call this function on the suggestion function:
$('.typeahead').typeahead({
minLength: 1,
highlight: true
}, {
name: 'countries',
display: function(item){ return item.country+'–'+item.city},
source: dataSource.ttAdapter(),
templates: {
empty: [
'<div class="empty">No Matches Found</div>'].join('\n'),
header: '<div><h5><table width="100%" border="1"><thead><tr><th width="50%" align="center">Item Name</th><th width="50%" align="center">City Name</td></th></table></h5></div>',
suggestion: function (data) {
return generateHTML(data)
}
}
});
Here is an example of how this could be done: http://jsfiddle.net/59b62kz3/
I have an array that looks like this:
0: {ID: null,
name: "test",
city: "Austin",
UserColors: [{color: "blue"},{hobby:"beach"} ... ]}
}...
I am trying to ng-repeat through the initial array but once I try to loop through the list, i see nothing, heres the html/angular
<tr ng-repeat="c in vm.people">
<td>{{c.name}}</td>
<td>{{c.city}}</td>
<td ng-repeat="uc in c.UserColors">
<td>{{uc.color}}</td>
</td>
</tr>
I am not sure what is wrong, and I would appreciate your help, I thank you in advance.
I would process the field with a custom filter:
<td ng-repeat-start="(key, value) in c.UserColors | reduce">
<b>{{key}}</b>
</td>
<td ng-repeat-end>
{{value}}
</td>
The filter:
app.filter("reduce",function() {
return function(items) {
var x = items.map(o => Object.entries(o));
var x2 = x.reduce(((a,x) => (a.concat(x))), []);
var x3 = x2.reduce(((o,x) => (o[x[0]]=x[1],o)), {});
return x3;
}
})
The DEMO
angular.module("app",[])
.controller("ctrl",function(){
var vm = this;
vm.people = {
0: {ID: null,
name: "test",
city: "Austin",
UserColors: [{color: "blue"},{hobby:"beach"}]
},
1: {ID: null,
name: "best",
city: "Boston",
UserColors: [{colorx: "red"},{shirt:"black"}]
},
2: {ID: null,
name: "rest",
city: "Paris",
UserColors: [{colory: "yel"},{fruit:"peach"}]
},
}
})
.filter("reduce",function() {
return function(items) {
var x = items.map(o => Object.entries(o));
var x2 = x.reduce(((a,x) => (a.concat(x))), []);
var x3 = x2.reduce(((o,x) => (o[x[0]]=x[1],o)), {});
return x3;//items;
}
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app" ng-controller="ctrl as vm">
<h3>Table</h3>
<table>
<tr ng-repeat="c in vm.people">
<td>{{c.name}}</td>
<td>{{c.city}}</td>
<td ng-repeat-start="(key, value) in c.UserColors | reduce">
<b>{{key}}</b>
</td>
<td ng-repeat-end>
{{value}}
</td>
</tr>
</table>
</body>
Yep, it's me again. I'm trying to filter an array based on an array of strings. So while a single string filter is easy with Vue...
<div v-for="item in items | filterBy 'words' in 'property'">
...multiple search strings becomes more complex. There's been several questions about how to do this on StackOverflow already, but very few answers. Currently I'm trying to repurpose the custom filter found here for my needs, but it's not working.
My use case:
I have an array of groups (checkboxes) that the user can select to filter an array of people. Each person is assigned to one or more groups so if any one of their groups is selected by the user, that person should show up.
So my templates look like this:
<template v-for="group in ensemble_groups">
<input name="select_group[]" id="group_#{{ $index }}"
:value="group"
v-model="selected_groups"
type="checkbox">
<label for="group_#{{ $index }}">#{{ group }}</label>
</template>
<template v-for="person in cast | filterBy selectGroups">
<div>#{{ person.actor_name }}</div>
</template>
You see my custom filter selectGroups there? Here's my Vue arrays:
selected_groups: [],
ensemble_groups: ["Leads","Understudies","Children","Ensemble"],
cast: [
{
actor_name: "Dave",
groups: ["Leads"],
},
{
actor_name: "Jill",
groups: ["Leads"],
},
{
actor_name: "Sam",
groups: ["Children","Ensemble"],
},
{
actor_name: "Austin",
groups: ["Understudies","Ensemble"],
},
],
And finally here's the custom filter. I can't tell if it's even being triggered or not, because when I click on a group checkbox, nothing happens.
filters: {
selectGroups: function() {
if (!selected_groups || selected_groups.length === 0) {
return cast;
}
return this.recursiveFilter(cast, selected_groups, 0);
}
},
methods: {
recursiveFilter: function(cast, selected_groups, currentPosition) {
if (currentPosition+1 > selected_groups.length)
return cast;
var new_cast;
new_cast = cast.filter(function(person) {
for (group of person.groups) {
if (group.value == selected_groups[currentPosition])
return true;
}
});
return this.recursiveFilter(new_cast, selected_groups, currentPosition+1);
}
}
So if the user selects Leads only Dave and Jill should appear. If the user then checks Children, Dave, Jill, and Sam should appear. I'm so close!
I would use a computed property instead of a filter and a method.
I'd go through each cast member and if any of their groups is in selected_groups I'd allow it through the filter. I'd so this using Array.some.
results: function() {
var self = this
return self.cast.filter(function(person) {
return person.groups.some(function(group) {
return self.selected_groups.indexOf(group) !== 1
})
})
},
Here's a quick demo I set up, might be useful: http://jsfiddle.net/crswll/df4Lnuw6/8/
Since filters are deprecated (in v-for, see Bill's comment), you should get into the habit of making computeds to do filtery things.
(If you're on IE, you can't use includes without a polyfill; you can use indexOf...>=0 instead.)
new Vue({
el: '#app',
data: {
selected_groups: [],
ensemble_groups: ["Leads", "Understudies", "Children", "Ensemble"],
cast: [{
actor_name: "Dave",
groups: ["Leads"],
}, {
actor_name: "Jill",
groups: ["Leads"],
}, {
actor_name: "Sam",
groups: ["Children", "Ensemble"],
}, {
actor_name: "Austin",
groups: ["Understudies", "Ensemble"],
}, ]
},
computed: {
filteredCast: function() {
const result = [];
for (const c of this.cast) {
if (this.anyMatch(c.groups, this.selected_groups)) {
result.push(c);
}
}
return result;
}
},
methods: {
anyMatch: function(g1, g2) {
for (const g of g1) {
if (g2.includes(g)) {
return true;
}
}
return false;
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.min.js"></script>
<div id="app">
<template v-for="group in ensemble_groups">
<input name="select_group[]" id="group_#{{ $index }}" :value="group" v-model="selected_groups" type="checkbox">
<label for="group_#{{ $index }}">#{{ group }}</label>
</template>
<template v-for="person in filteredCast">
<div>#{{ person.actor_name }}</div>
</template>
</div>
var demo = new Vue({
el: '#demo',
data: {
search: 're',
people: [
{name: 'Koos', age: 30, eyes:'red'},
{name: 'Gert', age: 20, eyes:'blue'},
{name: 'Pieter', age: 12, eyes:'green'},
{name: 'Dawid', age: 67, eyes:'dark green'},
{name: 'Johan', age: 15, eyes:'purple'},
{name: 'Hans', age: 12, eyes:'pink'}
]
},
methods: {
customFilter: function(person) {
return person.name.indexOf(this.search) != -1
|| person.eyes.indexOf(this.search) != -1
;
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div id="demo">
<input type="text" class="form-control" v-model="search"/>
<br/>
<table class="table">
<thead>
<tr>
<th>name</th>
<th>eyes</th>
<th>age</th>
</tr>
</thead>
<tr v-for="person in people | filterBy customFilter">
<td>{{ person.name }}</td>
<td>{{ person.eyes }}</td>
<td>{{ person.age }}</td>
</tr>
</table>
</div>
I am trying to create a general table using element type custom directive that takes collection of data and generates the table header and create all the rows and columns of the table, then i tried to sort all the columns. The part of this code is working fine. But my next goal is to apply the filter on all the columns of the table, as the data is dynamic so we cannot decide about the field to filter at run time. The problem I diagnosed is that I am using ng-repeat="row in customers|filter:{$scope.searchkey:$scope.search}". Here seems to be the problem because in then expression the first thing should be object but $scope returns object.
Following is my code.
Index.html
<body>
<div>
<my-table input="Customers"></my-table>
<br />
<br />
<br />
<my-table input="Users"></my-table>
</div>
</body>
script.js
angular.module('DirectiveDemo', [])
.controller('Controller', ['$scope', function ($scope) {
$scope.Customers = [{ Name: "2Touqeer", Code: "2" },
{ Name: "3Nadeem", Code: "3" },
{ Name: "1Talha", Code: "1" },
{ Name: "4Muslim Khan", Code: "4" }
];
$scope.Users = [{ Name: "Touqeer", Code: "2", ID: "2Touqeer", CID: "CID1" },
{ Name: "Nadeem", Code: "3", ID: "3Muslim", CID: "CID3" },
{ Name: "Talha", Code: "1", ID: "1Nadeem", CID: "CID2" },
{ Name: "Muslim Khan", Code: "4", ID: "1Talha", CID: "CID5" },
{ Name: "Khan", Code: "4", ID: "1Khan", CID: "CID78" }
];
}])
.directive('myTable', function () {
return {
restrict: 'E',
transclude: true,
scope: {
customerInfo: '=input'
},
templateUrl: 'my-table-info.html',
controller: function ($scope) {
$scope.searchKey = 'CID';
alert($scope.searchKey)
$scope.reverseSort = false;
$scope.search = '';
$scope.List = [];
$scope.order = function (item) {
$scope.orderByField = item;
alert($scope.search)
}
$scope.filterValue = function (itemKey,valueKey) {
$scope.searchKey = itemKey;
if (valueKey != 'undefined') {
$scope.search = valueKey;
alert($scope.search)
}
}
}
};
})
my-table-info.html
<table ng-transclude>
<thead class="GridHeaderItem ControlBorder">
<tr>
<th ng-repeat="(key, value) in customerInfo[0]"> <a href="#" ng-click="order(key); reverseSort = !reverseSort">
{{key}}
<span ng-show="orderByField==key">
<span ng-show="!reverseSort">
<i class="glyphicon-circle-arrow-up glyphicon"></i>
</span>
<span ng-show="reverseSort">
<i class="glyphicon-circle-arrow-down glyphicon"></i>
</span></span>
</a></th>
</tr>
<tr class="ControlBorderRight ControlBorderTop ControlBorderBottom">
<td class=" ControlBorderLeft ControlBorderBottom" ng-repeat="(key, value) in customerInfo[0]" >
<input type="text" style="width:100%;" ng-model="searchfilter" ng-change="filterValue(key,searchfilter);" />
<!--ng-change="filter(key);"-->
</td>
</tr>
</thead>
<tr ng-repeat="row in customerInfo|filter:{co[row]:2}|orderBy:orderByField:reverseSort" class="ControlBorderRight ControlBorderTop ControlBorderBottom ">
<td ng-repeat="col in row" class=" ControlBorderBottom ControlBorderLeft Control LabelControl">{{col}}</td>
</tr>
</table>
You should build a custom filter function angular.module('myMod',[]).filter('myFilter',function(){ return function(input){...} }) then your repeat should look like this: ng-repeat="row in customers | myFilter" - the row object will be fed to the filter function as the input parameter.