Angular JS filters using Firebase data - angularjs

I have some data in firebase that looks like this:
|
|
--users
|
--1
|
--email:"hello#gmail.com"
--name:"User 01"
--2
|
--email:"hello2#gmail.com"
--name:"User 02"
--chat
|
---JU9ZpBj7P9dWgNYN4To
|
--email:"hello#gmail.com"
--content:"Hi, i'm user 01! How are you?"
--user:"1"
--timestamp:"123456789"
---JX8ZpBnli7hliwehlfi
|
--email:"hello2#gmail.com"
--content:"Hi, i'm user 02! I'm great thanks!"
--user:"2"
--timestamp:"123456789"
I get the chat data from firebase in an object called 'messages', and my HTML/angular looks like this:
<ul class="chat" ng-repeat="message in messages | orderByPriority | reverse">
<li>
<strong>{{message.user | uid2name}}</strong> {{message.content}}
</li>
</ul>
So what I want to do is grab message.user and convert it from a userid into a name. I figured a good way would be to use a filter:
.filter('uid2name', function(loginService, $rootScope) {
// Takes a userid and outputs a users name
return function(input) {
var ref = new Firebase('https://<myapp>.firebaseio.com/users/' + input);
return $firebase(ref).name;
};
})
Now this works fine, but it does so by essentially polling firebase multiple times per second - not what I want to do at all. I have thought about caching the response in $rootScope but this seems a bit sloppy. What's the best way going about this? I am open to any ideas, I am not wed to the idea of using a filter.

Filters are one the things I love a bout Angular, but they are too heavy (since they are evaluated with every digest cycle) and definitely does't suite to your approach, unless you use cache (just don't store it in $rootScope, create a cache service instead).
Firebase is all about performance, in most cases normalization is your enemy. You already store user email, why don't you also store user name as well?
Changing the data structure to include userId in the URL of your Chat data structure may help: Data Modeling Best Practices in Firebase/AngularFire

In the end, I solved this problem by using the angular-cache plugin (the default angular cache-factory is good but it lacks a few useful features). I cached the users in order to use a filter without hammering firebase.

Related

Need help structuring an Angular app

I have a basic angular app, users can post Adverts, now I am working on how to search / filter for adverts. I am currently using a filter like so..
<a data-ng-repeat="advert in filtered = (adverts | filter:filterBySearch) | startFrom:(currentPage-1)*entryLimit | limitTo:entryLimit" data-ng-href="#!/adverts/{{advert._id}}" class="list-group-item">
where I have created my own fiter, filterBySearch
I also have a range slider for filtering by a range of prices, and I have all this working with pagination.
My concern is because I have declared <section data-ng-controller="AdvertsController"> at the top of the homepage in order to use the AdvertsController for Advert logic, I am now finding I am putting logic in here around, pagination, the range slider, filtering logic etc. I would like to know how best to split this logic out into their own controllers or whatever it needs.
The controller should not he hadling such complex business logics in itself. It must be used for binding scope elements, handling events, updating states etc. I would suggest pulling your pagination, range slider, filtering logics etc in dedicated angular service and inject the it into your AdvertsController. This way you can also utilize them in any other view/controller you require.
Read more about modularity in AngularJS here
If you have business logic pertaining to elements inside of your tag, then it still makes sense to have all of your code inside of that controller. It might make more sense to rename your Controller to something that better suits your needs.

Is it possible to "angularize" data already in the page? (AngularJS + Drupal)

I have an HTML page that's been generated on the server. It contains data similar to this:
<ul>
<li>Banana</li>
<li>Apple</li>
<li>Pear</li>
</ul>
Is it possible to "angularize" (or "post-compile") such data to obtain the same behavior as if the list had been generated with:
<ul>
<li ng-repeat="item in items">{{item.name}}</li>
</ul>
That way, my list would be sortable, filterable, etc.
Why would I want to do that?! ;-)
I'm using Drupal as my page generator and for multiple reasons I'd like to keep it that way (my content is translatable, themable with Drupal's theme functions, etc.)
Having the initial version of the page fully rendered by the server makes it indexable by search engines. The AngularJS behaviors are mostly just UI enhancements.
I'd like to avoid an additional roundtrip to the server just to re-transfer the same data.
Caveats:
I'm not just asking how to implement the desired behavior with AngularJS + Drupal (I could expose Drupal's data via an endpoint to which AngularJS would send requests). Instead, I'm asking how to recycle data that's already in the HTML to turn it into an AngularJS model (without resorting to ngInit, ideally).
What I am asking breaks the MVC pattern, I know.
In case you're curious, the site is http://ng-workshop.com/ (it's a collection of AngularJS resources and tutorials).
Thanks!
One way to provide angular the data is to echo out the php data to the page as a javascript object above the other included scripts on the page.
$dbResult = ['f1', 'f2', 'f3'];
echo "<script type='text/javascript'> var php_data = " . json_encode($dbResult) . ";</script>";
then somewhere in your angular js code...
$scope.items = php_data || [];
You may want to place the data in a namespaced javascript object to prevent any kind of stomping on.
Example:
PHP -> echo "myApp.page.data =" . json_encode($dbResult) . ";"
Angular -> $scope.items = myApp.page.data || [];

Anngularjs reusing html page for multiple results

I'm creating a test app in angular that takes a date input, and displays weather information from an API request for today and tomorrow.
I've used the a dayTemperatures.html using ng-include to show the results for today, but want to reuse dayTemperatures.html to show the weather information for tomorrow on the same page.
I think using ng-include makes it harder. The Brute force want I think think of is to create another view with similar variable names, but that is probably defeating the purpose. Any ideas on how this works?
Html:
ng-include="'Angular/weatherInfo.html'"
js:
var onWeatherComplete = function (data)
{
$scope.dailyWeatherDetails = data;
}
Please guide me the correct way to achieve my objective.

Angularjs Search in large data set with pagination

currently I use filter:query for searching within the data, something like this
<input type="text" ng-model="query">
<tr ng-repeat="thought in thoughts | filter:query">
<td>thought.title</td>
</tr>
It works well, if I load the complete json data at once.
Now my question is can I achieve server side search along with pagination as I don't want to load the complete data set at once?
One approach : For the current result set search can be performed normally using filters, if no results are found make a call to server requesting the another piece of data. Is this approach good ?
Well, assuming you have a API/CGI on your server which executes the searches and returns a subset (e.g. by using SQLs limit start,number) it shall not be that complicated to achieve this. When you start a new query you would set thoughts to a empty array and then make the first API call, e.g. returning 10 results. And then you could have a button or whatever mechanism to make the next API call, returning result 11-20. In your $http callback function you would then simply always append the data returned by the server to your array, so that new data is added at the end. So think of something like this (this is no actual tested code, just written down for the sake of this answer):
$scope.getdata = function() {
$http.post('/api/whatever',
{ query: $scope.query, startat: $scope.thoughts.length })
.success(function(response,status,headers,config){
$scope.thoughts.push.apply( $scope.thoughts, reponse.data );
});
$scope.search = function() {
$scope.thoughts = [];
$scope.getdata();
}
Search for: <input ng:model="query">
<button ng:click="search()">Search</button>
<button ng:click="getdata()">Get more results</button>
There's not going to be any way to do a client-side search unless you load all the data into the client with your first ajax request.
Server side search is probably gonna be your best bet.

AngularJS pattern for persisting record selection through multiple asynchronous calls?

We're working on CRUD patterns for a fairly large application. One common UI pattern we're using to define a one-to-many relationship where a record is associated by checkbox. The challenge is to persist selections (checked/unchecked) through asynchronous calls (search/sort) that refresh the record list (and the associated ng-model). I'd like to hear from more advanced AngularJS users (I'm a noob) what's considered a best practice for this? Any feedback is appreciated!
EDIT
Here's a working plunk showing how I'd most likely tackle this with my current understanding of Angular. Please let me know if you have a better approach!
I think u could maintain a separate collection of selected names. So the next time you filter the list, you just need to lookup in the collection in order to keep the item selected. That you can do by binding some variable (arrSelected) in the controller or you can create a separate service also.
Have you considered using a filter instead of refetching. Of course this really depends on how many items you plan on having, but I've had success filtering a nested JSON object with around 5000 items.
Here is the full plunker : http://plnkr.co/edit/l4jYgt0LjRoP2H0YuTIT?p=preview
Highlights:
In index.html for your repeat you add | filter
<tr ng-repeat="user in users | filter:userFilter">
In script.js we add the filter function and a $scope variable to hold the filtered letter:
$scope.filteredLetter
$scope.userFilter = function (elem) {
if (elem.name.lastIndexOf($scope.filteredLetter, 0) === 0 || $scope.filteredLetter == '') {
return true;
} else {
return false
}
}
As a bonus I added in ng-class to show which letter was highlighted.
Pretty simple code but this gives you full persistence now even as people change things. You can even experiment now with adding a <input> tag with an ng-model binding to say $scope.filteredName. Then all you need to do is add the JS to the filter to do a deeper filter for part of the name.

Resources