Alexandre
Alexandre

Reputation: 3170

Reused ng-controller as singleton

I understand that ng-controller create a new instance of controller. How could I reused the controller in different place of my template and keep the same model.

I try to find out by myself but I am new in AngularJs and a bit lost...

See my example in Plunker: http://plnkr.co/edit/VGLEcdlY4IaAJmJPWhQ7?p=preview

HTML

<body ng-app="app">
    <div ng-controller="test">
        <label ng-click="test()">Test: {{ name }}</label><br/>
        <input type="text" ng-model="name" />
    </div>
    <p>Here some other ng-controller...</p>
    <div ng-controller="test">
        <label ng-click="test()">Test: {{ name }}</label><br/>
        <input type="text" ng-model="name" />
    </div>
</body>

Js

angular
.module('app', [])
.controller('test', function($scope) {
    $scope.name = 'hello world';
    $scope.test = function(){ alert('alert'); };
})

The 2 fields are not connected to the same model/scope, how can make this singleton and have the same model for both input.

Upvotes: 1

Views: 1771

Answers (4)

hally9k
hally9k

Reputation: 2583

Think carefully about what you're doing here. A controller shouldn't hold state. It should be repeatable and stateless. If you want to persist state I suggest you use a service. Services are singletons by design.

Upvotes: 0

webmak10
webmak10

Reputation: 519

If i whant to use controler as singleton I do some like this(this work for me in angular 1.4.3)

(function (app) {
    var AbstractController = app.controllers.AbstractController;
    MessageBoxController.static = {
        options: AbstractController.static.createOptions([
            '$ionicSideMenuDelegate',
            MessageBoxController
        ]),
        pluginFolderName: 'message_box',
        hooks: [
            {hook: 'leftMenu', template: 'message_list.html'},
            {hook: 'topBarLeft', template: 'main.html'}
        ]
    };
    var instance;
    function MessageBoxController() {
        if(instance){
            return instance;
        }
        instance = this;
        var mp = this;
        mp.messagesNb = 0;
        mp.test = 'blabla';
        extend(mp, new AbstractController());
        function _init(params) {
            mp._parentInit(params, MessageBoxController.static.options);
            mp.init();
        }
        mp.init = function () {
            mp.showMsgButton = false;
            mp.toggleLeftMenu = mp.toggleLeftMenu;
            mp.messages = {};
            mp.getMessageCSSClass = mp.getMessageCSSClass;
            mp.clearMsg = mp.clearMsg;
            mp.registerMessageWatching();
        };
        mp.registerMessageWatching = function () {
            mp.unregisterWatchintInterval('MessageBoxController');
            var interval = setInterval(mp.checkMessages, 1000);
            mp.registerWatchingInterval('MessageBoxController', interval);
        };
        mp.checkMessages = function () {
            var messages = mp.getAllMsgs();
            mp.messages = messages;
            var messagesCount = messages.length;
            if (mp.messagesNb === messagesCount) {
                return;
            }
            if (messagesCount > 0) {
                mp.$scope.$apply(function () {
                    mp.showMsgButton = true;
                });
            } else {
                mp.$scope.$apply(function () {
                    mp.showMsgButton = false;
                });
            }
            mp.messagesNb = messagesCount;
        };
        mp.toggleLeftMenu = function () {
            mp.$rootScope.$root.leftMenu = !mp.$rootScope.$root.leftMenu;
            if(mp.$rootScope.$root.leftMenu){
                mp.$rootScope.buff.title = mp.$rootScope.$root.title;
                mp.$rootScope.$root.title = app.l('Messages');
            }else{
                mp.$rootScope.$root.title = mp.$rootScope.buff.title;
            }
        };
        mp.getMessageCSSClass = function(type){
            switch(type){
                case app.messagesTypes.inform:
                    return 'confirm_msg';
                    break;
                case app.messagesTypes.error:
                    return 'error';
                    break;
                case app.messagesTypes.warning:
                    return 'warning';
                    break;
            }
        };
        mp.clearMsg = function(key){
           mp.messages.splice(key, 1);
           mp.messagesNb--;
           if(mp.messagesNb < 1){
               mp.$rootScope.$root.leftMenu = false;
               mp.$rootScope.$root.title = mp.$rootScope.buff.title;
           }
        };
        _init(arguments);
    }
    app.controllers.MessageBoxController = MessageBoxController;
})(deliveryManagerGlobal);

And in template write like this

<div ng-controller="MessageBoxController as msgBox" class="msg_box">
    <div class="card" ng-repeat="(key, message) in msgBox.messages">
        <div class="item item-text-wrap" ng-class="msgBox.getMessageCSSClass(message.type)">
            {{message.context}}
        </div>
        <div class="close ion-android-close" ng-click="msgBox.clearMsg(key)"></div>
    </div>
</div>

If you will write like this you do not use $scope( angular is create for every ng-controller new scope) and you can use singelton. sou you can add new dom element with same ng-controller="MessageBoxController as msgBox" and it will use your prev instance. For now I have not see collateral bugs in my app structure. I have use dinamic hooks managmet so I was needed singleton and this work for me good

Upvotes: 0

Rasalom
Rasalom

Reputation: 3111

I had similar case too, and I think the only thing you can do is using service.

angular
.module('app', [])
.controller('test', function($scope, singleton) {
    $scope.name = singleton.getField();


}).service('singleton', function(){
  var field = {value : 'hello world'};


  var getField = function(){
    return field;
  }

  return {
    getField : getField
  };

});

Here I used 'field' as an object because in this case you'll have link to the same object in both controllers.

http://plnkr.co/edit/8yXY2qsIqcBtTqZDUxSa?p=preview

Upvotes: 3

Davin Tryon
Davin Tryon

Reputation: 67296

By far the most simple way is to move the ng-controller attribute up to the parent element:

<body ng-app="app" ng-controller="test">
    <div>
        <label ng-click="test()">Test: {{ name }}</label><br/>
        <input type="text" ng-model="name" />
    </div>
    <p>Here some other ng-controller...</p>
    <div>
        <label ng-click="test()">Test: {{ name }}</label><br/>
        <input type="text" ng-model="name" />
    </div>
</body>

Updated Plunker

Upvotes: 0

Related Questions