Где должно храниться состояние модели в Angular.js

Нахожу Угловойиспользование моделей сбивает с толку. Кажется, что Angular придерживается подхода, согласно которому модель может быть чем угодно, т.е. Angular не включает явный класс модели, и вы можете использовать ванильные объекты JavaScript в качестве моделей.

Почти в каждом угловом примере яВидно, что модель фактически является объектом, либо созданным вручную, либо возвращенным из вызова API через Ресурс. Потому что почти каждый угловой примерВыглядит просто, обычно данные модели хранятся в $ scope в контроллере, и любое состояние, связанное с моделью, например выбор, также сохраняется в $ scope в контроллере. Это хорошо работает для простых приложений / примеров, но это выглядит как упрощение, когда приложения становятся более сложными. Состояние модели, хранящееся в контроллере, рискует стать контекстным и потеряться, например, при изменении контекста; Контроллер храненияselectedGallery а такжеselectedPhoto может хранить только глобальныйselectedImageнеselectedPhoto за галерею. В такой ситуации использование контроллера на галерею может устранить эту проблему, но может показаться расточительным и, вероятно, неуместным и ненужным с точки зрения пользовательского интерфейса.

Угловая»Определение моделей кажется более близким к тому, что я бы назвал VO / DTO, который является тупым объектом, передаваемым между сервером и клиентом. Мой инстинкт заключается в том, чтобы обернуть такой объект в то, что я бы назвал Моделью - класс, который поддерживает состояние, относящееся к DTO / VO (например, к выбору), предлагает мутаторы, необходимые для манипулирования DTO / VO, и уведомляет остальную часть применение изменений к базовым данным. Очевидно, что об этой последней части хорошо заботится Angular 's привязки, но я все еще вижу сильный вариант использования для первых двух обязанностей.

Однако я не имеюЯ действительно не видел эту модель, использованную в примерах, которые яЯ смотрел, но я также не видел того, что я бы назвал масштабируемой альтернативой. Похоже, что Angular не рекомендует использовать Services в качестве моделей, применяя Singletons (я знаю, что есть способы обойти это, но они этого не делают).кажется, широко используется или одобрен).

Итак, как мне сохранять состояние данных модели?

[Редактировать] Второй ответ вэтот вопрос интересно и близко к тому, что ям в настоящее время использую.

 Mark Rajcok17 мая 2013 г., 18:15
Что неВам нравится один сервис для каждой модели?galleryService может хранить массив галерей.
 Undistraction18 мая 2013 г., 01:16
Но как бы вы справились с ситуацией, которую ямы обрисовали в общих чертах - где есть структура как Галереи> Галерея> Фото? В более мелкой структуре - Галереи> Gallery, одиночный GalleriesService отлично работает, манипулируя его галереями, но когда структура модели становится более сложной, это нене обрезай это. Что делать, если вам нужны государства и доступа на фото? Конечно, тогда имеет смысл, что каждая галерея существует как отдельный сервис / модель, способная работать со своими фотографиями?
 Undistraction18 мая 2013 г., 08:44
@JoshDavidMiller Спасибо. Я'Придется еще раз взглянуть на реализацию ngResource. Было бы здорово, если бы вы могли сделать голый Gist или даже какой-нибудь псевдокод в качестве ответа;)
 Undistraction18 мая 2013 г., 17:47
@JoshDavidMiller Думал, что было бы хорошо, если бы это было отдельным вопросом / ответом, поэтому просто задайте его здесь:stackoverflow.com/questions/16626075/..., Пожалуйста, оставьте ответ с вашим примером.
 Josh David Miller18 мая 2013 г., 08:09
@Pedr я не сделалЯ имею в виду использовать ngResource, хотя это общий подход, но реализация аналогична ему. ngResource действует как коллекция и возвращает объекты (или их массивы) со специальными методами, добавленными к каждому, например,$save, С одногоgallery сервис, вы можете вернуть объект (или их массив) с любыми методами индивидуального уровня, которые вам нужны, например,getPhotos - который возвращает массив фотообъектов (очевидно, из отдельного сервиса с внедрением зависимостей). Когда вам нужно сохранить состояние, вы используете посредническую службу, которая обращается кgallery или жеphoto Сервисы.
 Tamlyn14 сент. 2013 г., 18:44
Для всех, кто интересуется: VO =Объект значения, DTO =Объект передачи данных.
 Undistraction17 мая 2013 г., 18:59
@MarkRajcok У меня нет проблем с Singleton Services. Во многих ситуациях это все, что вам нужно, и в ситуации, которую вы описываете, все будет хорошо. Но что, если каждая галерея имеет массив фотографий, каждая из которых должна поддерживать состояние?
 Mark Rajcok17 мая 2013 г., 19:54
Я, вероятно, все еще моделирую все в сервисе (или сервисах) и выставляю контроллерам только то, что им нужно.
 Undistraction18 мая 2013 г., 06:43
@MarkRajcok Это имеет большой смысл и в значительной степени мой подход на данный момент. У меня есть (одноэлементная) служба / модель галереи, в которую я передаю объект данных, возвращенный функцией GalleriesResource :: query (). Затем эта служба строит модель галереи для каждого узла галереи в объекте данных, передавая узел, и каждая модель галереи создает модель фотографии для каждой фотографии, снова передавая узел. Теперь каждый узел объекта данных управляется, пока структура данных не повреждена (и может быть изменена и перенастроена на сервер, если / когда это необходимо).
 Undistraction18 мая 2013 г., 06:48
@JoshDavidMiller Итак, в этом случае служба оборачивает ресурс?
 Mark Rajcok18 мая 2013 г., 03:53
Я полагаю, что я могу чрезмерно упростить и проектировать на лету здесь ... У меня было бы три модельных объекта: 1) фотообъект, 2) объект галереи (одно свойство которого является массивом фотообъектов), 3 ) объект galleryCollection (одно свойство которого представляет собой массив объектов галереи). (GalleryCollection может не быть отдельным объектом - он может быть просто частью самой самой galleryService.) Методы и свойства могут существовать во всех трех. На мой взгляд, каждая фотография и галерея - это отдельный объект, они просто группируются / управляются / доступны сервису. Модели могут быть определены за пределами сервиса.
 Josh David Miller18 мая 2013 г., 04:03
Я согласен с @MarkRajcok (как это часто бывает). Самый чистый и простой способ - использовать сервисы, как он описал. Это значительно упрощает тестирование и делает каждый сервис более расширяемым и многократно используемым. Я думаю это'важно рассматривать услуги, а не возвращать модельобъект а как вернуть модельAPI, Этот API - это то, что вы используете в контроллере для доступа к одному или к коллекции объектов модели. Так чтоgallery служба может иметь знакомые методы (получить, обновить, удалить и т. д.), одновременно управляя состоянием и возвращая объекты с помощью отдельных методов записи, например.$resource

Ответы на вопрос(3)

Состояние (и модели) хранятся в $ scope

$ scope is Angular 'Объект хранения данных.. Сам $ scope не является моделью, но вы можете хранить модели в $ scope.

Каждый $ scope имеет родительский $ scope, вплоть до $ rootScope, образуя древовидную структуру, которая свободно отражает ваше DOM. Когда вы вызываете директиву, которая требует нового $ scope, например, ng-controller, новый объект $ scope будет создан и добавлен в дерево.

Объекты $ scope связаны с использованием прототипного наследования. Это означает, что если вы добавите модель на более высоком уровне в дереве, она будет доступна для всех более низких уровней. Это феноменально мощная функция, которая делает иерархию $ scope практически прозрачной для автора шаблона.

Контроллеры инициализируют $ scope

Целью контроллера является инициализация $ scope, Один и тот же контроллер может инициализировать множество объектов $ scope в разных частях страницы. Контроллер создается, устанавливает объект $ scope и затем завершает работу. Вы можете использовать один и тот же контроллер для инициализации множества областей $ в разных частях страницы.

В случае вашей галереи изображений у вас будет контроллер imageGallery, который вы затем примените к каждой части DOM, которую вы хотите использовать в качестве галереи, используя директиву ng-controller. Эта часть страницы получитсобственный $ scope, который вы бы использовали для хранения атрибута selectedPhoto.

Прототипные прицелы

$ scope наследует от своего родителя с использованием простого старого прототипического наследования вплоть до $ rootScope, так что вы можете хранить ваши объекты в любом месте иерархии, что имеет смысл. Вы получаете дерево объектов $ scope, которое примерно соответствует вашему текущему DOM. Если ваш DOM меняется, новые $ scope объекты создаются для вас по мере необходимости.

$ scope - это простой объект JavaScript. Это'Не более расточительно создать несколько объектов $ scope, чем создать массив с несколькими объектами currentImage. Это'Разумный способ организовать ваш код.

Таким образом, Angular избавляется от старого "где я храню свои данные проблема, которую мы часто находим в JavaScript. Это'Источником одного из действительно значительных приростов производительности, которые мы получаем от Angular.

Получили глобальные данные (например, userId)? сохраните его на $ rootScope. Есть локальные данные (например, currentImage в галерее, где есть несколько экземпляров галереи)? Сохраните его в объекте $ scope, который принадлежит этой галерее.

$ scope автоматически доступен для вас в правильной части шаблона.

Угловые модели тонкие

Исходя из истории Rails, где мы подчеркиваем жирные модели и узкие контроллеры, я нашел Angular's 'едва там модели удивительные. Фактически, использование большого количества бизнес-логики в вашей модели часто приводит к проблемам в будущем, как мы иногда видим в модели User в Rails, которая, если выне будьте осторожны, будут расти, пока не станут непригодными.

Угловая модель - это просто объект JavaScript или примитив.

Любой объект может быть моделью. Модели обычно определяются с использованием JSON в контроллере или AJAXed с сервера. Модель может быть объектом JSON или просто строкой, массивом или даже числом.

Конечно, естьНичто не мешает вам добавлять дополнительные функции к вашей модели и сохранять их в объекте JSON, если вы этого хотите, но это будет портирование в парадигме, которая не 'T действительно подходит с Angular.

Угловые объекты обычно являются хранилищами данных, а не функциями.

Модель на переднем конце не настоящая модель

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

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

Бизнес-логика может жить в услугах.

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

Сервисы - это одиночные объекты. Как и любой другой объект JavaScript, вы можете поместить в них функции или данные. Angular поставляется с кучей встроенных сервисов, таких как $ http. Вы можете создать свой собственный и использовать внедрение зависимостей, чтобы автоматически предоставлять их своим контроллерам.

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

Услуги не являются моделями

Конечно, ты не должениспользовать услуги в качестве моделей. Используйте их как объекты, которые могут делать вещи. Иногда они делают вещи для вашей модели. Это'Это другой способ мышления, но работоспособный.

 Ore444407 нояб. 2016 г., 11:00
Sidenote: Вы не обязаны использовать только инструменты, предоставляемые Angular для управления состоянием. В Angular можно интегрировать любую библиотеку управления состоянием, например,redux.js.org
 Undistraction02 мая 2014 г., 08:14
Благодарю. Все хорошие моменты. Однако ты неТ суть вопроса. Вы предлагаете, чтобы все состояние было сохранено вреальный» модели на сервере? Потому что это фактически сводит на нет одно из главных преимуществ богатого клиента - скорость. Я бы сказал, что во многих случаях модель на стороне клиентареальный» как серверная модель, хотя это, очевидно, зависит от архитектуры. В моем вопросе я специально обращаюсь к состоянию, которое относится только к клиенту - состояние выбора, а не к более постоянному состоянию, которое необходимо сохранить на сервере.
 superluminary02 мая 2014 г., 11:21
Да, вы, конечно, правы, состояние должно храниться на клиенте, а задачи переднего плана должны выполняться на клиенте. Я просто чувствовал, что было полезно поговорить об этой проблеме.
 Undistraction02 мая 2014 г., 12:35
Определенно хорошо поговорить об этом. Я думаю, что это все еще нерешенная проблема. Государство - то, где скрываются монстры, и никто не любит иметь дело с этим.
 mjj140909 окт. 2015 г., 19:48
Хороший пост, это парадигма, которой я пытаюсь следовать в своем приложении.
 superluminary12 нояб. 2014 г., 11:31
Конечно, для нас состояние прекрасно решается с помощью Angular с использованием $ scope.

как вы храните то, что называетемодельные объекты ", Угловой контроллер$scope существует исключительно какпосмотреть модель " в целях управления вашим пользовательским интерфейсом. Я предлагаю разделить эти два понятия в вашем коде.

Если вы хотите аккуратное уведомление об изменении области Angular ($watch), вы можете использовать объект области видимости для хранения данных вашей модели, если хотите (var myScope = $rootScope.$new()). Просто неt использовать тот же объект области видимости, к которому привязан ваш пользовательский интерфейс.

Я рекомендую написание пользовательских услуг для этой цели. Итак, поток данных выглядит так:

AJAX -> Таможенное обслуживание -> Модель Scope Object -> Контроллер -> UI Scope Object -> DOM

Или это:

AJAX -> Таможенные услуги -> Простые старые объекты JavaScript -> Контроллер -> UI Scope Object -> DOM

 Undistraction18 мая 2013 г., 01:08
Я уже разделил эти понятия, именно поэтому яЯ задал этот вопрос. Стандартный угловой способ работы с состоянием модели, кажется, хранится в этой модели представления, что и делает меня неудобным. Создание новой области видимости неМне кажется, это хорошее решение. Это похоже на неправильное использование возможностей.

давайтеНе забывайте, что Angular - это веб-фреймворк, и если высохранить свое состояние " исключительно в объекте, он не выживет, когда пользователь нажмет обновление в своем браузере. Поэтому выяснение того, как сохранить состояние данных модели в веб-приложении, означает выяснение того, как вы собираетесь сохранить его, чтобы ваш код работал в среде браузера.

Благодаря Angular вы действительно можете сохранить свое состояние, используя:

Вызов ресурса RESTful $URL-адрес, представляющий экземпляр вашей модели

В вашем простом примере хранение пользовательских действий, таких какselectedGallery а такжеselectedPhoto может быть представлен с помощью URL с чем-то вроде:

// List of galleries
.../gallery

// List of photos in a gallery
.../gallery/23

// A specific photo
.../gallery/23/photo/2

URL-адрес имеет решающее значение, поскольку он позволяет пользователю перемещаться по истории браузера с помощьюback а такжеforward кнопок. Если вы хотите поделиться этим состоянием с другой частью вашего приложения, веб-приложение предоставляет множество методов, позволяющих вам достичь этого, используя cookie / localStorage, скрытый фрейм / поля или даже сохраняя его на своем сервере.

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

 marcoseu18 мая 2013 г., 16:42
Я понимаю вашу точку зрения. Я согласен с вами, что Angularдокументация гласит, что "Наконец, важно понимать, что все сервисы Angular являются приложениями ». но я воспринимаю это как плохую попытку документирования. Я лично пользуюсь.service когда нужен Синглтон, но используйте.factory вернуть новое "в состоянии объекты, когда я хочу иметь экземпляр объекта.
 Mark Rajcok18 мая 2013 г., 18:37
@ marcoseu, интересно ... хотя ты можешь сделать это, не так ли? Теперь внедрение зависимостей работает совсем по-другому с этой фабрикой - то есть, поскольку вы возвращаете функцию (объект), а не объект (то есть {}), вы должны использоватьnew создать экземпляр. Это означает, что вы можетеделиться этим экземпляром с другими объектами, используя обычный синтаксис внедрения зависимостей. Я думаю, что это может сбить с толку других разработчиков Angular, которые могут использовать ваш код.
 marcoseu18 мая 2013 г., 16:06
На самом деле это не так..service возвращает Singleton, но вы можете создать экземпляр, используя.factory, Позвольте мне найти пример, и я обновлю этот комментарий с ним.
 marcoseu18 мая 2013 г., 17:14
Вот:plnkr.co/edit/9UMI1zeMRArefqgvC8Ch?p=preview, Я попытался сделать это как можно проще, чтобы донести смысл.
 Undistraction18 мая 2013 г., 17:08
Если у вас есть пример использования фабрики для генерации экземпляров, было бы здорово увидеть это.
 marcoseu18 мая 2013 г., 19:25
@MarkRajcok Я понимаю, что использованиеnew сбивает с толку, но это напомнило мне о том, что услуга, которую я вызываю, не подлежит обмену. Однако, если проблема связана исключительно с синтаксисом, вы также можете вернуть объект, то есть: вызвать new в.factory, Я обновил поршень, чтобы проиллюстрировать, как это можно сделать.
 Undistraction18 мая 2013 г., 15:59
Также .service и .factory оба возвращают Singleton Services (что очень нелогично).
 Undistraction18 мая 2013 г., 16:12
Благодарю. Я согласен, что это возможно, но это, конечно, неT продвигается как обычное использование .factory.
 marcoseu18 мая 2013 г., 19:13
@MarkRajcok Я намеренно возвращаю новый 'в состоянии функционировать так, чтобыnew Ключевое слово будет указывать, что эта услуга не должна использоваться другими объектами. Например, у меня есть обертка вокруг ui.bootstrap.alert, которую я создаю, передавая $ scope в моем контроллере. Когда мне нужно показать предупреждение позже, я просто звонюalert.success("Ok"), Тем не менее, я согласен с тем, что неясно, когда вызывающий абонент должен использовать новый, но пользователь сервиса должен понять, как сервис должен использоваться.
 Undistraction18 мая 2013 г., 15:33
Это имеет большой смысл, и я полностью согласен с тем, что состояние приложения необходимо отслеживать по пути / строке запроса, однако, безусловно, это проекция состояния приложения, а не способ его сохранения. Панель URL - это странная комбинация модели и представления, но в конечном итоге она отражает внутреннее состояние приложения или вызывает изменения в этом состоянии. Самому приложению по-прежнему нужны способы внутреннего отслеживания этого состояния, и это делается на Сервисе / Модели, где приложение 'Актеры могут делиться доступом, кажется правильным решением для меня.
 Undistraction18 мая 2013 г., 17:40
Ницца. Спасибо за это желанное отклонение.

Ваш ответ на вопрос