0

In my Ionic 5 / Angular app, I am trying to update a Conversation object within the below Conversation array:

private _conversations = new BehaviorSubject<Conversation[]>([
    new Conversation(
      'conversation1',
      'user3',
      'user1',
      [
        new Message('message1', 'Test message', 'user3', new Date(2018, 0O5, 0O5, 17, 23, 42, 11)),
        new Message('message2', 'Another message', 'user1', new Date(2018, 0O6, 0O5, 17, 23, 42, 11))
      ]),
    new Conversation(
      'conversation2',
      'user4',
      'user2',
      [
        new Message('message3', 'my  message', 'user4', new Date(2018, 0O7, 0O7, 17, 23, 42, 11)),
        new Message('message4', 'more messages', 'user2', new Date(2018, 0O7, 0O7, 17, 23, 42, 11)),
      ])
  ]);

Here are the Conversation & Message models I'm using:

export class Conversation {
    constructor(
        public id: string,
        public userId: string,
        public mechanicId: string,
        public messages: Message[]
    ) { }
}

export class Message {
    constructor(
        public id: string,
        public text: string,
        public userId: string,
        public timestamp: Date
    ) { }
}

I'm able to add to the array using these methods below:

addConversation(mechanicId: string, message: string) {
    const newConversation = new Conversation(
      Math.random().toString(),
      this.authService.userId,
      mechanicId,
      [this.createMessage(message)]
    );
    return this.conversations.pipe(
      take(1),
      delay(1000),
      tap(conversations => {
        this._conversations.next(conversations.concat(newConversation));
      }));
  }

  private createMessage(message: string): Message {
    return {
      id: Math.random().toString(),
      text: message,
      userId: this.authService.userId,
      timestamp: new Date(Date.now())
    };
  }

But I'm unable to update a Conversation (i.e. Add a new Message object to an existing Conversation object).

Here is my latest attempt:

addToConversation(id: string, mechanicId: string, message: string) {
    const conversation = this.getConversation(id);
    if (conversation) {
      conversation.messages.push(
        this.createMessage(message)
      );
    }
  }

This doesn't work however, as I get the following error message on conversation.messages.push:

Property 'messages' does not exist on type Observable<{ id: string, userId: string, mechanicId: string, messages: Message[]; }>

Also, here is getConversation():

getConversation(id: string) {
    return this.conversations.pipe(
      take(1),
      map(conversations => {
        return { ...conversations.find(conversation => conversation.id === id) };
      }));
  }

get conversations() {
    return this._conversations.asObservable();
  }
4
  • Could you please show the getConversation(id) function? Commented Apr 9, 2020 at 18:58
  • Hi @MichaelD Thanks, added it above. Commented Apr 9, 2020 at 18:59
  • Also it isn't exactly clear how the this.conversations variable is defined. Commented Apr 9, 2020 at 19:00
  • Updated my question above Commented Apr 9, 2020 at 19:03

3 Answers 3

1

If you need access to the current value of the Behavior subject, you could use the getValue() method. It returns the current value held by the behavior subject. Using pipe, map and take(1) looks overkill to me. Try the following

getConversation(id: string) {
  return this._conversations.getValue().find(conversation => conversation.id === id);
}

addMessageToConversation(conversationId: string, message: Message) {
  this.getConverstation().messages.push(message);
}

Then call the addMessageToConversation() function anywhere in the code

addToConversation(id: string, mechanicId: string, message: string) {
  this.addMessageToConversation(id, this.createMessage(message));
  .
  .
}
Sign up to request clarification or add additional context in comments.

9 Comments

Thanks a lot for your answer. What I want to do is when addToConversation() is called, push a Message object into the existing Message[] that's inside that Conversation.
You mean the newly created Conversation object?
The Conversation that will be referenced in addToConversation() will always be pre-existing. Reason is that addToConversation() is called from inside a Conversation-Detail component (displaying existing messages), so the Conversation will always exist beforehand
I've edited the code. Please see if it works for you. Also I don't understand why you are returning a value from the addToConversation() function and what is the purpose of concat here.
I am afraid restructuring the code is beyond the scope of this question. And also navigation away and back is related to Conversation-Detail component and it is separate question. If this solution worked for this question, you are free to use it and create a new question for the other problem. If so, please don't forget to provide the code of Conversation-Detail component.
|
0

I think this will solve your problem , change the Message model and change createMessage function

    class Message {
    public id: string
    public text: string
    public userId: string
    public timestamp: Date
    constructor(
        options: any = {}
    ) {
        this.id = options.id || '';
        this.text = options.text || '';
        this.userId = options.userId || '';
        this.timestamp = options.timestamp || null; // null or new Date()
    }
}

private createMessage(message: string): Message {
    return new Message({
      id: Math.random().toString(),
      text: message,
      userId: this.authService.userId,
      timestamp: new Date(Date.now())
    });
  }

if not, let me know

Comments

0

In-stream of data you can't modify the object. You have to create(emit) a new data. Here, in your case You create/push new data using

observer.next(conversation) and takeLast(1) instead of take(1) takeLast will get the latest value always.

sample:

addToConversation(id: string, mechanicId: string, message: string) {
  const conversation = this.getConversation(id);
  if (conversation) {
    conversation.messages.push(
      this.createMessage(message)
    );
  this._conversations.next(conversation)
  }

}

Get Last:

getConversation(id: string) {
    return this.conversations.pipe(
      takeLast(1),
      map(conversations => {
        return { ...conversations.find(conversation => conversation.id === id) };
      }));
  }

Whole code:

export class Conversation {
  constructor(
    public id: string,
    public userId: string,
    public mechanicId: string,
    public messages: Message[]
  ) {}
}

export class Message {
  constructor(
    public id: string,
    public text: string,
    public userId: string,
    public timestamp: Date
  ) {}
}
class X {
  private messages = [
    new Conversation("conversation1", "user3", "user1", [
      new Message(
        "message1",
        "Test message",
        "user3",
        new Date(2018, 0o5, 0o5, 17, 23, 42, 11)
      ),
      new Message(
        "message2",
        "Another message",
        "user1",
        new Date(2018, 0o6, 0o5, 17, 23, 42, 11)
      ),
    ]),
    new Conversation("conversation2", "user4", "user2", [
      new Message(
        "message3",
        "my  message",
        "user4",
        new Date(2018, 0o7, 0o7, 17, 23, 42, 11)
      ),
      new Message(
        "message4",
        "more messages",
        "user2",
        new Date(2018, 0o7, 0o7, 17, 23, 42, 11)
      ),
    ]),
  ];
  private _conversations = new BehaviorSubject<Conversation[]>(this.messages);
  addToConversation(id: string, mechanicId: string, message: string) {
    const conversation = this.messages.find((conversation) => conversation.id === id)
    if (conversation) {
      conversation.messages.push(this.createMessage(message));
    }
    this._conversations.next(conversation)
  }
  addConversation(mechanicId: string, message: string) {
    const newConversation = new Conversation(
      Math.random().toString(),
      this.authService.userId,
      mechanicId,
      [this.createMessage(message)]
    );
    return this.conversations.pipe(
      takeLast(1),
      delay(1000),
      tap((conversations) => {
        this._conversations.next(conversations.concat(newConversation));
      })
    );
  }
  getConversation(id: string) {
    return this.conversations.pipe(
      take(1),
      map((conversations) => {
        return {
          ...conversations.find((conversation) => conversation.id === id),
        };
      })
    );
  }

  get conversations() {
    return this._conversations.asObservable();
  }
  private createMessage(message: string): Message {
    return {
      id: Math.random().toString(),
      text: message,
      userId: this.authService.userId,
      timestamp: new Date(Date.now()),
    };
  }
}

4 Comments

I tried your code, but I am getting this error message under conversation.messages.push: Property 'messages' does not exist on type Observable<{ id: string, userId: string, mechanicId: string, messages: Message[]; }>
Also this error appeared under this._conversations.next(conversation) Argument of type Observable<{ id: string, userId: string, mechanicId: string, messages: Message[]; }> is not assignable to parameter of type 'Conversation[]'
i though know basic coding! i just suggest how you should do, not exact code! lol. let me update my answer!
Oh OK. If you could, could you please use the existing getConversation() please?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.