Change element position in model on click?


#1

I’m making a simple todo list and this one has stumped me completely. When I click on a task’s checkmark, I’d like for that element in the model to be moved to the bottom of the list. I found NO way to do this. THe change needs to be persisted to a firebase backend, or localStorage if I decide to swap out the firebase adapter with a localStorage adapter.

I tried something naive like:

 {{#each model as |task index|}}
    <p {{action 'toggleTask' task task.isDone index}}>Some el</p>
 {{/each}}

controller.js//
      toggleTask(task, prop, i){
          task.set(i, 0);
          task.toggleProperty(prop);
          task.save();
        },

This doesn’t throw an error or anything, nothing happens. I tried a bunch of other ember and native javascript methods but nothing works. Haven’t found anything about this online. btw aside from trying to change the index of the elemtn the other two methods ni toggleTask() work fine


#2

I believe this is the simplest thing that works:

{{#each model as |task index|}}
  <p {{action 'toggleTask' task}}>Some el</p>
{{/each}}
actions: {
  toggleTask(task) {
    task.toggleProperty('isDone');
    this.get('model').removeObject(task); // remove it from its current position
    this.get('model').addObject(task); // add it to the end
    task.save(); // you definitely want some loading/error handling here
  }
}

You can’t change the index of the task object directly. You instead want to change the model array. That causes the {{each}} to re-render.

(This solution only mutates the order of tasks in memory. I can’t tell from your example how to use your API to change the order of tasks on the server.)

Also, your prop argument is not what you think. It will be the value of task.isDone, not the name of the property.


#3

Thanks but this doesn’t work because the model array is immutable so When I try this I get The result of a server query is immutable, to modify contents, use toArray()

I tried toArray() but not sure how to properly use it and how to update the model with it, everything I try returns an error. Surprised something so simple has proven so tricky!


#4

For example, in controller.js (example without any data validation):

import { A } from '@ember/array';
...
tasks: computed('model', function () {
  return A(this.get('model').toArray());
}),

Later you’ll be able to use this.get('tasks').addObject/removeObject to change the order.

Also as a second available solution you may try to split template loop into 2 loops in case the performance is not a critical thing for you:

each model as |task|
  unless task.isDone
    = task

each model as |task|
  if task.isDone
    = task

Hope it’ll help.


#5

Thanks but that only changes the array. Once the array is changed, how do you update the model with this new array? I tried something like this.set('model', newArray) and it changes the frontend correctly but does not persist to the store. I’m using firebase as a backend, while switching it out with the localStorage adapter.


#6

If you want to save the order on server then it’s a different question. Here we’re talking abt output only.

Note that the solution with 2 loops doesn’t change the order.


#7

I updated the question to reflect that the changes need to be persisted to localStorage or firebase.


#8

As a 3rd solution (for the output only) you may use data sorting:

export default Controller.extend({
  sortBy: ['name:asc', 'description:asc'],
  sortedBands: computed.sort('model', 'sortBy')
}

#9

Thank you for your help, When I first read your last reply it didn’t click for me because it didn’t show how to persist to the database. But after trying different things I realized that the computed property will always sort based on the property persisted to the database, so it actually does what I wanted. And instead of calling model from the template I can call sortedBands. Thanks guys for your help!


#10

Yeah, no problem, Greg :wink:

I hope you realise that bands is just a quick copy-paste, you don’t have to call your properties in this way :smiley:


#11

Ha of course, I figured it was an example from Rock n Roll with Ember.