I am having a problem when trying to filter an array in angular. I'm using typescript.
I have a parent page that contains a directive. The directive has a property of an Array of items which it displays in a datatable.
On the parent page, I want to filter the list that is being passed to the directive. Here is how I am doing it....
<table items="vm.items"></table>
In my parent controller, I have a button which when you press it executes the following function:
applyFilters() {
var filteredItems=[];
this.items.forEach((value, key) => {
if (value.item!== 'test') {
this.filteredItems.push(value);
}
});
console.log(this.filteredItems);
this.items = this.filteredItems;
}
But the value in the directive does not update when I update the filter?
What am I doing wrong here?
Here:
if (value.item!== 'test') {
this.filteredItems.push(value);
}
The variable filteredItems is defined through var filteredItems = [];, while you assign through this.filteredItems. Just use:
filteredItems.push(value);
...
this.items = filteredItems;
Related
Hi I have been using this tag to change my css style, if the condition totalAsset and sortedAsset are same
<div class="table-row" ng-repeat="x in myData" ng-click="sort()"
ng-class="{'lightblue': x.totalAsset == sortedAsset}">
totalAsset is my data in like this
$scope.myData = [
{
totalAsset: "23557"
},
{
totalAsset: "4512190",
},
{
totalAsset: "2190",
},
{
totalAsset: "1256790",
}
]
i have create a function that self sort the totalAsset
$scope.sort = function (){
$scope.unsortedAsset = $scope.myData.totalAsset;
$scope.sortedAsset=$scope.unsortedAsset.split("").sort().join("");
}
in the logic only the first and last row will become blue the other two rows remain same.
In sort() you directly access $scope.myData.totalAsset. This gets resolved as a reference to the last object in $scope.myData that has a totalAsset member.
Instead you wanted to iterate over all objects in myData. That can be achieved by supplying a parameter to the sort function like in the code below.
$scope.sort = function (totalAsset){
$scope.unsortedAsset = totalAsset;
$scope.sortedAsset=$scope.unsortedAsset.split("").sort().join("");
}
Then you also must call the sort function by supplying the parameter value.
<div class="table-row" ng-repeat="x in myData" ng-click="sort(x.totalAsset)" ng-class="{'lightblue': x.totalAsset == sortedAsset}">
Could you please tell me how to how to set/update a clicked item in React.js?.
I want the value of the clicked element to change to "test". How can I setup such event handler to do this?
Here is my code
On item click I am trying to update the item like that
btnClick(obj) {
obj.hse = 'test';
console.log(obj);
// this.setState({
// data: [obj]
// });
}
Since your data object is an array, I think the easiest way to implement this is to send your btnClick() function the id of the element that was clicked, update that value, and then save the new state.
Codepen
Like so:
this.state.data.map((item, i) => {
return <li onClick = {
this.btnClick.bind(this, i)
> {
item.hse
} < /li>;
})
By changing map(item) => { to map(item, i) => { you make use of the index parameter of the Array map method. This i variable is then used when binding the btnClick function.
btnClick(id) {
let temp = this.state.data.slice();
temp[id].hse = 'test';
this.setState({
data: temp
});
}
Here, the id is the index of the item clicked. Start off by creating a shallow copy of the this.state.data and put it into a local temp variable. Then, change the hse property of temp[id]. Finally, update the data state with the local variable.
edit: fixed broken codepen link
I have a long list of items that I want to show in a <ul>. I want to add a "filter" input, so the user can narrow down the list of items to those matching the filter.
My controller contains a filter prop and a list array:
function Ctrl() {
this.filter = m.prop('');
this.list = [];
}
I've added an update method to the controller, which looks at the filter prop and updates the contents of the list array:
Ctrl.prototype.update = function (value) {
var _this = this;
if (this.filter()) {
searchItems(this.filter(), function (items) {
_this.list = items;
});
} else {
this.list = [];
}
};
Finally, my view iterates over the list array and renders the items. Additionally, it displays an input on top, bound to the filter prop:
var view = function (ctrl) {
return m('#content', [
m('input', {
oninput: m.withAttr("value", ctrl.filter),
value: ctrl.filter()
}),
m('ul', [
ctrl.list.map(function (item, idx) {
return m('li', m('span', item.getName()));
})
])
]);
};
My question is, how to make the update function fire when the value of filter changes, so that I get the updated list of items?
Do I need to position two oninput events? One to update filter and one to fire update?
Should I use a single oninput event and update the filter property within he update function?
Anything else?
When you use m.withAttr, what you're saying is that when the event handler fires (oninput), you will take some attribute of the element (value) and pass it into your second argument, which is a function (ctrl.filter). Your current sequence of events:
filter property gets updated
mithril redraws
What you want to do, is call the update function (instead of the getter/setter ctrl.filter function), and bind it so you can retain the proper context in the function:
m('input', {
oninput: m.withAttr("value", ctrl.update.bind(ctrl)),
value: ctrl.filter()
}),
Then, in your update function the value will be passed to the function and you can set it there.
Ctrl.prototype.update = function (value) {
this.filter(value);
...
Now what'll happen:
ctrl.filter property gets updated
ctrl.list gets filtered based on ctrl.filter
mithril redraws
Another way to handle this is to not have any "list" property in your controller / model, but to let the view grab a filtered list instead. There's only one thing really changing, after all, and that's the "filter" property. The filtered list is derived from that, so by creating another property on the controller, you're effectively duplicating the same state.
Additionally, you could keep m.withAttr('value', ctrl.filter) and benefit from that simplicity.
Something like:
var filteredItems = ctrl.getFilteredItems();
var view = function (ctrl) {
return m('#content', [
m('input', {
oninput: m.withAttr("value", ctrl.filter),
value: ctrl.filter()
}),
m('ul', [
filteredItems.map(function (item, idx) {
return m('li', m('span', item.getName()));
})
])
]);
};
I'm looking for a way to sort an array inside an Angular service, and still retain the correct bindings in the controller.
If I skip the sorting, the bindings work great, but the array isn't ordered as I need it to be.
Whenever I perform the sort using Lodash's _.sortBy or angular's $filter('orderBy') service, one of two things happens:
The array in the service is sorted correctly, but the binding to the controller is severed due to it no longer referencing the same array anymore.
If I attempt to fix this by using Lodash's _.cloneDeep or angular's angular.copy, the browser freezes due to circular references (?).
Service.js
angular.module('exampleapp')
.factory('ClientFeedService', function($filter, $firebase, FIREBASE_URL, FeedItemService) {
return function(clientId) {
var ClientFeedService = this;
var ref = new Firebase(FIREBASE_URL + 'feeds/clients/' + clientId);
var initialDataLoaded = false;
ClientFeedService.feedArray = [];
ClientFeedService.sortItems = function() {
// Sorting logic here
};
/**
* Bind to the initial payload from Firebase
*/
ref.once('value', function() {
// Sort items after initial payload
ClientFeedService.sortItems();
initialDataLoaded = true;
});
/**
* Bind to new items being added to Firebase
*/
ref.on('child_added', function(feedItemSnap) {
console.log('child_added');
ClientFeedService.feedArray.unshift(FeedItemService.find(feedItemSnap.name(), feedItemSnap.val()));
// Sort after new item if initial payload loaded
if (initialDataLoaded) {
ClientFeedService.sortItems();
}
});
ClientFeedService.getFeedItems = function() {
return ClientFeedService.feedArray;
};
return ClientFeedService;
};
});
Controller.js
app.controller('ClientsFeedCtrl', function($scope, $stateParams, ClientFeedService) {
var clientId = $stateParams.clientId;
$scope.clientFeed = new ClientFeedService(clientId).getFeedItems();
});
There are a couple of ways that you can solve this. First, let's look at what is happening.
You are assigning the initial array to $scope.cliendFeed. After this, as data is added, a new Array is being generated and stored in the Service, but you still have a reference to the original Array. So ultimately, what you want to do is find a way to keep $scope.clientFeed in sync with your service.
The simplest solution is probably to use a getter method instead of storing a reference to the array in your scope.
In order to do this, you would have to add something like this:
var service = new ClientFeedService(clientId);
$scope.getClientFeed = function () {
return service.getFeedItems();
};
And make sure your ng-repeat called this function:
<li ng-repeat="item in getClientFeed()">...</li>
Hope that helps!
You can push the new data returned from API to the same array in the controller and then apply the $filter
Here is example
function getData(){
$scope.array.push(returnData);
sortArrayList($scope.orderByField, $scope.reverseSort);
}
function sortArrayList(orderByField, reverseSort){
$scope.array = $filter('orderBy')($scope.array, orderByField, reverseSort);
}
I have following knockout.js mapping code:
//JSON string
var startData= {"Name":"SMT","Price":{"Value":22,"Currency":"EUR"}}";
//convert json to model
var viewModel = ko.viewmodel.fromModel(startData);
viewModel.deletePrice= function() {
delete this.Price;
};
ko.applyBindings(viewModel);
Then, I have following template that shows Price nested object on the page:
<script type="text/html" id="PriceTemplate">
//render Value and Currency properties from nested object Price
</script>
Then in my code, I bind Price object to the teplate PriceTemplate - and this all works OK.
<div data-bind="template: { name: 'PriceTemplate', data: Price, templateOptions: { title: 'Prc'} }"></div>
Delete Price
The problem is this function deletePrice(). When it is called, it deletes the nested object Price, but the template is still rendered on the page with the initial data.
My question - how can I delete Price nested object and at same time remove rendered template?
I just found a solution (using ViewModel knockout extension):
var viewModel = ko.viewmodel.fromModel(startData,{
extend:{
"{root}.Price":function(obj){
return typeof(obj) == "object" ? ko.observable(obj) : ko.observable(null);
}
}
});
viewModel.deletePrice = function() {
this.Price(null);
};
You're deleting the property from the object which is not an observable event. Is there any reason you can't set the value of price as an observable this.Price(null)? This would trigger the observable change necessary to update the UI.