How to correlate view order (using orderBy) with model order - angularjs

I have an obviously simple but yet challenging problem in my AngularJS app:
When using orderBy on the view I loose the correlation between the order of the related model and the view order.
What I want to do: My view is a table. I want to set a highlight class for the selected row and I want to use cursor up/down keys to move this highlight.
I tried to decouple $index from the tracking by using track by currentDocument.objectid, but that doesn't seem to work.
How can I correlate the currently highlighted row in the view with the currently highlighted element of the related model in a way that I can use -1 or +1 with the cursor keys?

As the order of the data is important to your business process, it makes sense to make the order part of your actual model. If you read the last example of orderby on the angular api (https://docs.angularjs.org/api/ng/filter/orderBy), you will notice they achieve this very thing. Basically, instead of binding orderby directly to some model value, create your own order function and manually order your model.
I have created a plunker here: http://plnkr.co/edit/oM97ZTAIHTjI6YFiGDwI?p=preview Just click on the persons name to see the example (and reorder the list and try again).
Basically you create a manual sort function like this:
$scope.order = function(predicate, reverse) {
$scope.friends = orderBy($scope.friends, predicate, reverse);
};
Which actually reorders the model itself, rather than just the view array. You can call it however you like. Then you can simply pass $index from the view, and it will correlate correctly to the view order.

Related

Need a way to query MongoDB collection, and ngRepeat beginning at specific point

is there a way to query a MongoDB collection, return the results, use an AngularJS ng-repeat to iterate through the results, BUT BEGIN the iteration at a specific position in the results, somewhere in the middle for example?
I am currently returning a query to an angular view; a category of materials. Then i have my view set up to paginate(ng-repeat) through the results. However, no matter what material I click on to bring me into the view (from a different view), the ng-repeat always starts at the beginning of the materials list, rather than on the material i clicked. Any thoughts?
I do not believe the mongodb part has anything to do with the actual question but ill give you 2 options:
The quick way:
<div ng-repeat="item in items" ng-show="$index >= myCtrl.startPoint">
While this will do the trick you might want to do the ng-show expression with a filter. In addition you might run into performance issues. You should also use the track by feature.
A better way would be to have a filteredData model for the ng-repeat and do the correct filtering once per action. You would have to make sure yourself that the model is updated on every data change and every user input.
There are many choices in between these 2 options but what to use depends on your needs. For instance - does user input change frequently? Is your data updated frequently? etc.

Sorting and Filtering a collection in a Paged ListBox

I have a database with 10,000 items, to which you can add and remove while the app is running.
I have a ListBox that displays at most 100 items, and supports paging.
You can filter and sort on the 10,000 items, which needs to be immediately reflected in the listbox.
I have a button that randomly selects an item as long as it passes the filters.
What is the best set of collections/views to use for this kind of operation?
So far, my first step will be to create an ObservableCollection of ALL items in the database which we will call MainOC.
Then create a List of all items that match the filter by parsing MainOC which we will call FilteredList.
Then create a ListCollectionView based on the above List that holds the first 100 items.
CONS:
You have to recreate the ListCollectionView every time a sort operation is applied.
You have to recreate the ListCollectionView every time you page.
You have to recreate the ListCollectionView every time a filter is changed.
You have to recreate the ListCollectionView every time an item is added or removed to MainOC.
Is there a better approach that I am missing?
For example, I see that you can apply filters to a ListCollectionView. Should I populate my ListCollectionView with all 10,000 items? But then how can I limit how many items my ListBox is displaying?
Should I be doing my filtering and sorting directly against the database? I could build FilteredList directly off the database and create my ListCollectionView based off that, but this still has all the cons listed above.
Looking for any input you can provide!
This is a problem which is easily solved using DynamicData . Dynamic data is based on rx so if you are not familiar with the wonderful Rx I suggest you start learning it. There is quite a bit of a learning curve but but the rewards are huge.
Anyway back to my answer, the starting point of dynamic data is to get some data into a cache which is constructed with a key as follows
var myCache = new SourceCache<MyObject, MyId>(myobject=>myobject.Id)
Obviously being a cache there are methods to add, update and remove so I will not show those here.
Dynamic data provides a load of extensions and some controllers to dynamically interrogate the data. For paging we need a few elements to solve this problem
//this is an extension of observable collection optimised for dynamic data
var collection = new ObservableCollectionExtended<MyObject>();
//these controllers enable dynamically changing filter, sort and page
var pageController = new PageController();
var filterController = new FilterController<T>();
var sortController = new SortController<T>();
Create a stream of data using these controllers and bind the result to the collection like this.
var mySubscription = myCache.Connect()
.Filter(filterController)
.Sort(sortController)
.Page(pageController)
.ObserveOnDispatcher() //ensure we are on the UI thread
.Bind(collection)
.Subscribe() //nothing happens until we subscribe.
At any time you can change the parameters of the controllers to filter, sort, page and bind the data like follows
//to change page
pageController.Change(new PageRequest(1,100));
//to change filter
filterController.Change(myobject=> //return a predicate);
//to change sort
sortController .Change( //return an IComparable<>);
And as if by magic the observable collection will self-maintain when any of the controller parameters change or when any of the data changes.
The only thing you now have to consider is the code you need for loading the database data into the cache.
In the near future I will create a working example of this functionality.
For more info on dynamic data see
Dynamic data on Github
Wpf demo app

How to save child properties?

Breeze & Angular & MV*
I get an invoice object and expand it's necessary properties: Customer, Details, etc.
To access detail properties is easy, invoice.detail[n].property. And saving changes to existing properties (1 - n) is also easy. In my UI, I simply loop through my object vm.invoice.details to get & display all existing details, bind them to inputs, edit at will, call saveChanges(), done!
(keep in mind, in this UI, I need to complete the following too....)
Now, I have blank inputs for a new detail I need to insert.
However, I need to insert a new detail into the existing array of invoice details.
For example: invoice #5 has 3 details (detail[0], detail[1], detail[2]). I need to insert into this existing invoice, detail[3], and call saveChanges()
I've tried to call the manger.createEntity('invoice') but it complains about FK constraints. I know you can pass values as a second argument in createEntity('obj', newvalues)...but is that the correct and only method?
Seems like this should all be much easier but, well, I am at a loss so, please help where you can. TIA!
Take a look at the DocCode sample which has tests for all kinds of scenarios including this one.
Perhaps the following provides the insight you're looking for:
function addNewDetail() {
var newDetail = manager.createEntity('Detail', {
invoice: vm.currentInvoice,
... other initial values
});
// the newDetail will show up automatically if the view is bound to vm.details
}
Notice that I'm initializing the parent invoice navigation property. Alternatively, I could just set the Detail entity's FK property inside the initializer:
...
invoiceId: vm.currentInvoice.id,
...
Either way, Breeze will add the new detail to the details collection of the currentInvoice.
Your question spoke in terms of inserting the new Detail. There is no need to insert the new Detail manually and you can't manage the sort order of the vm.currentInvoice.details property any way.
Breeze has no notion of sort order for collection navigation properties.
If you need to display the details in a particular order you could add a sorting filter to your angular binding to vm.currentInvoice.details.
Make sure you have correct EntityName, because sometimes creating entity is a not as simple as it seems.Before working with entities see
http://www.getbreezenow.com/documentation/creating-entities
I will suggest you to look ur metadata file, go to last line of your file, you can see the field named "entitySet"
"entitySet":{"name":"Entity_Name","entityType":"Self.Entity_Name"}
check the entityName here i took as "Entity_Name" and then try to create the entity and use this name
manger.createEntity('Entity_Name');

Using filtering, ordering, and pagination together efficiently with AngularJS

I have been implementing the ability to filter a table based on different fields and at first it seemed fairly straight forward, but now things are getting more complex and I want to make sure I am utilizing Angular's Digest cycle effectively.
Right now, when the user first hits a view and the controller gets instantiated, I call the API and GET an array of data from the server. I call this data a dataSet. This is the raw, unfiltered array of data.
Then on the page there are two different ways to filter, a search bar (<input>) and an actual filter menu where the user can toggle different values On/Off and have the table update accordingly in real-time. At first I thought it would be simple enough to place the filter logic inline inside of ng-repeat using a pipe, however this doesn't work with my pagination because the pagination continues to reflect the dataSet and not the filtered data. This led my HTML to look like
<tr ng-repeat="mailbox in model.IndividualMailboxes.filtered | orderBy:model.IndividualMailboxes.ordering.predicate:model.IndividualMailboxes.ordering.reverse | paginate:(model.IndividualMailboxes.currentPage - 1) * model.IndividualMailboxes.resultsPerPage | limitTo:model.IndividualMailboxes.resultsPerPage">
This then led me to creating a second array that I call filtered. I basically filter the dataSet array into this new filtered array. Then my ng-repeat and pagination both use the filtered set. This also forced me to place the actual filter in the controller and use filterFilter(arr, term) to filter the dataSet in 2 stages
function filterIndividualMailboxes() {
var verifiedList = [], searchList = [];
verifiedList = filterFilter($scope.model.IndividualMailboxes.dataSet, { Valid : $scope.model.IndividualMailboxes.showVerified });
searchList = filterFilter(verifiedList, $scope.model.IndividualMailboxes.search);
$scope.model.IndividualMailboxes.filtered = searchList;
}
The above function simply filters the dataSet by our filtering options (there is only 1 in this case) and then takes that set and filters it based on the search term that the user can input.
The problem with this implementation is it doesn't seem to fit into the digest cycle efficiently as I am having to call filterIndividualMailboxes() whenever the data is changed in any way. The easiest way to implement that was to just $watch() the entire model like this
$scope.$watch('model.IndividualMailboxes', function(){
filterIndividualMailboxes();
}, true);
But I am afraid that this isn't very efficient and not the most fluid way to deal with filtering in Angular.
Does anyone know of a more "Angular" way of doing this that fits right into the digest cycle?

How to use getById? (ExtJS 4.1)

I want to get the raw value of a numberfield by looking up its id. Here the raw value is 4 and the id of the numberfield is satirSayisi.
I have tried several combinations but non has worked.
grid.header.getById('satirSayisi').getRawValue()
Ext.getCmp('satirSayisi').getRawValue()
Your example is a little short on detail, but I think I've got the idea. You have a grid, and you want to 1) get the selection, 2) get the model from this selection so that you can 3) get the value of a number field (satirSayisi). I'm assuming satirSayisi is the column name in the model, and not necessarily the id or itemId property.
Assuming that you have a handle on the grid, you grab a model from the selection like so:
var model = grid.getSelectionModel().getSelection();
Now you have a model instance (which should correspond to your database columns in some way). To get the value from satirSayisi, you would say:
var stuff = model.get('satirSayisi');
Accessing components by Id in ExtJS is no longer recommended. They suggest the use of itemId over id. A better way to get access to an object is ComponentQuery, such as...
component = Ext.ComponentQuery.query('button#Save')[0];
This example will give you a handle on the first button type object in your application with the itemId of Save. It will always grab stuff in an array, even when there is just one result.
Good luck!

Resources