Where to put model data and behaviour? [tl; dr; Use Services]

javascript model-view-controller angularjs dci

74543 просмотра

8 ответа

I am working with AngularJS for my latest project. In the documentation and tutorials all model data is put into the controller scope. I understand that is has to be there to be available for the controller and thus within the corresponding views.

However I dont think the model should actually be implemented there. It might be complex and have private attributes for example. Furthermore one might want to reuse it in another context/app. Putting everything into the controller totally breaks MVC pattern.

The same holds true for the behaviour of any model. If I would use DCI architecture and separate behaviour from the data model, I would have to introduce additional objects to hold the behaviour. This would be done by introducing roles and contexts.

DCI == Data Collaboration Interaction

Of course model data and behaviour could be implemented with plain javascript objects or any "class" pattern. But what would be the AngularJS way to do it? Using services?

So it comes down to this question:

How do you implement models decoupled from the controller, following AngularJS best practices?

Автор: Nils Blum-Oeste Источник Размещён: 13.07.2019 08:17

Ответы (8)


154 плюса

Решение

You should use services if you want something usable by multiple controllers. Here's a simple contrived example:

myApp.factory('ListService', function() {
  var ListService = {};
  var list = [];
  ListService.getItem = function(index) { return list[index]; }
  ListService.addItem = function(item) { list.push(item); }
  ListService.removeItem = function(item) { list.splice(list.indexOf(item), 1) }
  ListService.size = function() { return list.length; }

  return ListService;
});

function Ctrl1($scope, ListService) {
  //Can add/remove/get items from shared list
}

function Ctrl2($scope, ListService) {
  //Can add/remove/get items from shared list
}
Автор: Andrew Joslin Размещён: 20.06.2012 01:31

80 плюса

I'm currently trying this pattern, which, although not DCI, provides a classical service / model decoupling (with services for talking to web services (aka model CRUD), and model defining the object properties and methods).

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

РЕДАКТИРОВАТЬ: Раньше я думал, что этот шаблон будет идти против мантры «Угловая модель - это простой старый объект javascript», но теперь мне кажется, что этот шаблон идеально подходит.

РЕДАКТИРОВАТЬ (2): чтобы быть еще яснее, я использую класс Model только для разметки простых методов получения / установки (например: для использования в шаблонах представления). Для большой бизнес-логики я рекомендую использовать отдельные службы, которые «знают» о модели, но хранятся отдельно от них и включают только бизнес-логику. Если хотите, назовите его сервисным уровнем «бизнес-эксперт».

service / ElementServices.js (обратите внимание, как элемент добавляется в декларации)

MyApp.service('ElementServices', function($http, $q, Element)
{
    this.getById = function(id)
    {
        return $http.get('/element/' + id).then(
            function(response)
            {
                //this is where the Element model is used
                return new Element(response.data);
            },
            function(response)
            {
                return $q.reject(response.data.error);
            }
        );
    };
    ... other CRUD methods
}

model / Element.js (с помощью angularjs Factory, созданной для создания объектов)

MyApp.factory('Element', function()
{
    var Element = function(data) {
        //set defaults properties and functions
        angular.extend(this, {
            id:null,
            collection1:[],
            collection2:[],
            status:'NEW',
            //... other properties

            //dummy isNew function that would work on two properties to harden code
            isNew:function(){
                return (this.status=='NEW' || this.id == null);
            }
        });
        angular.extend(this, data);
    };
    return Element;
});
Автор: Ben G Размещён: 13.02.2013 11:22

29 плюса

Документация Angularjs четко гласит:

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

- Руководство разработчика AngularJS - Концепции V1.5 - Модель

Так что это зависит от вас, как объявить модель. Это простой объект Javascript.

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

Автор: S.C. Размещён: 30.05.2013 10:30

8 плюса

DCI - это парадигма, и как таковой нет angularJS способа сделать это, либо язык поддерживает DCI, либо нет. JS довольно хорошо поддерживает DCI, если вы хотите использовать преобразование исходного кода, и с некоторыми недостатками, если вы этого не делаете. Опять же, DCI не имеет ничего общего с внедрением зависимостей, как, скажем, класс C # и определенно не является сервисом. Таким образом, лучший способ сделать DCI с помощью angulusJS - это сделать DCI способом JS, который довольно близок к тому, как в первую очередь формулируется DCI. Если вы не выполните преобразование исходного кода, вы не сможете сделать это полностью, поскольку методы ролей будут частью объекта даже вне контекста, но это, как правило, проблема с DCI на основе внедрения методов. Если вы посмотрите на fullOO.infoна официальном сайте DCI вы можете взглянуть на реализации ruby, в которых также используется внедрение метода, или вы можете посмотреть здесь дополнительную информацию о DCI. Это в основном примеры RUby, но материал DCI не зависит от этого. Один из ключей к DCI заключается в том, что то, что делает система, отделено от того, чем она является. Таким образом, объект данных довольно тупой, но после привязки к роли в контекстной роли методы делают доступным определенное поведение. Роль - это просто идентификатор, не более того, при доступе к объекту через этот идентификатор доступны методы роли. Там нет роли объекта / класса. С внедрением метода область действия ролевых методов не совсем соответствует описанию, но близка. Примером контекста в JS может быть

function transfer(source,destination){
   source.transfer = function(amount){
        source.withdraw(amount);
        source.log("withdrew " + amount);
        destination.receive(amount);
   };
   destination.receive = function(amount){
      destination.deposit(amount);
      destination.log("deposited " + amount);
   };
   this.transfer = function(amount){
    source.transfer(amount);
   };
}
Автор: Rune FS Размещён: 20.06.2012 05:56

7 плюса

Эта статья о моделях в AngularJS может помочь:

http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/

Автор: marianboda Размещён: 12.09.2013 04:03

5 плюса

Как утверждают другие авторы, Angular не предоставляет готового базового класса для моделирования, но можно с пользой предоставить несколько функций:

  1. Методы взаимодействия с RESTful API и создания новых объектов
  2. Установление отношений между моделями
  3. Проверка данных перед сохранением в бэкэнде; также полезно для отображения ошибок в реальном времени
  4. Кэширование и отложенная загрузка, чтобы избежать расточительных HTTP-запросов
  5. Конечные автоматы (до / после сохранения, обновления, создания, нового и т. Д.)

Одна библиотека, которая делает все эти вещи хорошо, - это ngActiveResource ( https://github.com/FacultyCreative/ngActiveResource ). Полное раскрытие - я написал эту библиотеку - и успешно использовал ее при создании нескольких приложений масштаба предприятия. Он хорошо протестирован и предоставляет API, который должен быть знаком разработчикам Rails.

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

Автор: Brett Cassette Размещён: 11.01.2014 07:44

5 плюса

Более старый вопрос, но я думаю, что тема более актуальна, чем когда-либо, учитывая новое направление Angular 2.0. Я бы сказал, что наилучшей практикой является написание кода с как можно меньшим количеством зависимостей от конкретной среды. Используйте только отдельные части фреймворка там, где это добавляет прямую ценность.

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

Вот пример шаблона, который я считаю хорошим для отделения объектной модели от DOM.

http://www.syntaxsuccess.com/viewarticle/548ebac8ecdac75c8a09d58e

Основная цель состоит в том, чтобы структурировать ваш код так, чтобы его было так же легко использовать из модульных тестов, как и из представления. Если вы достигнете этого, вы сможете написать реалистичные и полезные тесты.

Автор: TGH Размещён: 19.12.2014 04:54

4 плюса

Я попытался решить эту проблему в этом посте .

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

В статье рассматриваются три подхода: использование $ http , $ resource и Restangular .

Вот пример кода для каждого из них с пользовательским getResult()методом в модели Job:

Рестаугольный (легкий горох):

angular.module('job.models', [])
  .service('Job', ['Restangular', function(Restangular) {
    var Job = Restangular.service('jobs');

    Restangular.extendModel('jobs', function(model) {
      model.getResult = function() {
        if (this.status == 'complete') {
          if (this.passed === null) return "Finished";
          else if (this.passed === true) return "Pass";
          else if (this.passed === false) return "Fail";
        }
        else return "Running";
      };

      return model;
    });

    return Job;
  }]);

Ресурс $ (немного более замысловатый):

angular.module('job.models', [])
    .factory('Job', ['$resource', function($resource) {
        var Job = $resource('/api/jobs/:jobId', { full: 'true', jobId: '@id' }, {
            query: {
                method: 'GET',
                isArray: false,
                transformResponse: function(data, header) {
                    var wrapped = angular.fromJson(data);
                    angular.forEach(wrapped.items, function(item, idx) {
                        wrapped.items[idx] = new Job(item);
                    });
                    return wrapped;
                }
            }
        });

        Job.prototype.getResult = function() {
            if (this.status == 'complete') {
                if (this.passed === null) return "Finished";
                else if (this.passed === true) return "Pass";
                else if (this.passed === false) return "Fail";
            }
            else return "Running";
        };

        return Job;
    }]);

$ http (хардкор):

angular.module('job.models', [])
    .service('JobManager', ['$http', 'Job', function($http, Job) {
        return {
            getAll: function(limit) {
                var params = {"limit": limit, "full": 'true'};
                return $http.get('/api/jobs', {params: params})
                  .then(function(response) {
                    var data = response.data;
                    var jobs = [];
                    for (var i = 0; i < data.objects.length; i ++) {
                        jobs.push(new Job(data.objects[i]));
                    }
                    return jobs;
                });
            }
        };
    }])
    .factory('Job', function() {
        function Job(data) {
            for (attr in data) {
                if (data.hasOwnProperty(attr))
                    this[attr] = data[attr];
            }
        }

        Job.prototype.getResult = function() {
            if (this.status == 'complete') {
                if (this.passed === null) return "Finished";
                else if (this.passed === true) return "Pass";
                else if (this.passed === false) return "Fail";
            }
            else return "Running";
        };

        return Job;
    });

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

Модели данных AngularJS: $ http VS $ resource VS Restangular

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

Автор: Alan Christopher Thomas Размещён: 17.07.2014 07:34
Вопросы из категории :
32x32