3

my second question relates to my first question. The answer just help me to understand my problem a little bit more.

My situation:

I have a table in parseplattform which stores my user pics. The user should be able to delete the selected ones. So I make a checkbox under each pic which gives me back the picID. So the next step is to finde all the selected pics in my table and "destroy" them.

This is my code snippit for just finding the selected pics:

  var inputs = document.querySelectorAll("input[type='checkbox']");

  for(var i = 0; i < inputs.length; i++) {
    if(inputs[i].checked == true){
      var imgForDelPromise = new Promise(function (resolve) {
        userPics_query.get(inputs[i].id, {
          success: function(picForDelete) {
            alert(picForDelete.id);
            resolve(picForDelete);
           },
           error: function(picForDelete, error) {
             alert("Error: " + error.code + " " + error.message);
           }
         });
      });
    }
  }

My problem:

If I select out of 12 pics, pic No. 1, 5 and 8 I would expect that I also will get back the ID of pic 1, 5 and 8 BUT I allways get the last one as often as many items I had selected. In this example 3 times 8.

Like in my first question suggested I tried it with promises but it didn't helped.

So I asked google and got this but it also didn't helped me.

I hope you understand my question and problem and hope you can help me.

Maybe there is an easier way like query.equalTo() where I can pass an array with different elements and get back an array with different objects. But I couldn't find it yet.

Thank you.

UPDATE:

The problem remains and I try to specify it a little bit more. For example:

var ids = Array.from(document.querySelectorAll("input[type='checkbox']"))
.filter( //only checked inputs
    function(input) { return input.checked; }
).map( //map to a promise
    function(input) {
        return input.id;
    }
);

console.log(ids);

ids.forEach(function(id) {
    userPics_query.get(id)
    .then(function(picObject) {
        console.log(picObject.id);
    })
    .then(function(error) {
        console.log(error);
    });
});

what I tried to build by your answers give me the following log:

Array [ "x2WSASwKWI", "NuhRXja0PL" ]
NuhRXja0PL
NuhRXja0PL

My Problem: I don't know how iterate thru an array and get back for each element the request from the server because every loop comes frist an THEN the get request and I can't get it in the right order. What do I miss?

By using Promise.all(promises) I pass the complete array but query.get can only handle one string at a time!

Update 2:

The function

query.get("xWMyZ4YEGZ", {
  success: function(gameScore) {
    // The object was retrieved successfully.
  },
  error: function(object, error) {
    // The object was not retrieved successfully.
    // error is a Parse.Error with an error code and message.
  }
});

cames from parseplattform.org

timetosmile
  • 73
  • 1
  • 11
  • updated my answer to show how to process one id at the time but I'd advice against it and instead try to solve the problem with `userPics_query.get` – HMR Jan 29 '18 at 13:14

3 Answers3

2

I'm not sure if the code you posted is the actual code giving you the problem. The variable i in your loop should increment as it is not used in an asynchronous callback (function passed to new Promise is immediately executed).

However; the code that does give you the problem could be resolved when you use the inputs as an array, filter out the ones that are not checked and then map them to promises:

var inputs = Array.from(document.querySelectorAll("input[type='checkbox']"));
Promise.all(
  inputs.filter(//only checked inputs
    function(input){return input.checked; }
  ).map(//map to a promise
    function(input){
      console.log("calling get with id:",input.id);
      //removed the new Promise wrap because the get function
      //  returns a promise
      return (new Parse.Query(SomeThing)).get(input.id);
    }
  )
)
.then(
  function(deleted){ console.log("deleted:",deleted); },
  function(error){console.error("something went wrong:",error); }
);

Since the problem obviously comes from userPics_query.get and not provided in the question you could badly solve it by processing the id's one at the time. You could do so by doing the following:

Array.from(document.querySelectorAll("input[type='checkbox']"))
.filter(//only checked inputs
    function(input){return input.checked; }
).reduce(//only start the next if current is finished
  function(p,input){
    console.log("calling get with id:",input.id);
    return p.then(
      all=>
        userPics_query.get(input.id)
        .then(item=>all.concat([item]))
    );
  },
  Promise.resolve([])
)
.then(
  function(deleted){ console.log("deleted:",deleted); },
  function(error){console.error("something went wrong:",error); }
);

It would probably better to keep using Promise.all and try to figure out what's wrong with userPics_query.get, I bet it's shared global mutable dependency that's used in asynchronous code.

HMR
  • 37,593
  • 24
  • 91
  • 160
  • Thank you for your answer. Yes this is my acutal code but a little bit shorter. I tested your answer and get following result: 0: Object { className: "Photo", _objCount: 45, id: "NuhRXja0PL" } 1: Object { className: "Photo", _objCount: 48, id: "NuhRXja0PL" } So the problem remains, because both objects have the same ID. – timetosmile Jan 27 '18 at 22:04
  • @AlexImZug updated the answer, make sure all id's are set correctly on the inputs and if they are then make sure your `userPics_query.get` returns the correct object. – HMR Jan 28 '18 at 10:20
  • it is a different output and `userPics_query.get` gives me a different output becase this command gets executed after my loop because it's asynce and my loop is to fast for it. This is way I get x times the same ID. – timetosmile Jan 29 '18 at 10:53
  • `calling get with id: x2WSASwKWI; calling get with id: NuhRXja0PL; Array [ "x2WSASwKWI", "NuhRXja0PL" ]; resoled id: x2WSASwKWI resolved with: NuhRXja0PL; resoled id: NuhRXja0PL resolved with: NuhRXja0PL; ` – timetosmile Jan 29 '18 at 12:50
  • @AlexImZug Repeat from yesterday: `make sure all id's are set correctly on the inputs` the logs show that you have different id's for the inputs so this is probably not a problem. `and if they are then make sure your userPics_query.get returns the correct object` That means we have to see where that function comes from since it's not included in your question. – HMR Jan 29 '18 at 12:55
  • The IDs on the Inputs are correct. Each pic has an other ID but in the end `query.get`retrieve always the same ID . Sry I thougth u know that the query.get function cames from [http://docs.parseplatform.org/js/guide/#retrieving-objects](parseplattform.org) In my code snippit you can only finde public methods. There is nothing special in it. – timetosmile Jan 29 '18 at 13:16
  • @AlexImZug can you please show us how you create `userPics_query`? Are you using `new Parse.Query(SomeThing);`? If you did then look at the updated answer, it correctly uses parse to query each id (I hope). – HMR Jan 29 '18 at 13:19
  • reduce() That is the answer what I was looking for. Now I get my wanted output. Thank you for your patience. @HMR YES: I create it like this `var userPics_query = new Parse.Query("Photo");` – timetosmile Jan 29 '18 at 13:22
  • @AlexImZug I think the `Promise.all` function works even faster but I'm not sure how parse is sharing state. Since we create a `new Parse.query` for every id it should work as well but a lot faster than the reduce. – HMR Jan 29 '18 at 13:25
  • I'll try it and see if it make a difference – timetosmile Jan 29 '18 at 13:34
  • 1
    Good answer. I'd suggest that @AlexImZug also heed the advice to encapsulate a little more using functions. I updated my answer to emphasize that + boiler plate Parse query + some jquery advice on checkboxes – danh Jan 29 '18 at 17:34
2

There's no need to create a promise, since the query functions return their own promises. In other words, in your code, all of these...

userPics_query.get(someParam, { success : successFn, error : errorFn })

may be replaced by these...

userPics_query.get(someParam).then(successFn, errorFn)

And wherever you've wrapped the first form in a Promise creation, you should definitely delete that and use the second form. In general, build functions for small, testable bits of work.

Applying that to your code, build a function to return a promise to get a user pic given an id...

// note - OP should substitute real collection and prop names
function userPicWithId(inputId) {
    var query = new Parse.Query("MyUserPicCollection");
    query.equalTo("myUserPicIdProperty", inputId);
    return query.get(inputsId);
}

Build a function to return an array of ids from checked checkboxes (idea borrowed from here)...

// something like this, but OP should test
function checkedIds() {
    return $(":checkbox:checked").map(() => this.id).get();
}

That gives us two simple, testable functions. And it's simple to use them as follows...

let promises = checkedIds().map(anId => userPicWithId(anId);

Run all of the promises with Promise.all...

Promise.all(promises).then(results => console.log(results));

All of this advice holds true for your earlier question as well. Once you get the hang of using properly encapsulated promises and logic, things get much simpler.

danh
  • 62,181
  • 10
  • 95
  • 136
  • Should filter out the checkboxes that are not checked, either with `Array.from(queryselector).filter` or with a more specific query selector: `querySelectorAll('input:checked')` Probably don't need to wrap the `userPics_query.get` but I'm not sure where that comes from and if it's used correctly (seems to be the case it's not doing what OP thinks it does) – HMR Jan 29 '18 at 11:19
  • @danh I get a `TypeError: inputs.map is not a function`. `Once you get the hang of using promises properly, things get much simpler.`I believe it and I think I'm very close to get it but the problem still remains in both solutions. – timetosmile Jan 29 '18 at 12:35
  • @AlexImZug `map` is a method of the Array.prototype. querySelectorAll does not return an array but since it returns a [nodelist](https://developer.mozilla.org/en-US/docs/Web/API/NodeList) Though documentation does not mention; this is iterable so you can use `Array.from`. The only thing different from this solution is that the call to `userPics_query.get` is not wrapped in `new Promise` since that already returns a promise. I updated my answer to reflect this. – HMR Jan 29 '18 at 13:01
1

I have seen that the most perform at thing to do is query for all ids in a single query and then manipulate them how you want.

export const getUsers = async (ids: string[]) => {
  const userQuery = new Parse.Query(Parse.User);
  userQuery.containedIn('objectId', ids);

  const data = await userQuery.find({ useMasterKey: true });

  const map = data.reduce((acc, item) => ({
      ...acc,
      [item.id]: item,
  }), {} as { [id: string]: Parse.User });

  return map;
};
Alejandro
  • 220
  • 2
  • 13