Передача данных между компонентами

В Ember.js компоненты играют ключевую роль в построении пользовательских интерфейсов. Для того чтобы обеспечить эффективное взаимодействие между компонентами, необходимо научиться правильно передавать данные между ними. Ember.js предоставляет несколько механизмов для передачи данных, каждый из которых подходит для различных сценариев.

1. Взаимодействие между родительским и дочерним компонентами

В Ember.js компоненты могут быть связаны иерархически, где один компонент является родительским, а другой — дочерним. Один из основных способов передачи данных между такими компонентами — это использование атрибутов компонента.

1.1. Передача данных с родителя на дочерний компонент

Родительский компонент может передавать данные в дочерний компонент через атрибуты. В шаблоне родительского компонента в JSX-подобном синтаксисе Ember передаются значения в дочерний компонент как параметры.

Пример передачи данных:

// Родительский компонент
import Component from '@glimmer/component';

export default class ParentComponent extends Component {
  parentMessage = 'Это сообщение от родителя';
}
<!-- Шаблон родительского компонента -->
<ChildComponent @message={{this.parentMessage}} />

В дочернем компоненте можно получить эти данные через свойства компонента:

// Дочерний компонент
import Component from '@glimmer/component';

export default class ChildComponent extends Component {
  get message() {
    return this.args.message;
  }
}

Здесь @message — это атрибут, который передается родительским компонентом, а в дочернем компоненте доступ к нему осуществляется через this.args.message. Такой подход позволяет передавать данные вниз по иерархии компонент.

1.2. Передача данных от дочернего компонента к родительскому

Если необходимо передать данные от дочернего компонента к родительскому, то Ember.js использует события. Дочерний компонент может отправить событие родительскому компоненту с помощью функции, привязанной к событию.

Пример реализации события:

// Родительский компонент
import Component from '@glimmer/component';

export default class ParentComponent extends Component {
  handleChildMessage(message) {
    console.log('Сообщение от дочернего компонента:', message);
  }
}
<!-- Шаблон родительского компонента -->
<ChildComponent @onMess age={{this.handleChildMessage}} />
// Дочерний компонент
import Component from '@glimmer/component';

export default class ChildComponent extends Component {
  sendMessage() {
    this.args.onMessage('Сообщение от дочернего компонента');
  }
}

В этом примере дочерний компонент вызывает this.args.onMessage, передавая данные родительскому компоненту, который их обрабатывает через метод handleChildMessage.

2. Использование потоков данных через сервисы

Для более сложных случаев, когда компоненты не находятся в прямой иерархической связи, а требуется передавать данные между различными компонентами на разных уровнях дерева, рекомендуется использовать сервисы. Сервисы позволяют хранить глобальное состояние приложения и обеспечивают возможность доступа к данным и логике из разных компонентов.

Сервисы в Ember.js — это обычные классы, которые могут быть внедрены в компоненты и другие сервисы для обмена данными.

Пример создания сервиса:

// Сервис
import Service from '@ember/service';

export default class MessageService extends Service {
  message = 'Глобальное сообщение';
}

Использование сервиса в компоненте:

// Компонент
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';

export default class MyComponent extends Component {
  @service messageService;

  get message() {
    return this.messageService.message;
  }
}

Теперь компонент имеет доступ к сервису, и может получать данные, а также изменять их, что позволяет синхронизировать данные между компонентами, не имеющими прямой связи.

3. Использование @tracked и двусторонняя привязка

Ember.js поддерживает двустороннюю привязку данных с помощью декоратора @tracked. Это позволяет отслеживать изменения в значениях и автоматически обновлять DOM.

Когда компонент использует @tracked, любые изменения в значении автоматически вызывают обновление представления. Эта функциональность полезна для синхронизации данных между компонентами, где изменения должны немедленно отражаться в пользовательском интерфейсе.

Пример использования @tracked:

// Компонент с @tracked
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

export default class MyComponent extends Component {
  @tracked value = 'Начальное значение';

  updateValue(newValue) {
    this.value = newValue;
  }
}

В шаблоне компонента:

<button {{on 'click' this.updateValue 'Новое значение'}}>Изменить</button>
<p>{{this.value}}</p>

В этом примере значение будет обновляться при клике на кнопку, и соответствующие изменения немедленно отразятся в интерфейсе. Это упрощает работу с динамическими данными, которые должны быть синхронизированы в рамках одного компонента.

4. Обмен данными между компонентами через состояние маршрута

Иногда требуется передавать данные между компонентами, связанными с маршрутом. В Ember.js маршруты обеспечивают более высокоуровневое управление состоянием приложения, и данные могут быть переданы через контекст маршрута.

Маршруты позволяют передавать данные через модель маршрута. Эта модель доступна для всех дочерних компонентов и шаблонов, связанных с маршрутом.

Пример:

// Маршрут
import Route from '@ember/routing/route';

export default class IndexRoute extends Route {
  model() {
    return {
      message: 'Сообщение из маршрута'
    };
  }
}
<!-- Шаблон маршрута -->
<p>{{this.model.message}}</p>

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

5. Использование контроллеров для передачи данных

Контроллеры в Ember.js предоставляют способ взаимодействия между маршрутами и компонентами, используя свойства, доступные в контексте маршрута. Контроллеры действуют как посредники для передачи данных от модели маршрута к компонентам.

Пример использования контроллера:

// Контроллер
import Controller from '@ember/controller';

export default class IndexController extends Controller {
  message = 'Это сообщение из контроллера';
}
<!-- Шаблон -->
<p>{{this.message}}</p>

Контроллеры позволяют более гибко управлять данными на уровне маршрута и передавать их в компоненты для отображения или обработки.

Заключение

Передача данных между компонентами в Ember.js включает в себя различные подходы и инструменты, такие как атрибуты для связи между родительскими и дочерними компонентами, события для обратной связи, сервисы для глобального состояния и маршруты для данных, специфичных для определенной части приложения. Каждое решение зависит от конкретной задачи и структуры приложения, поэтому важно выбирать наиболее подходящий метод в зависимости от ситуации.