AngularJS - Filtering by multiple checkbox groups - angularjs

I'm looking for a bit of advice on what's the best way to go about this:
I have about 5 groups of checkboxes. The list would be something like brands, model, colour, size and so on. As a user ticks off a brands, colours etc a list of cars get updated based on their selections.
The list is generated from a call to the database, I know it would be fairly straightforward if the list was generated once as a list that could be filtered entirely on the frontend but the DB is expected to grow to over 10,000 rows so that wouldn't be ideal.
My thinking at first was to post to the DB each time a checkbox is ticked and return the result to the view. This works fine for one set of checkboxes but I can't get my head around how to do it with multiple checkbox groups.
Here's the function in my controller that handles that:
$scope.getSelectedBrands = function() {
$scope.brandsselected = $filter('filter')($scope.brands, {checked: true});
var senddata = $filter('filter')($scope.brands, {checked: true});
$http.post('/brands', senddata).success(function(data, status, response) {
$scope.cars = data;
});
}
I don't know if using a different but very similar function for each checkbox group would make too much sense and doesn't seem very DRY. At the moment each checkbox group e.g brand would have it's own URL to post to to return the set of results. Also since a checkbox in any of the checkbox groups could be ticked first the initial data would be returned from a different function depending on what checkbox was checked.
Is there a better way to approach this? I also would need to allow users to uncheck a checkbox and repopulate the list again.
Hopefully that makes sense.
Thanks

OK I think I was approaching it the wrong way. All I needed to do was to create a function like the below and fire it whenever a checkbox is checked. It just gets call the form data.
$scope.getFormdata = function(){
var formdata = [{
brands : $filter('filter')($scope.brands, {checked: true}),
types : $filter('filter')($scope.types, {checked: true})
}];
console.log(formdata);
}
Very straightforward.

Related

ExtJs remote (list) filter reset

I have an issue with Extjs remote filter that I excpect you could help to clarify.
I've created a grid with a remote filter that works great, but if I update the grid info, the filter keeps the previous loaded data.
I've tried everything: store.doDestroy, store.removeAll, even assign a new store to the var with Ex.create, but I didn't succeed.
I've created a Fiddle to try to reproduce my issue:
First load default info (Simpsons)
Then open the 'Hobbie' filter (Simulates a select distinct). You get Jazz, Skate, Beer and Knit.
After that, update the grid data (Switch to Van Houtens)
Finally, try to get Van Houtens hobbies (Videogames, Margaritas and None), but you get Simpsons Hobbies (Jazz, Skate..), cause the filter was previously loaded. (Notice how there was not a loading mask).
Now restart the test skipping step 2 (and 5 to avoid infinite loop XD) and notice how the right hobbies are shown.
I need to 'reset' that previous store load.
Notes:
If I just do a store.load, the request is triggered, but the returned values are not bound to the filter list.
If try to bind the store with this bind:{store:'{filterStore}'}, noting happens
You just need to "rebind" the filters for each column like this:
var grid = this.up('grid');
//Clear the current filters before changing
grid.getPlugins()[0].clearFilters();
var store = grid.getStore();
setStoreFilter(store, 'Van Houten');
//Setting the store filters is not enough
setStoreFilter(filterStore, 'Van Houten');
setStoreFilter(hobbiesStore, 'Van Houten');
//You need to "rebind" the store, it needs a little delay to work properly
setTimeout(function(){ grid.getColumns()[3].filter.bindMenuStore(hobbiesStore); }, 1000);
Here is the updated FIDDLE

2 Dropdowns, 1 object ids, other with lists from lists in objects

I've got two multiselect dropdowns on a page that uses angularJS. The first is populated from a service call which returns a lists of objects which are basically category objects. The second dropdown is a list of all the specifics from all those categories. Let's call them Groups and Items for simplicity. Originally, I just populated the second list with a second api call which returned a list of those Items. But since the Group objects already contain a list of the Items which are related, this seems unnecessary. (And I'd like to be able to tie the two together, so that if a group is selected, only the items which fall into that group are displayed in the second dropdown, but that's secondary...) My first dropdown get's populated with this code:
$scope.promiseGetGroupNames = providerContactInfoService.getGroupNames();
$scope.promiseGetGroupNames.then(function (pl) {
$scope.GroupDDLdata = pl.data;
}, function (errorPl) {
//error message...
});
So I would assume that in there I could loop through the data and generate a list there, but it seems like angular would have a simpler way of doing things... Is there a way that I can set the options of the second dropdown to be a complete list of items from all of the groups in the first, and then when a user selects one of those groups, remove all but the items which fall into that category?
Additionally, I have researched this, and found some answers that looked like there was a way to connect the ng-options of 2nd dropdown to the ng-model of the first, which seems like what I want to do, at least for the 2nd half, but I still need to get all options in there at page load...
The other idea I had was to simply make two service calls... the first pulls all of the possible items, the 2nd would pull a subset that only falls into the group select... since only one group is ever allowed to be selected, it's not terribly inefficient, but if the person selects one group, then a different one... seems like that would all be unnecessary if it's possible to link all the objects/options/models together...
What is the best practice for this type of scenario, and if it's the first, could someone explain a little about how to set up that connection? I understand the basics of pulling the possible options from the model, but not exactly sure on the syntax (I'm relatively new to angularJS.)
How about something like this ?
1) Populate your first dropdown list :
<select ng-options="groups" ng-model="selectedGroup">
$scope.init = function () {
service.getGroups.then(function (groups) {
$scope.groups = groups;
});
};
$scope.init();
2) Use ng-change directive on your first dropdown list :
<select ng-options="groups" ng-change="fillSecondList()" ng-model="selectedGroup">
<select ng-options="secondList" ng-model="selectedThing">
$scope.fillSecondList = function () {
// Whatever you want, you can use $scope.selectedGroup here
$scope.secondList = ...
};
$scope.fillSecondList will fire when your selection changes so you can update $scope.secondList according to what's selected in your first list ($scope.selectedGroup), for example using a service call using selectedGroupas a parameter.
EDIT :
If all your needed data is fetched from the first service call, you can use a filter, as you don't actually need to modify your secondList model but rather change what is displayed in your second list.
$scope.init = function () {
service.getGroups.then(function (groups) {
$scope.groups = groups;
// Extract the second list from the first one
$scope.secondList = groups.doSomething();
});
};
<select ng-options="secondList | filter: { group : selectedGroup.id }" ng-model="selectedThing">
For example, this will display only secondList items which have a group property equal to selectedGroup.id.
You can use a custom filter if your filtering is more complicated, or a filter function in your controller (triggered with ng-change) if you don't plan to re-use this filter anywhere else :
$scope.filterSecondList = function () {
$scope.secondList = $scope.secondList.filter(function (item) {
return item.group === $scope.selectedGroup.id;
});
};

Angular CRUD, update view when backend/database changes ($resource and REST)

I am currently making an application in angular which does this:
(On page load) Make an api call in angular controller (to symfony2 end point) to get: items.
$scope.items = ItemsService.query(function(data){
$scope.loading = false;
}, function(err){
$scope.loading = false;
});
items is an array containing many item objects.
Each item contains parameters e.g. item.param1 item.param2.
I have built it in a similar way to this tutorial:
http://www.sitepoint.com/creating-crud-app-minutes-angulars-resource/
i.e. The angular controller calls a service which calls the (symfony2) backend api endpoint.
The endpoint passes back items which is gets from a database. Items are then put into the view using ng-repeat (item in items).
This all works fine.
Now, I have a button (in the ng-repeat) which effectively causes a PUT request to be made to (another symfony2 endpoint), thus updating item.param1in the database. This also happens in the tutorial I linked to.
The problem is that (in my application and in the tutorial) I have to again make an api call which updates ALL the items, in order to see the change.
I want to just update the data in the view (immediately) for one object without having to fetch them all again.
i.e. something like:
$scope.items[4] = Items.get({id: item.id}, function(){});
Except the application array key isn't known so I cant do that.
(so something like: $scope.items.findTheOriginalItem(item) = Items.get({id: item.id}, function(){});.
Another possible solution (which seems like it may be the best?). I have looked here:
http://teropa.info/blog/2014/01/26/the-three-watch-depths-of-angularjs.html
And tried doing the equality $watch: $scope.$watch(…, …, true);. Thus using watch to see when the item sub-array is updated. This doesn't seem to update the data in the view though (even though it is updating in the database).
Any advice on the best way of doing this (and how to do it) would be great! Thanks!
Essentially the button click should use ng-click to execute a function and pass the item to that function. Example:
...ng-repeat="item in items"...
<button type="button" ng-click="updateItem(item)">Update</button
...
Then in the function you have the exact item that you want to update. If you are using $resources, it would be something like:
$scope.updateItem = function(item) { item.$update(...); };
Unless I didn't understand you

angularjs - streamline form (automatic) submission based on dirty scope

Problem space
I have a problem where I'm submitting a form based on criteria being fulfilled, rather than having a form submission button.
Let's say I have 3 drop downs, the first two are grouped but one needs to be selected, meaning I can select one or the other but I can't leave them empty, the 3rd one is a required field.
After that, the page automatically fetches in results.
Lets say I have checkboxes and a few more dropdowns. Any future selections on the 3 dropdowns mentioned, checkboxes, and dropdowns automatically filters the results.
What I know
Now after reading angular documentation, I was checking up on $dirty, $pristine and operations on both, like $setDirty and $setPristine; however, it seems that this is for a FormController
So I'm assuming this is useful for an entire scope. I didn't see any inclination that I can figure out for selected scopes.
What I have so far
So basically, I was hoping that I'd be making use of the scope's tracking features, but I don't know much about it. I created a single controller for my application and a single scope, since that's what seemed easiest for me. I have 3rd party plugins that play a role into the scope like:
$scope.3rdpartyConfig = {
prop1: [],
prop2: getData()
}
I don't think something like that would be useful in checking to see form submission if I was going to check the $dirty state of my form.
Then I thought about the old way I used to do things, but "angularlizing" it:
so I'd have something like:
<input type="checkbox" ng-model="state.Checked" ng-change="checkIfWeCanSubmitThenSubmit()" id="ng-change-example1" />
So I'd be having ng-changes and ng-clicks all over my html form, hitting that function, where the function would look like this pseudocode:
$scope.checkIfWeCanSubmitThenSubmit= function() {
var validated = false;
//check to see if dropdown1 or dropdown2 are selected
//check to see if dropdown3 is selected
// add more here per requirement
//if the above are true, then validated = true
if (validated)
{
//add dropdown4 and 5, and checkbox groups into filter
}
submit();
}
But I was thinking this isn't the angular way of doing things since this certainly isn't facilitated.
I was hoping that the scope would offer some kind of way, where I can check to see what pieces of my scope is dirty or not before I can submit and fetch data, or if there is a better way than appending this function to every html element; like having some kind of scope tracker that I can check up on and watch.
Which reminds me, I don't want to have a series of $scope.$watch either, its just that it'd be way too much work to bind to every piece of html code, unless there's way to watch the scope of a collection of specific scope variables, then, I wouldn't mind.
like (forgive the pseudocode):
$scope.$watch('dropdown1, dropdown2, dropdown4', function(dirty, pristine)
{
if (dirty)
{ blah blah blah }
});
Edit (2/28/2013):
I tried doing it this way:
$scope.masterCriteria =
[
{ DropDown1: $scope.AppModel.Dropdown1},
{ DropDown2: $scope.AppModel.Dropdown2 },
{ DropDown3: $scope.AppModel.Dropdown3 },
{ Checkbox1: $scope.AppModel.Checkbox1 },
{ Checkbox2: $scope.AppModel.Checkbox2 }
];
$scope.$watch('masterCriteria', function (newVal) {
if (newVal) { logger.info("did I change?"); }
}, true);
The watcher detected nothing, and any values I changed to the scope of AppModel wasn't being picked up in the $watch. Was worth a try, still trying to figure this out.
You can slightly change your model and group fields related to input form together. Put them into single object. Like this:
$scope.state = { checkbox1: false, checkbox2: true, ... }
Later bind input boxes to field of state object:
<input ng-model="state.checkbox1" ... >
And watch state object to catch all updates of nested fields:
$scope.$watch('state', ...
JsFiddle example here

knockout checkbox binding selected value

I am learning knockout and was trying to build a page that will build a list of selectable users.
JSFiddle: http://jsfiddle.net/Just/XtzJk/3/ (I am unable to get the data assignment right).
The data assignment is working in my page as I make a call to Controller, like below and it binds to the controls as expected
$.getJSON("/Wizard/GetUsers",function(allData){
var mappedUsers = $.map(allData.AllUsers, function(item){return new User(item)});
self.AllUsers(mappedUsers);
if(allData.SelectedUsers != null){
var mappedSelectedUsers = $.map(allData.SelectedUsers, function(item){return new User(item)});
self.SelectedUsers(mappedSelectedUsers);}
});
Problems:
a.) What's wrong with the JSFiddle I wrote? Got it working.
b.) In my code I am able to get the function for selected checkbox invoked but I am unable to get the value stored in the "User" parameter that I receive in the function. In Chrome JS console I can see the user object has the right value stored, I just am unable to retrieve it. Got this by doing ko.toJS().
Thanks.
EDIT:
Ok, I got my JSFiddle working, I had to select Knockout.js in the framework. The updated fiddle: http://jsfiddle.net/Just/XtzJk/5/
Also, for getting the selected checkboxe's value I did
ko.toJS(user).userName
But I think I'll take the approach of selecting values from a list and then on click move them to another "Selected" list and remove the values from the previous ones. Got this idea from this post: KnockoutJS: How to add one observableArray to another?
OK, I think I've got the solution you need...
I started by setting up an observable array of selectedUserNames, and I applied this to the <li> elements like this:
<input type="checkbox"
name="checkedUser"
data-bind="value: userName, checked:$root.selectedUserNames" />
[Note: it's important to declare the value before declaring the checked binding, which threw me for a bit… ya learn something new every day!]
Why bind an array of userName values to the checked binding? Well, when an array is passed to the checked binding, KO will compare the value of each checkbox to the values in the checked array and check any checkbox where its value is in that array. (Probably explained better in the KO documentation)
Then, while I left the observableArray for SelectedUsers, I set up a manual subscription to populate it, like so:
self.selectedUserNames.subscribe(function(newValue) {
var newSelectedUserNames = newValue;
var newSelectedUsers = [];
ko.utils.arrayForEach(newSelectedUserNames, function(userName) {
var selectedUser = ko.utils.arrayFirst(self.AllUsers(), function(user) {
return (user.userName() === userName);
});
newSelectedUsers.push(selectedUser);
});
self.SelectedUsers(newSelectedUsers);
});
[I had originally tried to set up a dependent observable (ko.computed) for selectedUserNames with functions for both read and write, but the checkbox wasn't having it.]
This subscription function examines the new selectedUserNames array, looks up the user from AllUsers whose userName matches a value in that selectedUserNames array, and pushes matching User objects to the SelectedUsers array… well, actually it pushes each matching User to a temp array and then that temp array is assigned to SelectedUsers, but the goal is met. The SelectedUsers array will now always contain what we want it to contain.
Oh, I almost forgot… here's the fiddle I created, so you've got the full solution: http://jsfiddle.net/jimmym715/G2hxP/
Hope this helps, but let me know if you have any questions

Resources