срабатывает перехват $ onChanges при обновлении коллекции из сервиса angularjs - PullRequest
0 голосов
/ 23 ноября 2018

Я пытаюсь заставить $ onChanges перехватывать работу неизменным способом.

Служба чата

class ChatService {
  constructor() {
    this.collection = {
      1: [
        {
          chat: 'Hi',
        },
        {
          chat: 'Hello',
        },
        {
          chat: 'How are you?',
        },
      ],
    };
  }
  getCollection() {
    return this.collection;
  }
  getChatById(id) {
    return this.collection[id];
  }
  addChat(id, chat) {
    // this.collection[id].push(chat);
    this.collection[id] = this.collection[id].concat(chat);
  }
}

Компонент чата

const Chat = {
  bindings: {},
  template: `<chat-list chats="$ctrl.chats" add-msg="$ctrl.addMsg(chat)"></chat-list>`,
// template: `<chat-list chats="$ctrl.chats[$ctrl.id]" add-msg="$ctrl.addMsg(chat)"></chat-list>`,
  controller: class Chat {
    constructor(ChatService) {
      this.ChatService = ChatService;
      this.id = 1;

      // if i get the all the chat collection by
      // this.chats = ChatService.getCollection()
      // and then use like above in the commented out template,
      // and it works and triggers $onChanges

      this.chats = ChatService.getChatById(this.id);
    }

    addMsg(msg) {
      this.ChatService.addChat(this.id, { chat: msg });
    }
  },
};

Компонент списка чата

const ChatList = {
  bindings: {
    chats: '<',
    addMsg: '&',
  },
  template: `
    <div>
    <li ng-repeat="chat in $ctrl.chats">{{chat.chat}}</li>
    <form ng-submit="$ctrl.addMsg({chat: chatmodel})">
      <input ng-model="chatmodel">
    </form>
    </div>

  `,
  controller: class ChatList {
    $onChanges(changes) {
      console.log(changes);
      if (changes.chats && !changes.chats.isFirstChange()) {
        // this.chats = changes.chats.currentValue;
      }
    }
  },
};

Однако, крюк $onChanges не срабатывает.Я знаю, что для того, чтобы вызвать пожар $onChanges, нужно прервать ссылку привязки chats в chat-list компоненте chat.

Также я могу повторно получить чаты последобавив метод addMsg, он будет работать и запускать $onChanges, но если сообщение получено от другого пользователя и, скажем, если я использую службу Pusher, оно будет обновлять только коллекцию чатов в службе чата, а неchat-list компонент.

Один из способов $onChanges, кажется, срабатывает, когда я получаю всю коллекцию чатов, а затем использую ctrl.id, чтобы получить определенные чаты при прохождении через привязки, такие как <chat-list chats="$ctrl.chats[$ctrl.id]" вместо <chat-list chats="$ctrl.chats.Тем не менее, это обновит список чата, ничего не делая на $onChanges.

В идеале я хотел бы обновить список чата в представлении на <chat-list chats="$ctrl.chats, а затем использовать currentValue из хука $onChanges и не использовать такие как $watch и $doCheck.Я не уверен, как это сделать.Любая помощь приветствуется.Спасибо и заранее.

Вот очень простой пример этого на plunkr .

1 Ответ

0 голосов
/ 23 ноября 2018

Давайте на минутку рассмотрим, что делает ваш код, чтобы убедиться, что мы понимаем, что происходит не так:

Конструктор в ChatServices создает новый объект в памяти (Object A), этот объект имеетсвойство 1, которое содержит массив в памяти (Array 1)

constructor() {
    this.collection = {
      1: [
        {
          chat: 'Hi',
        },
        {
          chat: 'Hello',
        },
        {
          chat: 'How are you?',
        },
      ],
    };
  }

В конструкторе компонента вы используете ChatService для извлечения Array 1 из памяти и сохранения его в this.chatsсвойство из вашего компонента

this.chats = ChatService.getChatById(this.id);

Итак, в настоящее время у нас есть две переменные, указывающие на один и тот же массив (Array 1) в памяти: свойство chats вашего компонента и collection '* 1 свойство в ChatService.

Однако, когда вы добавляете сообщение в ChatService, вы используете следующее:

addChat(id, chat) {
    this.collection[id] = this.collection[id].concat(chat);
  }

Что это делает: Обновляет collection * свойство 1 не указывает на Array 1, но вместо этого создает новый массив, объединяя как текущий Array 1, так и новый message, сохраняет его в памяти (Array 2) и присваивает его collection[id].

Примечание. Это означает, что Object A объектСвойство t 1 также указывает на Array 2

Даже если свойство collection 1 было правильно обновлено, когда дело доходит до неизменности, свойство chats вашего компонентав памяти все еще указывает на Array 1.

Ничто не указывает на то, что следует указывать на Array 2.

Вот простой пример, демонстрирующий, что происходит:

const obj = { 1: ['a'] };

function get() {
  return obj['1'];
}

function update() {
  obj['1'] = obj['1'].concat('b');
}

const result = get();
console.log('result before update', result );
console.log('obj before update', obj['1']);
update();
console.log('result after update', result );
console.log('obj after update', obj['1']);

Как видно из приведенного выше фрагмента, указание obj['1'] на новый массив не приводит к изменению массива result точек на.

Вот почему следующее работает правильно:

Один из способов, с помощью которого $ onChanges срабатывает, - это когда я получаю всю коллекцию чатов, а затем использую ctrl.id для получения определенных чатов при прохождении через привязки, например<chat-list chats="$ctrl.chats[$ctrl.id]" вместо <chat-list chats="$ctrl.chats.

В этом случае вы сохраняете ссылку на Object A.Как упоминалось выше, 1 свойство ChatService collection обновляется правильно, поэтому это будет отражаться в вашем компоненте, так как он также использует тот же Object A.

.используя вышеуказанный способ (то есть, передав Object A вашему компоненту), вы должны убедиться, что компонент знает об изменениях, внесенных в Object A (поскольку он не может знать об этом, когда не имеет к нему доступа).

Типичный способ, которым подобные вещи делаются в Angular (я знаю, что это AngularJS, но просто указываю, как можно решить эту проблему так, как Angular будет работать и отлично работает с Angular JS), это использование RXjs и подписка наизменения чатов в вашем компоненте.

...