如何重构calendarViewModel,使其不与特定控制器绑定?

时间:2014-07-28 17:32:03

标签: javascript angularjs

对于一个小型的Angular.js测试平台项目,我设置了以下的plunker:

My Plunked Plunker

最初,当calendarViewModel直接包含在Angular控制器中时,我有一个本地版本的测试平台。

appControllers.controller('PageController', [
    '$scope', '$http', 'Enums', 'ViewModels',

    function ($scope, $http, Enums, ViewModels) {
        var calendarViewModel = function () {
            var pub = {};

            pub.date = new Date();
            pub.isOpen = false;

            pub.today = function () {
                if(pub.isOpen)
                    pub.date = new Date();
            };

            pub.clear = function () {
                if(pub.isOpen)
                    pub.date = null;
            };

            pub.hide = function () {
                pub.isOpen = false;
            };

            pub.toggle = function ($event) {
                $event.preventDefault();
                $event.stopPropagation();

                $scope.hideCalendars();
                pub.isOpen = !pub.isOpen;
            };

            return pub;
        };

        // Backing model for this 'controller'
        $scope.viewModel = {
            // Properties:
            startCalendar: new calendarViewModel(),
            endCalendar: new calendarViewModel(),

            // data:
            // Generates an object that is sent to the server with $http calls.
            data: function () {
                var object = {
                    startDate: startCalendar.date.toString(),
                    endDate: endCalendar.date.toString()
                };

                return JSON.stringify(object);
            }
        };

        // - Controller-specific functions... ----------------------------------
        $scope.hideCalendars = function () {
            $scope.viewModel.startCalendar.hide();
            $scope.viewModel.endCalendar.hide();
        };

        $scope.clear = function () {
            $scope.viewModel.startCalendar.clear();
            $scope.viewModel.endCalendar.clear();
        };

        $scope.today = function () {
            $scope.viewModel.startCalendar.today();
            $scope.viewModel.endCalendar.today();
        };

        // Restricts certain days from being selected.
        $scope.disableWeekends = function (date, mode) {
            return mode === 'day' 
                   && (date.getDay() === Enums.DaysOfTheWeek.Sunday 
                      || date.getDay() === Enums.DaysOfTheWeek.Saturday);
        };

        // This is a demonstration scope action.  Pretty much, the pattern
        // I found, is to have a view model expose a method that creates
        // a stringified JSON blob that we can send to the server.  This
        // method is how such a save function would work.
        $scope.save = function () {
            var promise = $http({
                method: 'POST',
                url: '/some/server/url',
                data: $scope.viewModel.data()
            });

            promise.success(function (data) {
                // Do something with the returned data?
            }).error(function (data) {
                // Do something with the error data?
            });
        };
        // - End of Controller-specific functions... ---------------------------

        // Picker-specific options...
        $scope.dateOptions = {
            'starting-day': Enums.DaysOfTheWeek.Monday,
            'format-day': 'dd',
            'format-month': 'MM',
            'format-year': 'yyyy',
            'min-mode': Enums.PickerMode.Day,
            'max-mode': Enums.PickerMode.Year
        };

        $scope.format = 'MM/dd/yyyy';
        $scope.today();
    }
]);

因为我将它重构为ViewModels常量对象,但我从Angular得到以下错误:

TypeError: undefined is not a function
at Object.pub.toggle (http://run.plnkr.co/AKUBdEb5M3KT5DM9/app.services.js:31:4)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:10185:21
at http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:17835:17
at Scope.$eval (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:11936:28)
at Scope.$apply (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:12036:23)
at HTMLInputElement.<anonymous> (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:17834:21)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:2613:10
at forEach (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:310:20)

我这样做的原因是因为理论上多个控制器可能需要与calendarViewModels绑定的日历(这就是我首先创建calendarViewModel功能对象的原因。)我希望calendarViewModel构造不与特定的控制器绑定,但是我通过这种方式重构它显然已经破坏了。

我认为我走在正确的轨道上,但无论如何,显然缺少某些东西。我的问题:对我来说,重构calendarViewModel的正确方法是什么,哪种方法有效并且更容易重复使用?

1 个答案:

答案 0 :(得分:1)

来自你的掠夺者的一些事情:

  • 不要使用app.constant来做工厂。请改用app.factory,例如:

_

appServices.factory('ViewModels', function() { 

    var pub = {};

    pub.date = new Date();
    pub.isOpen = false;

    pub.today = function () {
        if(pub.isOpen)
            pub.date = new Date();
    };

    pub.clear = function () {
        if(pub.isOpen)
            pub.date = null;
    };

    pub.hide = function () {
        pub.isOpen = false;
    };

    pub.toggle = function ($event) {
        $event.preventDefault();
        $event.stopPropagation();

        //hideAll();
        pub.isOpen = !pub.isOpen;
    };

    return pub;

});
  • 执行此操作时,您的工厂将在控制器之间自动共享:

_

appControllers.controller('FirstController', [ '$scope', 'MyCalendarService', function($scope, MyCalendarService){
  $scope.myCalendarService = MyCalendarService;

}]);

appControllers.controller('SecondController', [ '$scope', 'MyCalendarService', function($scope, MyCalendarService){
  $scope.myCalendarService = MyCalendarService;

}]);

...如果控制器是在html中并行定义的。如果它们是嵌套的,您只需要在顶层注入您的服务。理想情况下,您只需要在控制器中对DI进行一些服务并将它们分配给范围。

这会回答你的问题吗?

PS:hideAll没有定义在你的plunker中,我评论了它,事情开始起作用了。

编辑:这个经过编辑的plnkr可以做你想做的事:http://plnkr.co/edit/7VDYDQhK2CDGnwa8qhWf?p=preview