I'm trying to pass variable in filter object to search relevant field but when I put object without passing variable like underneath it works fine:-
stock.controller.js
var vm = this;
vm.stocks = [
{
itemName:'Peanut',
labelName: 'Fortune nuts',
cost : '$4'
},
{
itemName:'Sandwich',
labelName:'Coast Food',
cost: '$10'
}
]
vm.searchOptions = {itemName:vm.search};
stock.html
<input type="text" ng-model="vm.search"/>
<tr ng-repeat="s in vm.stocks | filter:{itemName:vm.search}">
<td>{{s.itemName}}</td>
<td>{{s.labelName}}</td>
<td>{{s.cost}}</td>
</tr>
But in case when I get object from variable It won't work. Following I pass variable vm.searchOptions instead of writing it hard coded.
<tr ng-repeat="s in vm.stocks | filter:vm.searchOptions">
<td>{{s.itemName}}</td>
<td>{{s.labelName}}</td>
<td>{{s.cost}}</td>
</tr>
Related
I have an array of objects which I want to display as a table with filter. My filter with model name is filtering the deep object family. This works fine but not the way I want it to work...
What I want: Insert string to input, e.g. 'Ma'. Now, I want it to display all items containing a string in family matching 'Ma' - that means I want to keep all the family members displayed as long as one string matches. In my example this would be the filtered result:
Homer Marge, Bart, Lisa, Maggie
Ned Maude, Rod, Todd
Example Code with snippet below:
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function($scope) {
$scope.tableData = [
{id: 1, name: 'Homer', family: ['Marge', 'Bart', 'Lisa', 'Maggie']},
{id: 2, name: 'Carl', family: []},
{id: 3, name: 'Lenny', family: []},
{id: 4, name: 'Clancy', family: ['Sarah', 'Ralph']},
{id: 5, name: 'Ned', family: ['Maude', 'Rod', 'Todd']},
{id: 6, name: 'Moe', family: []}
];
});
table td {
padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<table>
<tr>
Filter family members: <input type="text" ng-model="name">
</tr>
<tr ng-repeat="item in tableData">
<td>{{item.name}}</td>
<td>
<span ng-repeat="member in item.family | filter: name">
{{member}}{{$last ? '' : ', '}}
</span>
</td>
</tr>
</table>
</div>
You need to filter the first ng-repeat:
<tr ng-repeat="item in tableData | filter: name && {family: name}">
The key is to provide the name of the property you want to filter on. Here it is the property family.
See fiddle (Type ma in the search box)
Edit: And remove the filter you placed on the inner ng-repeat
You can solve this without a custom filter, by providing a conditional filter on the first ng-repeat. For example:
<tr ng-repeat="item in tableData | filter: (name.length > 0 || '') && {family: name}">
You need the conditional because if you add a filter and remove it, the blank arrays wont be considered (angular quirk).
I have created a working fiddle here
You want to use a custom filter on the top ng-repeat. You can specify a function as the filter: custom filter answer. In this custom filter return all items in tableData that contain a match in the sub array.
Note: You could use lodash to help with this calculation.
Example: plunkr example
<tr ng-repeat="item in tableData | filter:criteriaMatch(name)">
...
$scope.criteriaMatch = function( criteria ) {
return function( item ) {
if(!angular.isDefined(criteria) || criteria === "")
return true;
var results = _.filter(item.family, function(n) {
if(_.contains(n.toUpperCase(), criteria.toUpperCase()))
{
return true;
}
});
if(results.length !== 0)
return true;
else return false;
};
};
Do you try to use the filter in tableData?? Something like this:
<input type="text" ng-model="family">
And
<tr ng-repeat="item in tableData | filter: family">
JS
angular.module('bindExample', []).controller('ExampleController', ['$scope', function($scope) {
$scope.gridFields = {
id: {
width: 50
},
price: {
width: 60
},
};
$scope.allData = {
'one': {
id: '1234qwe',
price: 900
},
'two': {
id: 'asdadw',
price: 1700
},
'three': {
id: '342sdaw',
price: 1200
},
};
$scope.edit = function(row) {
console.log(row);
$scope.buffer = $scope.allData[row];
}
}]);
HTML
<div ng-app="bindExample">
<div ng-controller="ExampleController">
<table>
<tbody>
<tr ng-repeat="(row, data) in allData">
<td ng-repeat="(field, option) in gridFields" ng-bind="data[field]"></td>
<td><button ng-click="edit(row)">edit</button></td>
</tr>
</tbody>
</table>
<div>
<input type='text' ng-model="buffer.id"/>
</div>
<div>
<input type='text' ng-model="buffer.price"/>
</div>
</div>
</div>
After click on edit, values go to $scope.buffer variable from $scope.allData, and the inputs use the buffer as model, but when input is change the values in allData variable changing as well, but i don't want this, this is why is try to pass the values to other...
Problem illustrated here: JSFIDDLE
Any idea?
Use angular.copy()
$scope.buffer = angular.copy($scope.allData[row]);
Javascript will hold reference if assigned data is either function or object or array.
It provides a great benifit to the developer in many ways . but if you wanna to remove reference you have to clone it.
using angular
$scope.buffer = angular.copy($scope.allData[row]);
First things, you're going to get unexpected results in ng-repeat if you use a parent object literal rather than an array (Angular doesnt guarantee that it will iterate through keys in order):
$scope.allData = [ //you're better off using an Array
'one': {
id: '1234qwe',
price: 900
},
'two': {
id: 'asdadw',
price: 1700
},
'three': {
id: '342sdaw',
price: 1200
},
]; //see above
Secondly, the reason this is happening is that Javascript copies everything as a reference unless it is a primitive, so when you do this:
$scope.buffer = $scope.allData[row];
You're actually just storing a pointer to the original object $scope.allData[row] in $scope.buffer.
To do a "deep copy" yo ucan use angular.copy as suggested by #moncefHassein-bey in his answer.
My code is like this
<body ng-app="myApp" ng-controller="MainCtrl">
<div>Name only
<input ng-model="search.name" />
<br />
<table id="searchObjResults">
<tr>
<th>Name</th>
<th>Phone</th>
</tr>
<tr ng-repeat="friendObj in friends | filter:search:strict | limitTo:1">
<td>{{friendObj.name}}</td>
<td>{{friendObj.phone}}</td>
</tr>
</table>
</div>
<div>
<button type="button" id="btn_submit" ng-click="submitForm()">Get rates</button>
</div>
angular.module('myApp', []).controller('MainCtrl', ['$http', '$scope', function ($http, $scope) {
$scope.friends = [{
name: 'John',
phone: '555-1276'
}, {
name: 'Mary',
phone: '800-BIG-MARY'
}, {
name: 'Mike',
phone: '555-4321'
}, {
name: 'Adam',
phone: '555-5678'
}, {
name: 'Julie',
phone: '555-8765'
}, {
name: 'Juliette',
phone: '555-5678'
}];
$scope.submitForm = function () {
// i want to get the data here
};
}]);
As you can see at a time only one friend will be active on my screen. when I press my submit button, I want that data (filtered single row) to be the only value on my current $scope.friends so that I can send it to an external service as the selected data. Can any one point out what i need to do here
Fiddle
Note: I can't change the position of this button.
Why not make your button part of the table row, since there will only ever be one? Here is a JSFiddle showing it working in that fashion.
The ng-click function handler for the button can then simply take a parameter that is the actual friendObj you are interested in:
<button type="button" ng-click="submitForm( friendObj )">Get rates</button>
EDIT: There is actually a way to do this if you can't move the button; make the ng-repeat operate over a NEW array, which will be accessible outside of the ng-repeat. So your ng-repeat statement becomes:
<tr ng-repeat="friendObj in newArray = (friends | filter:search:strict | limitTo:1)">
And then your button can simply reference the one-element array:
<button type="button" ng-click="submitForm( newArray )">Get rates</button>
Updated Fiddle here :-)
Try this:
$scope.submitForm = function () {
var data = $filter('filter')($scope.friends, $scope.search.name);
};
Fiddle here.
If you put the filter in the controller instead of the view, you could set a variable like $scope.result that the submitForm function could use. For example, in your HTML, you could add an ng-change directive to your search field like so:
<input ng-model="search.name" ng-change="updateResult()" />
Then, instead of using ng-repeat, you'd use ng-show to show the one result, or hide the row if there is no result:
<tr ng-show="result">
<td>{{result.name}}</td>
<td>{{result.phone}}</td>
</tr>
Then in your controller:
$scope.search = {name: ''};
$scope.updateResult = function() {
$scope.result = $filter('filter')($scope.friends, $scope.search.name)[0];
}
$scope.updateResult();
// ...
$scope.submitForm = function() {
// $scope.result can be used here
}
EDIT: The advantage of this approach is it's a bit DRYer because you don't re-filter inside submitForm. MarcoS's approach has the advantage of being a lot shorter!
I want to make search based on ID using angularjs filter. I am generating some value based on ID of node like based on userId I am finding name, phone no of user from storage JSON object.
But I am not able to find how to filter result if i have just ID and its JSON object.
I have made jsfiddle to get clear idea about my issue http://jsfiddle.net/U3pVM/5969/
HTML
<div ng-app ng-controller="Ctrl">
Search: <input ng-model="searchTableQuery.id">
<table id="searchTextResults" class="table table-bordered">
<tr><th>Name</th></tr>
<tr ng-repeat="friend in friends | filter:searchTableQuery">
<td>{{getCurrentValue(friend.id, friends)}}</td>
</tr>
</table>
</div>
JS
function Ctrl($scope) {
$scope.friends = [{id: 1, name:'John', phone:'555-1276'},
{id: 2, name:'Mary', phone:'800-BIG-MARY'},
{id: 3, name:'Mike', phone:'555-4321'},
{id: 4, name:'Adam', phone:'555-5678'},
{id: 5, name:'Julie', phone:'555-8765'},
{id: 6, name:'Juliette', phone:'555-5678'}];
$scope.getCurrentValue = function (id, availableData) {
var result = _.where(availableData, { 'id': id });
if (_.isEmpty(result)) {
result = [{id:0,value:""}];
};
return result[0].name;
}
}
How to make filter on table using ID of node in angularjs?
<div ng-app ng-controller="Ctrl">
Search: <input ng-model="searchTableQuery">
<table id="searchTextResults" class="table table-bordered">
<tr><th>Name</th></tr>
<tr ng-repeat="friend in friends | filter:{id:searchTableQuery}">
<td>{{getCurrentValue(friend.id, friends)}}</td>
</tr>
</table>
</div>
As suggested above, angular do read your searchTableQuery but cannot identify it as an attribute of your list of friend objects so it can filter it, or to be more clear doesn't now to what attribute of the friend object does he have to couple it.
To resolve the issue, you have to force the recognition of searchTableQuery as an id by doing : filter:{id:searchTableQuery}.
In knockout.js, is it possible to let the right-hand-side of a binding (the value of the binding) be dynamic? For example,
<input data-bind="value: dynamicBinding()"/>
<script type="text/javascript">
var vm = {
dynamicBinding : function() {
return "foo().bar";
},
foo : ko.observable({
bar : ko.observable("hi");
}
};
ko.applyBindings(vm);
</script>
the result should be that the the dynamicBinding function is executed while applying the bindings and the resulting string is used as the binding. The input element should be bound to foo().bar, which is the observable with the value "hi".
If you wonder why I would want this, I am trying to render a dynamic table with knockout, where both the rows and the columns are observableArrays, and I want to allow the column definitions to contain the expression of the binding for that column. I.e., I want to be able to do this:
<table data-bind="foreach: data">
<tr data-bind="foreach: $root.columns">
<td data-bind="text: cellValueBinding()"></td>
</tr>
</table>
<script type="text/javascript">
var vm = {
data: ko.mapping.fromJS([
{title: "Brave New World", author: { name : "Aldous Huxley" },
{title: "1984", author: { name : "George Orwell" },
{title: "Pale Fire", author: { name : "Vladimir Nabokov" }]),
columns: ko.observableArray([
{header: "Title", cellValueBinding: function () { return "$parent.title"; }},
{header: "Author", cellValueBinding: function () { return "$parent.author().name"; }}
])
};
ko.applyBindings(vm);
</script>
As you can see from the example, the column definition knows how to extract the value from the data. The table markup itself is more or less a placeholder. But as far as I can tell, this does not work, due to the way knockout processes the bindings. Are there any other options available?
Thanks.
Solution: I ended up using Ilya's suggestion - I can let cellValueBinding be a function that accepts the row and column as arguments, and returns an observable. This technique is demonstrated in this fiddle.
Use ko.computed for it.
Look on example
JSFiddle
EDIT
In your second example, you can pass $parent value ti the function
<td data-bind="text: cellValueBinding($parent)"></td>
and in model
{header: "Title", cellValueBinding: function (parent) { return parent.title; }},
{header: "Author", cellValueBinding: function (parent) { return parent.author().name; }}
JSFiddle