Как отправить параметры OData источника данных KendoUI в абстрактную фабрику данных RESTful AngularJS?

Некоторое время назад я настроил абстрактную фабрику данных в AngularJS, и мне нужен совет, как упростить ее использование. Сначала у меня сложилось впечатление, что я создам еще одну фабрику для каждой сущности, которая будет вызываться из контроллера AngularJS. Оказывается, это выглядит слишком сложно, и я хотел бы удалить промежуточную фабрику сущностей (ContentTypesFactory) и просто вызвать абстрактную фабрику непосредственно из контроллера AngularJS.

В следующем примере я подключаю источник данных KendoUI.

АннотацияRepository.js:

app.factory('abstractRepository', [function () {

    // we will inject the $http service as repository service
    // however we can later refactor this to use another service
    function abstractRepository(repositoryService, whichEntity, odataUrlBase) {
        //this.http = $http;
        this.http = repositoryService;
        this.whichEntity = whichEntity;
        this.odataUrlBase = odataUrlBase;
        this.route;
    }

    abstractRepository.prototype = {
        getList: function () {
            return this.http.get(this.odataUrlBase);
        },
        get: function (id) {
            return this.http.get(this.odataUrlBase + '/' + id);
        },
        insert: function (entity) {
            return this.http.post(this.odataUrlBase, entity);
        },
        update: function (entity) {
            return this.http.put(this.odataUrlBase + '/' + entity.ID, this.whichEntity);
        },
        remove: function (id) {
            return this.http.delete(this.odataUrlBase + '/' + id);
        }
    };

    abstractRepository.extend = function (repository) {
        repository.prototype = Object.create(abstractRepository.prototype);
        repository.prototype.constructor = repository;

    }

    return abstractRepository;

}]);

ContentTypesFactory.js:

/// <reference path="../Scripts/angular.min.js" />
/// <reference path="../app.js" />
/// <reference path="AbstractRepository.js" />

// each function returns a promise that can be wired up to callback functions by the caller
// the object returned from the factory is a singleton and can be reused by different controllers
app.factory('contentTypesRepository', ['$http', 'abstractRepository', function ($http, abstractRepository) {

    var odataUrlBase = '/odata/ContentTypes'
    var whichEntity = 'ContentTypes';

    function contentTypesRepository() {
        abstractRepository.call(this, $http, whichEntity, odataUrlBase);
    }

    abstractRepository.extend(contentTypesRepository);

    return new contentTypesRepository();

}]);

ContentTypesController.js:

app.controller('contentTypesController', ['$scope', '$log', 'contentTypesRepository',
    function ($scope, $log, contentTypesFactory) {

        $scope.odataUrlBase = '/odata/ContentTypes';
        $scope.status;

        //
        // Grid
        //
        $scope.contentTypesDataSource = new kendo.data.HierarchicalDataSource({
            type: "odata",
            transport: {
                create: {
                    url: $scope.odataUrlBase
                },
                read: {
                    type: "GET",
                    url: $scope.odataUrlBase,
                    //function (data) {
                        // pass in the URL to the abstract factory                     
                    //},
                    dataType: "json"                                // the default result type is JSONP, but WebAPI does not support JSONP
                },
                update: {
                    contentType: "application/json",

                    url: function (data) {
                        return $scope.odataUrlBase + '(' + data.ContentTypesId + ')';
                    } . . . 

Теперь, удалив ссылку на ContentTypesFactory, мой новый код контроллера выглядит так:

app.controller('contentTypesController', ['http', '$scope', '$log', 'abstractDataFactory',
    // the abstract data factory accepts controller type parameters for RESTful CRUD

    function ($scope, $log, abstractDataFactory) {
        //function ContentTypeController($scope) {

        var crudServiceBaseUrl = "/odata/ContentTypes";

        var odataUrlBase = '/odata/ContentTypes'
        var whichEntity = 'ContentTypes';

        // Pulled the following code from the ControlerTypesFactory, but not sure how to implement it in this ContentTypesController:

        //function contentTypesRepository() {
        //    abstractRepository.call(this, $http, whichEntity, odataUrlBase);
        //}

        //abstractRepository.extend(contentTypesRepository);
        //return new contentTypesRepository();

        $scope.greeting = 'Hola!';



        var dataSource = new kendo.data.DataSource({
            type: "odata",
            transport: {
                read: {
                    url: crudServiceBaseUrl,
                    dataType: "json"
                },
                update: { // PUT
                    url: function (data) {
                        console.log(data);
                        dataType: "json"
                        return crudServiceBaseUrl + "(" + data.ContentTypeId + ")";
                    },
                    error: function (e) {
                        console.log("error: " + e);
                    }
                }, . . .

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


person ElHaix    schedule 10.02.2014    source источник


Ответы (2)


Взглянув на это сегодня утром свежим взглядом, я считаю, что у меня есть рабочее решение.

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

$http внедряется в фабрику, которая принимает два параметра whichEntity, odataUrlBase и устанавливается при создании экземпляра. Фабрика предоставляет такие методы, как getList(odataOptions). Именно эти параметры необходимо передавать вместе с запросами как часть строки запроса, и это делается через params $http.

Новая абстрактная фабрика внедряется в контроллер, а затем создается экземпляр с необходимыми параметрами:

app.controller('contentTypeController', ['$scope', '$log', 'abstractFactory3',

    function ($scope, $log, abstractFactory3) {

        var dataFactory = new abstractFactory3("ContentType", "/odata/ContentType");

Вот новая абстрактная фабрика:

app.factory('abstractFactory3', function ($http) {

    function abstractFactory3(whichEntity, odataUrlBase) {
        this.whichEntity = whichEntity;
        this.odataUrlBase = odataUrlBase;
    }

    abstractFactory3.prototype = {
        getList: function (odataOptions) {
            //var result = $http({               
            //    url: this.odataUrlBase,
            //    method: 'GET',
            //    params: odataParams
            //});

            return $http.get(this.odataUrlBase, {
                params: odataOptions
            });
        }
    };

    return abstractFactory3;
});

Поскольку это используется через kendo.data.DataSource, мы должны извлечь параметры OData:

var odataParams = kendo.data.transports["odata"].parameterMap(options.data, "read");

После успешного get мы возвращаем options источника данных с результатом:

    var dataSource = new kendo.data.DataSource({
        type: "odata",
        transport: {
            read:

                function (options) {
                    var odataParams = kendo.data.transports["odata"].parameterMap(options.data, "read");

                    dataFactory.getList(odataParams)
                        .success(function (result) {
                            options.success(result);
                        });

                    //$http({                           // // example of using $http directly ($http injection was removed from this controller, as this is now handled by the abstract data factory)
                    //    url: crudServiceBaseUrl,
                    //    method: 'GET',
                    //    params: odataParams
                    //})
                    //.success(function (result) {
                    //    options.success(result);
                    //});
                },  // update, create, destroy,  . . .

Я понимаю, что в моем первоначальном вопросе контроллер фактически использовал транспорт Kendo, а не фабрику Angular, которую я настроил.

Итак, что у меня сейчас есть:

контроллер angular (источник данных KendoUI) -> фабрика данных angular

Я также переформулировал вопрос, чтобы более точно отразить, в чем заключалась вся проблема, и надеюсь, что это кому-то поможет.

Есть и другие небольшие нюансы, такие как получение и передача идентификаторов данных, объектов, JSON.stringification, которые лучше всего показаны в полном решении. Надеюсь, следующее поможет:

Абстрактная фабрика данных AngularJS:

app.factory('abstractFactory3', function ($http) {

    function abstractFactory3(odataUrlBase) {
        this.odataUrlBase = odataUrlBase;
    }

    abstractFactory3.prototype = {
        getList: function (odataOptions) {
            //var result = $http({
            //    url: this.odataUrlBase,
            //    method: 'GET',
            //    params: odataParams
            //});

            return $http.get(this.odataUrlBase, {
                params: odataOptions
            });
        },
        get: function (id, odataOptions) {
            return $http.get(this.odataUrlBase + '/' + id, {
                params: odataOptions
            });
        },
        insert: function (data) {
            return $http.post(this.odataUrlBase, data);
        },
        update: function (id, data) {
            return $http.put(this.odataUrlBase + '(' + id + ')', data);
        },
        remove: function (id) {
            return $http.delete(this.odataUrlBase + '(' + id + ')');
        }
    };

    return abstractFactory3;
});

Контроллер AngularJS:

app.controller('contentTypeController', ['$scope', '$log', 'abstractFactory3',
    // the abstract data factory accepts controller type parameters for RESTful CRUD

    function ($scope, $log, abstractFactory3) {

        var dataFactory = new abstractFactory3("/odata/ContentType");

        var crudServiceBaseUrl = "/odata/ContentType";

        var dataSource = new kendo.data.DataSource({
            type: "odata",
            transport: {
                read:

                    function (options) {
                        var odataParams = kendo.data.transports["odata"].parameterMap(options.data, "read");

                        dataFactory.getList(odataParams)
                            .success(function (result) {
                                options.success(result);
                            })
                            .error (function (error) {
                                console.log("data error");
                            });

                    },
                update:
                    function (options) {
                        var data = options.data;
                        dataFactory.update(data.ContentTypeId, data)
                            .success(function (result) {
                                options.success(result);
                            })
                            .error(function (error) {
                                console.log("data error");
                            });
                },
                create:
                    function (options) {
                        var data = options.data;
                        data.ContentTypeId = "0";           // required for valid field data
                        dataFactory.insert(data)
                            .success(function (result) {
                                options.success(result);
                            })
                            .error(function (error) {
                                console.log("data error");
                            });
                },
                destroy: 
                    function (options) {
                        var data = options.data;
                        dataFactory.remove(data.ContentTypeId)
                            .success(function (result) {
                                options.success(result);
                            })
                            .error(function (error) {
                                console.log("data error");
                            });

                },
                parameterMap: function (options, type) {
                    // this is optional - if we need to remove any parameters (due to partial OData support in WebAPI
                    if (operation !== "read" && options.models) {
                        return JSON.stringify({ models: options });
                    }
                },

            },
            batch: false,
            pageSize: 10,
            serverPaging: true,
            change: function (e) {
                console.log("change: " + e.action);
                // do something with e
            },
            schema: {
                data: function (data) {
                    //console.log(data)
                    return data.value;
                },
                total: function (data) {
                    console.log("count: " + data["odata.count"]);
                    return data["odata.count"];
                },
                model: {
                    id: "ContentTypeId",
                    fields: {
                        ContentTypeId: { editable: false, nullable: true },
                        //UserId: {editable: false, nullable: false },
                        Description: { type: "string", validation: { required: true } },
                        //msrepl_tran_version: { type: "string", validation: { required: true } }
                    }
                }
            },
            error: function (e) {
                //var response = JSON.parse(e.responseText);
                var response = e.status;
                console.log(response);
            }

        });


        $("#grid").kendoGrid({
            dataSource: dataSource,
            pageable: true,
            height: 400,
            toolbar: ["create"],
            columns: [
                        { field: "ContentTypeId", editable: false, width: 90, title: "ID" },
                        { field: "Description", title: "Content Type" },
                        { command: ["edit", "destroy"] }
            ],
            editable: "inline"
        });


    }]);
person ElHaix    schedule 13.02.2014

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

person koolunix    schedule 10.02.2014
comment
Ясно, поэтому я должен создать отдельную фабрику контроллеров данных (фактически названную репозиторием с использованием абстрактной фабрики?) для каждого объекта, который использует этот абстрактный контроллер? Я надеялся избежать этого, но при этом оказалось, что это нарушает принцип единой ответственности дизайна SOLID — да? - person ElHaix; 11.02.2014
comment
Я думаю, что в angular единственная ответственность, о которой вы говорите, лежит на этих синглтонах, контроллерах и фабриках. Все проекты REST, которые я вижу в Интернете с angular, используют их, многие с сервисами $resource или $http — посмотрите, например: docs.angularjs.org/tutorial/step_11 dreamfactory.com/angularjs-example blog.pluralsight.com/angularjs-step-by-step-services www.twilio.com /blog/2013/12/votr-part-5-angularjs-crud-restful-apis.html - person koolunix; 11.02.2014
comment
Отлично читает. Я не предлагаю делать вызов RESTful из файла ContentTypesController. Я хотел бы получить некоторую информацию о том, следует ли удалять файл ContentTypesFactory. Кажется, я мог просто установить odataUrlBase и whichEntity в контроллере, который использует абстрактную фабрику. - person ElHaix; 11.02.2014