How do I rearrange the order of a collection in Firestore using a reference that I already have?

Is there a method to maintain a reference of the same collection while altering the ordering using firestore?

TLDR: I am looking to achieve similar functionality as demonstrated in: , but since my data is sourced from firestore, I am struggling to find the right approach to accomplish this dynamically.


Imagine I have a messagesService.ts file which holds a collection of messages and an Observable reference to these messages:

messagesCollection: AngularFirestoreCollection<Message>
messages: Observable<Message[]>;

this.messagesCollection = this.db.collection('messages', ref => {
  return ref.orderBy('dateCreated', 'desc');
});

this.messages= this.messagesCollection.valueChanges();

When this data is displayed using

*ngFor="let message of messages | async"
, it showcases the latest messages at the top, just as expected.

Assistance Required:

I would like to implement a feature where users can click a button to change the order of messages (or the order of retrieved data from firestore). For instance, sorting messages by most likes, highest views, oldest messages, alphabetical order, etc. Do I need a separate reference for each sorting criteria? This approach seems clunky, so is there a more efficient way to accomplish this dynamically?

Initially, I attempted something like this:

sortMessagesBy(field) {
    this.messagesCollection = this.db.collection('messages', ref => {
          return ref.orderBy(field, 'desc');
    });
}

However, this did not work well because it appeared to modify the reference to the collection, resulting in the messages observable not being updated.

So, I tried another approach:

sortMessagesBy(field) {
    this.messagesCollection = this.db.collection('messages', ref => {
          return ref.orderBy(field, 'desc');
    });
    this.messages= this.messagesCollection.valueChanges();
    return this.messages;
}

But this creates a new this.messages object, causing ngFor to re-render the entire page, making it appear messy (even with trackBy implemented).

In the long run, I aim to delve deeper within the same collection, possibly incorporating a where clause to target specific subsets of messages while maintaining dynamic sorting capabilities, e.g.:

this.messagesCollection = this.db.collection('messages', ref => {
      return ref.where("type", "==", "todo").orderBy(field, 'desc');
});

It seems challenging to achieve this without solving the dynamic sorting puzzle first.

Answer №1

It appears that the AngularFirestoreCollection class is immutable, meaning it cannot be altered once it has been created.

This aligns with the behavior of Firebase SDK classes. The following code snippet

ref.where("type", "==", "todo").orderBy(field, 'desc')

Generates a Query object, which is also immutable. To modify the result ordering, you will need to create a new query and a fresh AngularFirestoreCollection.

Answer №2

After gaining insights from @Frank van Puffelen's explanation, I was able to devise a solution for the problem at hand. The following code snippet showcases my approach:

getMessages(type=null, field=null, direction=null) {
  console.log("sorting messages by: ", type, field, direction);
  this.messagesCollection = this.db.collection('messages', ref => {
    if (type && field) {
      return ref.where('typeId', '==', type).orderBy(field, direction? direction : 'desc');
    } else if (type && !field && !direction) {
      return ref.where('typeId', '==', type)
    } else if (!type && field) {
      return ref.orderBy(field, direction? direction : 'desc');
    } else {
      return ref;
    }
  });
  this.messages = this.messagesCollection.valueChanges();
  return this.messages;
}

This method eliminates the need for separate variables in my service for each query scenario, simplifying the process. Now, within any component, I can utilize calls like below:

this.messages = this.messageService.getMessages(this.type, 'views');

or like this:

this.messages = this.messageService.getMessages(null, 'dateCreated');

A Point to Note: Creating composite indexes on the typeId field and distinct fields intended for ordering (such as views, dateCreated, likes, etc) was necessary. Fortunately, creating these indexes in Firebase proved to be straightforward.

However, an issue arises when the page flickers upon calling this function, replacing the value in this.messages which is utilized in an *ngFor loop. It seems that the entire page re-renders even with trackBy implemented.

Update:

To address the rendering issue, subscribing to the messages within the component instead of using the async pipe in ngFor yielded positive results. Below is an example taken from MessageComponent.ts:

this.messageService.getMessages(this.type).subscribe(res => this.messages = res);  

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Here's a guide on customizing the appearance of the date picker in NativeBase components for React Native by

Is there a way to show icons while using the date picker component from the Native Base UI library? ...

Exploring solutions for handling asynchronous issues with vue3-google-map

While working with a Vue library for managing Maps called vue3-google-map, I encountered an issue when trying to define certain polylines that would not allow me to select the center of the marked area: Here is my map template: <template> <Goo ...

Using the clientWidth property in React

While I have a solid background in Javascript, I am relatively new to working with React. In my previous projects where I coded directly in javascript for the browser, I frequently used the following code snippet: width = document.getElementById('elem ...

Having trouble with a JavaScript function as a novice coder

Hello, I'm still getting the hang of JavaScript - just a few days into learning it. I can't figure out why this function I'm calling isn't functioning as expected. Here's the content of my HTML page: <!doctype html> <htm ...

Navigating the Angular Controller life cycle

I have set up my application states using ui-router: $stateProvider .state('app', { abstract: true, views: { 'nav@': { templateUrl: 'app/navbar.html', controller: 'NavbarController' ...

Tips for creating a customized dropdown menu that opens upwards without relying on Bootstrap

Looking for some assistance with creating an animated dropdown menu that slides upwards. Any help is appreciated! $('.need-help').click(function() { $('.need_help_dropdown').slideToggle(); }); .need-help { background:url(../im ...

Despite encountering Error 404 with AJAX XHR, the request is successfully reaching the Spring Controller

I am attempting to upload a file with a progress bar feature. var fileInput = document.getElementById('jquery-ajax-single') var form = new FormData(); form.append('uploadFile',fileInput.files[0]); $.ajax({ url: "fi ...

What are the best techniques for creating animations in AngularJS?

I've been trying to figure out how to animate in AngularJS by searching online, but I haven't found a perfect solution yet. html <span class="sb-arrow down" ng-click="hideSampleList($event)"></span> ...

A guide on crafting a test scenario for an AngularJS controller using the Jasmine framework

I recently created an angular module called userModule.js 'use strict'; angular.module('users', ['ngRoute','angular-growl','textAngular','ngMaterial','ngMessages','ngImgCrop', ...

Automatically tapping the Quora "expand" button through code

I am attempting to automate the clicking of the "more" button located at the bottom of a page like using watir. Below is the code I currently have: require 'watir-webdriver' b = Watir::Browser.new b.goto 'quora.com/'+ ARGV[2] + ' ...

Implementing HTML content inside an el-select component in Vue.js using Element UI

I am working with a dropdown feature that has various options: var Main = { data() { return { drawer: { form: { period: null } }, data : [ { label: "JAN to MAR 2021", value: " ...

The console is displaying a null value outside of the block, however, the correct value is returned when

I am having an issue where the first console.log() is not returning the correct value, but the second one is. Can anyone provide assistance with this problem? angular.module('resultsApp', ['ngCookies']) .config(['$qProvider&a ...

Dismiss the Popover in Ionic 2

After opening a popover that redirects me to another page and then returning to the root page (popToRoot), I reload the data/dom upon an event and dismiss the popup once the json data is received from the server. Everything works smoothly with a lengthy ti ...

What is the best way to sum up multiple checkbox values in JavaScript?

var bookRate = new Array('The Right to differ', 'Issues In Contemporary Documentary', 'Writing, Directing and Producing', 'Lee Kuan Yew My Lifelong Challenge'); var selection = document.rate.checkbox; var sum = 0.00 ...

AngularJS - retrieving and displaying the selected value from an HTML dropdown menu

Could someone help me figure out why the Land selection is empty when trying to display it as {{ selectCenter.land }}? For reference, here is a functional plunker: http://plnkr.co/edit/Q8jhdJltlh14oBBLeHJ9?p=preview And the relevant code snippet: ...

Troubleshooting base href issues in AngularJS routing

For a demonstration, I decided to try out this plunker from a tutorial that showcases tab routing across different pages. After downloading the entire zip file and running it as is (e.g. with all files in the same directory and utilizing CDN links), I enco ...

Is it possible to execute JavaScript code (using Node.js) through AppleScript on MAC OS?

After downloading Node.js for MAC OS from this link: http://nodejs.org/download/ (http://nodejs.org/dist/v0.10.29/node-v0.10.29.pkg), I needed to execute a JavaScript on Google Chrome. To do this, I utilized the following AppleScript code: do shell script ...

The callback function in AngularJS' $http is failing to trigger

$scope.submitNewUser = function() { $http({ method: 'POST', url: 'api/user/signup', data: {'user': $scope.user}, headers: {'Content-Type': ...

Enhance user experience with jQuery UI sortable and the ability to edit content in real

I am facing an issue with jquery sortable and contenteditable. The problem arises when I try to use jquery sortable along with contenteditable, as the contenteditable feature stops working. After searching on Stack Overflow, I found a solution. However, up ...

Eliminate web address parameter using regular expressions

Looking to remove a specific URL parameter from a given URL. For instance, if the URL is: http://example.com?foo=bar&baz=boo And I want to eliminate foo=bar to get: http://example.com?baz=boo Or removing baz=boo would leave me with: http://exampl ...