Ionic / Firebase - error splice is not a function (reorderArray)

我们两清 提交于 2019-12-24 21:54:59

问题


I have beat my head for a couple days...trying to use the (ionItemReorder)="reorderItems($event)" to reorder a list. I have a list of songs I'm getting from FireBase. When I fire the reOrderItems click event I get an error: TypeError: array.splice is not a function at reorderArray

I assume it's probably something very simple in the way I'm defining "songs". I have tried several different ways...but at this point I'm just grasping at straws.

Any suggestions would be greatly appreciated. Thank you! ER

Typescript:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, reorderArray } from 'ionic-angular';
import { AngularFireModule} from 'angularfire2';
import { AngularFireDatabase } from 'angularfire2/database';

@IonicPage()
@Component({
  selector: 'page-songs',
  templateUrl: 'songs.html',
})
export class SongsPage {

  //songs: any = {};
  //songs = {};
  //songs = [];
  //songs: any = [];
  songs: any;  
  btnName: any = 'Reorder';
  flag: any = false;

  constructor(
    public navCtrl: NavController, 
    public navParams: NavParams,
    public afd: AngularFireDatabase
  ) 
  {    
    this.songs = this.afd.list('/songs/').valueChanges();    
  }

  //Button in navbar to toggle reordering the list of songs
  actionBtn(){
    if (this.btnName == 'Reorder') {
      this.btnName = 'Done';
      this.flag = true;
    }
    else{
      this.btnName = 'Reorder';
      this.flag = false;
    }
  };

  reorderItems(indexes){
    //let element = this.songs[indexes.from];
    //this.songs.splice(indexes.from, 1);
    //this.songs.splice(indexes.to, 0, element);
    this.songs = reorderArray(this.songs, indexes);    
   };

  showChords(song){
    this.navCtrl.push('ChordsPage', song)
  }

}

HTML:

<ion-header>
  <ion-navbar>
    <ion-title>Songlist</ion-title>
    <ion-buttons end>
      <button ion-button small clear (click)="actionBtn();">
        {{btnName}}
      </button>
    </ion-buttons>
  </ion-navbar>
</ion-header>

<ion-content>
    <ion-list reorder="{{flag}}" (ionItemReorder)="reorderItems($event)">
        <ion-item-sliding *ngFor="let song of songs | async ; let i = index">

          <ion-item>
              <h2>{{i+1}}. {{ song.Title}}</h2>
              <p>{{song.Artist}}</p>        
          </ion-item>                

          <ion-item-options side="right">
              <button ion-button (click)="showChords(song)">Chords</button>
            </ion-item-options>

            <ion-item-options side="left">
                <button ion-button color="danger" (click)="removeSong(song)">Delete
                  <ion-icon name="trash"></ion-icon>
                </button>
            </ion-item-options>

              </ion-item-sliding>
      </ion-list>
</ion-content>

回答1:


Subscribe to the Observable and map the items to be pushed into the songs array. Also, use the lifecycle hook ionViewDidLoad instead of the constructor for doing things at initialization.

import { AngularFireDatabase } from 'angularfire2/database';
import { ISubscription } from 'rxjs/Subscription';
import { OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable';

...

export class ... implements OnDestroy {

    songslist: Observable<any[]>;
    subscription: ISubscription;
    songs: any[];

    constructor(...

    ionViewDidLoad() {

        this.songslist = this.afd.list<Item>('songs').valueChanges();
        this.subscription = songslist.subscribe(items => {
            return items.map(item => { this.songs.push(item); });
        });

    }

    ...

    reorderItems(...

    ...

    ngOnDestroy {
        this.subscription.unsubscribe(); // Make sure to unsubscribe, don't leave them open.
    }
} 



回答2:


Trying to accomplish something very similar - except with Firestore instead of the RealTime Database - had me wanting to bang my head against the wall, but finally got it working so hopefully it's helpful to you or others even though some things will be slightly different.

The array.splice is not a function error comes up when you are trying to perform the function on something that isn't an array, in this case the observable which for reasons beyond my understanding seems to be returned as a single object.

To get things working I declared each of these variables separately:

photoCollectionRef:AngularFirestoreCollection < PhotoSlide>; 
photo$: Observable < PhotoSlide [] >; 
photo = {} as PhotoSlide; 
photoArray: any=[];

Then set each value as follows

this.photoCollectionRef = this.afs.collection('posts').doc(this.postId).collection('photos/', ref => ref.orderBy('index'));
this.photo$ = this.photoCollectionRef.valueChanges();

this.photo$.take(1).subscribe((pictures) => {
  pictures.forEach(p=>{
   return  this.photoArray.push({index:p.index, photoURL: p.photoURL, id:p.id })
  })
});

In the template file *ngFor loops over photo of photoArray (without an async pipe) and the reorderItem function called from the item group works perfectly with standard single line syntax from the ionic docs.

Then to update the indexes in the Firestore Collection, I call the below function from a button that's visible once the list order has been modified:

saveNewOrder(){
this.photoArray.forEach((element, i) => {
  this.photoCollectionRef.doc(element.id).update({
    index: i
  });
});

}

I should note that I also always use the .createId() method and then .doc(newId).set({id:newId etc}) when adding new documents rather than just .add because I find it a lot less annoying to do that than be stuck using .snapshotChanges and map function rather than just .valueChanges() - which is why the above functions work with the syntax shown

Also if you don't use the rxjs take(1) operator before the subscription it all looks fine at first but after the saveNewOrder function's executed the list repeats several times




回答3:


I am late getting back to this but here is my attempt at explaining how I got this working.
**Caveat - I am a hobbyist coder so I apologize in advance if I'm explaining using wrong terms or if I just understand it wrong..the code works:).

The goal is to get a list from Firebase using the NEW version of Firebase and pull it into an array so that I can present it to the screen and then use the reorderArray function to keep the array indexes in order after moving items around in a list. So if I have an array of songs...I'll call it songList (think no Firebase yet) and assume this is my HTML:

<ion-list reorder="true" (ionItemReorder)="reorderItems($event)">
    <ion-item *ngFor="let song of songList; let i = index">
    {{i+1}}. {{song.title}} - {{song.artist}}
    </ion-item>
  </ion-list> 

Then I have the standard function to reorder the array:

reorderItems(indexes) {        
    this.songList = reorderArray(this.songList, indexes);    
  };

Add the 'reorderArray' to the import to enable this:

import { NavController, AlertController, reorderArray } from 'ionic-angular';

After looking into this a little I think the reorderArray function does a few .splice commands to get the indexes moved around in the array.

So fast forward to replacing my array with a Firebase list. See all the above code from the 1st post...all that works to get the list to show up on the HTML page. But as soon as the reorderArray is fired I get "splice" errors thrown. As it turns out the Firebase list at this point is an object and the reorderArray expects an array. I got the below code from a Youtube video:

//Set up the songList array
    var x = this.afDatabase.list('/songs');
    x.snapshotChanges().subscribe(item => {
      this.songList = [];
      item.forEach(element => {        
        var y = element.payload.toJSON();
        y["fbKey"] = element.key;
        this.songList.push(y);
      })
    })

I will try to explain as best I can what this is doing. I set x to be a ref to my Firebase list /songs. I invoke snapShotChanges() to get my list values and the key. I then subscribe to the list to walk through the items. I declare setList as an array. I iterate over the list of items and I guess 'payload' is a special property that gets all the object data?? I think I cast all that object data into an array. I add a new field to the list I think so I can get back at the .key value from a field? I then push all that data into an array.

Again I'm not sure how all this magic works but it does. Now I have an array in songList that holds all my data...and now I can use the reorderArray function to keep the indexes straight on the client side after a reorder.

But...new problem. There is no client side representation of that index value out in the Firebase list.

The rest of this code is a little hazy as when things started working I was all over the map and adding lots of stuff to see it work. Right now I'm having Ionic Serve issues and can't get this running right now without deploying it up to Firebase or Ionic View...so I have to go by memory.

So here is what my final HTML looks like:

<ion-list reorder="true" (ionItemReorder)="reorderItems($event)">
    <ion-item *ngFor="let song of songList | orderBy: 'sortOrder'; let i = index">
    {{i+1}}. {{song.title}} - {{song.artist}} ({{song.sortOrder}})
    </ion-item>
  </ion-list>  

The only real difference here is that I have an orderBy on a new field called sortOrder.

Here's how all this works:

reorderItems(indexes) {  
    var luTitle = '';
    var luArtist = '';

    this.songList = reorderArray(this.songList, indexes);

    this.songList.forEach( song => {
      this.afDatabase.database.ref('songs/' + song.fbKey + '/sortOrder').set(this.songList.indexOf(song));

      this.afDatabase.database.ref('songs/' + song.fbKey)
      .on('value', function(snapshot) {
        luTitle = snapshot.child('title').val();
        luArtist = snapshot.child('artist').val();
      })

      console.log("Index: " + this.songList.indexOf(song));      
//      console.log("Title: " + song.title);      
      console.log("LU Title: " + luTitle);      
      console.log("LU Artist: " + luArtist);      
      console.log("FB Key: " + song.fbKey);  
      console.log("Sort Order: " + song.sortOrder); 
    })  
  };

A lot of this is just logging stuff to the console but the real work is this:

The first thing I do is run the reorderArray over the this.songList and it gets all the indexes in the right place on the client side. Then I iterate over all the items in that array and I create a reference to the FB list using the song.fbKey that we set when we converted the initial FB list to an array. Then I .set a sortOrder field for that song equal the the current index of that song as it exists on the client side at that moment in time. Everything else I think after that is me logging stuff to the console to look at values. The LU (Look Up) stuff was just me figuring out how to get a value back in from Firebase.

Now the orderBy in the ngFor immediately orders everything by the sortOrder field that basically comes in real time from FB. I can't remember but I think if the list is brand new from FB and there is no sortOrder field yet it defaults to sorting by the key...which is fine...the first time reorder is fired all the sortOrders get set.

I'm sure there are some bugs I will discover when I get back to this...but it's working code as of now.

Thanks for reading if you made it this far.

ER



来源:https://stackoverflow.com/questions/47400606/ionic-firebase-error-splice-is-not-a-function-reorderarray

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!