ember.jsember-dataember-modelember-components

How to achieve sort functionality in Ember 2.1.8 for Async=true relationships


I have following computed properties, tempate and a model where there are relationships which are async=true

ArrayController is used to sort the people-cards 1st after upgrading to 2.1.8 its not working. how to achieve this ?

computed properties

 people: computed(
    'model.coordinators',
    'model.directors',
    'model.professors',
    'model.students',
    'model.advisors',
    'model.managers',
    'model.contacts',
    'model.consultants',
    'model.guests',


    function() {

      console.log("people......")

      var coordinators = this.get( 'model.coordinators' ),
          professors   = this.get( 'model.professors' ),
          guests       = this.get( 'model.guests' ),
          students     = this.get( 'model.students' ),
          advisors     = this.get( 'model.advisors' ),
          directors    = this.get( 'model.directors' ),
          consultants  = this.get( 'model.consultants' ),
          contacts     = this.get( 'model.contacts' ),
          managers     = this.get( 'model.managers' ),
          people       = A();

      coordinators.forEach( function( person ) {

        people.pushObject( EmberObject.create( { person:person, id:person.id, role:'coordinators', title:'Coordinator', selected:false } ) );
      } );

      professors.forEach( function( person ) {

        people.pushObject( EmberObject.create( { person:person, id:person.id, role:'professors', title:'Faculty', selected:false } ) );
      } );

      guests.forEach( function( person ) {

        people.pushObject( EmberObject.create( { person:person, id:person.id, role:'guests', title:'Guest', selected:false } ) );
      } );

      students.forEach( function( person ) {

        people.pushObject( EmberObject.create( { person:person, id:person.id, role:'students', title:'Participant', selected:false } ) );
      } );

      advisors.forEach( function( person ) {

        people.pushObject( EmberObject.create( { person:person, id:person.id, role:'advisors', title:'Programme Advisor', selected:false } ) );
      } );

      directors.forEach( function( person ) {

        people.pushObject( EmberObject.create( { person:person, id:person.id, role:'directors', title:'Programme Director', selected:false } ) );
      } );

      consultants.forEach( function( person ) {

        people.pushObject( EmberObject.create( { person:person, id:person.id, role:'consultants', title:'Programme Consultant', selected:false } ) );
      } );

      contacts.forEach( function( person ) {

        people.pushObject( EmberObject.create( { person:person, id:person.id, role:'contacts', title:'Programme Contact', selected:false } ) );
      } );

      managers.forEach( function( person ) {

        people.pushObject( EmberObject.create( { person:person, id:person.id, role:'managers', title:'Programme Management', selected:false } ) );
      } );

    return people;
  } ),
  peopleWithoutDuplicates: computed( 'people' ,function() {

    var dedupedPeople = {},
        people = this.get( 'people' );

    people.forEach( person => dedupedPeople[ person.id ] = person );

    return Object.keys( dedupedPeople ).map( id => dedupedPeople[ id ] );
  } ),

  groupedPeople: computed( 'peopleWithoutDuplicates', 'filter', function() {

    var people    = this.get( 'peopleWithoutDuplicates' ),
        tabPeople = A(),
        getFilter = this.get( 'filter' ),
        arrayController;

    if ( getFilter === 0 || getFilter === 1 ) {

      tabPeople.pushObjects( people.filterBy( 'role', 'coordinators' ) );
      tabPeople.pushObjects( people.filterBy( 'role', 'directors' ) );
      tabPeople.pushObjects( people.filterBy( 'role', 'professors' ) );
      tabPeople.pushObjects( people.filterBy( 'role', 'advisors' ) );
      tabPeople.pushObjects( people.filterBy( 'role', 'managers' ) );
      tabPeople.pushObjects( people.filterBy( 'role', 'contacts' ) );
      tabPeople.pushObjects( people.filterBy( 'role', 'consultants' ) );
      tabPeople.pushObjects( people.filterBy( 'role', 'guests' ) );
    }

    if ( getFilter === 0 || getFilter === 2 ) {

      tabPeople.pushObjects( people.filterBy( 'role', 'students' ) );
    }

    arrayController = Ember.Controller.create( {

   model: tabPeople,
   sortProperties: [ 'person.lastName' ],
   sortAscending: true
    } );

return arrayController
  } ),





  filteredResults: computed( 'filterText', 'groupedPeople.[]', function() {

    var filter = this.get( 'filterText' ).replace( /\s+([^\s]+)/, '|$1').replace( /\s+$/, '' ),

        regExp = new RegExp( filter, 'i' ),
        filteredResults = this.get('groupedPeople').filter(function(result) 
        {
          return regExp.test( result.get( 'person.fullName' ) );
        } );

    return filteredResults;
  } ),

updated code with the help of @lux and its still not working

export default Mixin.create( {
  animate: true,
  filterText: '',
  filter: 0,
  filterTabs: A(),
  card: null,
  changeofperson:null,
  people:A(),

  people: computed(
    'model.coordinators.[]',
    'model.directors.[]',
    'model.professors.[]',
    'model.students.[]',
    'model.advisors.[]',
    'model.managers.[]',
    'model.contacts.[]',
    'model.consultants.[]',
    'model.guests.[]',

    function() {
      debugger;
      var people = A();
      this.get( 'model.coordinators.[]' ).forEach( function( person ) {

        people.pushObject( Ember.Object.create( { person:person, id:person.id, role:'coordinators', title:'Coordinator', selected:false } ) );
      } );

      this.get( 'model.professors.[]' ).forEach( function( person ) {

        people.pushObject( Ember.Object.create( { person:person, id:person.id, role:'professors', title:'Faculty', selected:false } ) );
      } );

      this.get( 'model.guests.[]' ).forEach( function( person ) {

        people.pushObject( Ember.Object.create( { person:person, id:person.id, role:'guests', title:'Guest', selected:false } ) );
      } );

      this.get( 'model.students.[]' ).forEach( function( person ) {

        people.pushObject( Ember.Object.create( { person:person, id:person.id, role:'students', title:'Participant', selected:false } ) );
      } );

      this.get( 'model.advisors.[]' ).forEach( function( person ) {

        people.pushObject( Ember.Object.create( { person:person, id:person.id, role:'advisors', title:'Programme Advisor', selected:false } ) );
      } );

      this.get( 'model.directors.[]' ).forEach( function( person ) {

        people.pushObject( Ember.Object.create( { person:person, id:person.id, role:'directors', title:'Programme Director', selected:false } ) );
      } );

      this.get( 'model.consultants.[]' ).forEach( function( person ) {

        people.pushObject( Ember.Object.create( { person:person, id:person.id, role:'consultants', title:'Programme Consultant', selected:false } ) );
      } );

      this.get( 'model.contacts.[]' ).forEach( function( person ) {

        people.pushObject( Ember.Object.create( { person:person, id:person.id, role:'contacts', title:'Programme Contact', selected:false } ) );
      } );

      this.get( 'model.managers.[]' ).forEach( function( person ) {

        people.pushObject( Ember.Object.create( { person:person, id:person.id, role:'managers', title:'Programme Management', selected:false } ) );
      } );

      return people;

    }),


    sortedPeople: computed('people', function() {
      debugger;
      return this.get('people').sortBy('person.lastName')
    }),

    peopleWithoutDuplicates: computed( 'sortedPeople' ,function() {

      var dedupedPeople = {},
      people = this.get( 'people' );

      people.forEach( person => dedupedPeople[ person.id ] = person );

      return Object.keys( dedupedPeople ).map( id => dedupedPeople[ id ] );
    } ),

      groupedPeople: computed( 'peopleWithoutDuplicates', 'filter', function() {

        var people    = this.get( 'peopleWithoutDuplicates' ),
        tabPeople = A(),
        getFilter = this.get( 'filter' ),
        arrayController;

        if ( getFilter === 0 || getFilter === 1 ) {

          tabPeople.pushObjects( people.filterBy( 'role', 'coordinators' ) );
          tabPeople.pushObjects( people.filterBy( 'role', 'directors' ) );
          tabPeople.pushObjects( people.filterBy( 'role', 'professors' ) );
          tabPeople.pushObjects( people.filterBy( 'role', 'advisors' ) );
          tabPeople.pushObjects( people.filterBy( 'role', 'managers' ) );
          tabPeople.pushObjects( people.filterBy( 'role', 'contacts' ) );
          tabPeople.pushObjects( people.filterBy( 'role', 'consultants' ) );
          tabPeople.pushObjects( people.filterBy( 'role', 'guests' ) );
        }

        if ( getFilter === 0 || getFilter === 2 ) {

          tabPeople.pushObjects( people.filterBy( 'role', 'students' ) );
        }

        // arrayController = Ember.Controller.create( {
        //
        //   model: tabPeople,
        //   sortProperties: [ 'person.lastName' ],
        //   sortAscending: true
        // } );

        return tabPeople;
      } ),

      filteredResults: computed( 'filterText', 'groupedPeople.[]', function() {

        var sortedArr =this.get('groupedPeople')
        var filter = this.get( 'filterText' ).replace( /\s+([^\s]+)/, '|$1' ).replace( /\s+$/, '' ),
        regExp = new RegExp( filter, 'i' ),

        filteredResults = sortedArr.filter( function( result ) {
          return regExp.test( result.get( 'person.fullName' ) );
        } );

        return filteredResults;
      } ),

      person: Ember.computed( 'card.person', function() {

        return this.get( 'card.person' );
      } ),


    } ); 

template.hbs

{{#each filteredResults as |result|}}
    <div class="grid--column grid--column-3">

      {{people-card card=result loaded=result.person.isLoaded openProfileAction="openProfile" profileLoadedAction="personLoaded" }}

    </div>
  {{/each}}

model

{
    coordinators: DS.hasMany( 'profile', { async: true } ),
    directors: DS.hasMany( 'profile', { async: true } ),
    professors: DS.hasMany( 'profile', { async: true } ),
    students: DS.hasMany( 'profile', { async: true } ),
    advisors: DS.hasMany( 'profile', { async: true } ),
}

im trying to show a sorted list of people-cards in template ( sort by lastname ) . this works under ember 1.13 but when i upgraded to ember2.1.8 im getting an error ArrayController is depreciated. how to achive the sorting for those async=true relationships to work this again ?


Solution

  • Actually I don't really understand why your current code works. Because if it does it will also work with sortBy.

    The problem you're mentioning with async relationship is true. However AFAIK your current code should not work as well because the dependency keys of people is wrong.

    Basically you have two ways to solve your problem:

    1. by using the promises and returning a PromiseArray
    2. by utilising computed properties

    There is additionally ember-computed-promise which I've written to address similar issues. It's more an experiment tho.

    So for utilising computed properties you first have to fix the dependency key of your people computed property. It could then look like this:

    people: computed(
      'model.coordinators.[]',
      'model.directors.[]',
      ...
      function() {
        [
          'coordinators',
          'directors',
          ...
        ].forEach(n => this.get(`model.${n}`).map(person => EmberObject.create({
          person,
          id: person.id,
          selected: false,
        }))).reduce((a, b) => [...a, ...b], []);
      }
    ),
    

    I've also shown you how you could make it simpler. For role and title you could use a lookup object.

    Now this CP should always contain all people, and when more people get loaded async it will update. This is important. Now you can just sort them (or do anything with them):

    sortedPeople: computed('people', function() {
      return this.people.sortBy('person.lastName')
    })
    

    this will work reliable because whenever new people are added because they are loaded the people CP will retire and sortedPeople will refire.

    If you want to use ember-computed-promise it will allow you to utilise an async function which maybe can make your code pretty readable:

    sortedPeople: computedPromise('model', async function() {
      const data = await RSVP.hash({
        coordinators: this.get('model.coordinators'),
        ...
      });
    
      const people = [];
      data.coordinators.forEach(person => people.push(EmberObject.create({
        person: person,
        id: person.id,
        role: 'coordinators',
        title: 'Coordinator',
        selected: false
      })));
    
      ...
    
    
      return people.sortBy('person.lastName')
    })
    

    Here you're basically active waiting for the data to load. This has the benefit that it will be pretty easy to show a loading spinner (the result will be reliable null) while with the other solution the data gets added as it comes.

    I won't explain in detail how to utilise PromiseArray because I would strongly recommend to move away from Proxy Objects in ember 3.1+ because you can't utilise native getters for them.