ng-repeat and ng-controller on the same DOM element

16,354

Solution 1

You should break your controller into UserListController and UserController. The list of users should be part of UserListController and the each item can be managed by UserController

Something like

<table ng-controller='UserListController'>
        <tbody ng-controller="UserController" ng-repeat="user in users" ng-click="toggleSelectedUser()" ng-switch on="isSelectedUser()" ng-init="user=user">

So the user controller becomes

angular.module("myApp", [])     
    .controller("UserController", ["$scope", function($scope) {
        $scope.selected = false;

        $scope.toggleSelectedUser = function() {
            $scope.user.selected = !$scope.selected;
        };

        $scope.isSelectedUser = function() {
            return $scope.user.selected;
        };
    }]);

Solution 2

What I want is the variable selected should be unique for each scope.

Yes you can.

Multiple controller way

You can create root controller mainController and add to your model users new controller per user.

After that, call new controller in ng-repeat as ng-controller="user.ctrl"

Demo Fiddle

I would write something like:

HTML

<div ng-controller="mainController">
    <table>
        <tbody ng-repeat="user in users" ng-controller="user.ctrl" ng-click="toggleSelectedUser()" ng-switch on="isSelectedUser()">
            <tr>
                <td>{{user.name}}</td>
                <td>{{user.email}}</td>
            </tr>
            <tr ng-switch-when="true">
                <td colspan="2" style="padding-left: 10px">{{user.desc}}</td>
            </tr>
        </tbody>
    </table>
</div>

JS

var fessmodule = angular.module('myModule', []);

fessmodule.controller('mainController', function ($scope) {
    $scope.users = [{
        ctrl: fooCtrlA,
        name: "AAAAA",
        email: "[email protected]",
        desc: "Description about AAAA"
    }, {
        ctrl: fooCtrlB,
        name: "BBBBB",
        email: "[email protected]",
        desc: "Description about BBBBB"
    }, {
        ctrl: fooCtrlC,
        name: "CCCCC",
        email: "[email protected]",
        desc: "Description about CCCC"
    }];
});

fessmodule.$inject = ['$scope'];

function fooCtrlA($scope) {
    $scope.selected = true;
    $scope.toggleSelectedUser = function () {
        $scope.selected = !$scope.selected;
    };
    $scope.isSelectedUser = function () {
        return $scope.selected;
    };
}

function fooCtrlB($scope) {
    $scope.selected = false;
    $scope.toggleSelectedUser = function () {
        $scope.selected = !$scope.selected;
    };
    $scope.isSelectedUser = function () {
        return $scope.selected;
    };
}

function fooCtrlC($scope) {
    $scope.selected = false;
    $scope.toggleSelectedUser = function () {
        $scope.selected = !$scope.selected;
    };
    $scope.isSelectedUser = function () {
        return $scope.selected;
    };
}

However you can see we have code duplicate!!. Each "child" controller has the same logic:

    $scope.selected = false;
    $scope.toggleSelectedUser = function () {
        $scope.selected = !$scope.selected;
    };
    $scope.isSelectedUser = function () {
        return $scope.selected;
    };

If you want to make your code work, I would use ng-model.

Other way with ng-model

Demo 2 Fiddle

HTML

<tbody ng-repeat="user in users" ng-click="toggleSelectedUser(user)" ng-switch on="isSelectedUser(user)">
            <tr ng-model="user">
                <td>{{user.name}}</td>
                <td>{{user.email}}</td>
            </tr>
            <tr ng-switch-when="true">
                <td colspan="2" style="padding-left: 10px">{{user.desc}}</td>
            </tr>
        </tbody>

and controller with modified model:

var fessmodule = angular.module('myModule', []);

fessmodule.controller('mainController', function ($scope) {
    $scope.users = [{
        selected: false,
        name: "AAAAA",
        email: "[email protected]",
        desc: "Description about AAAA"
    }, {
        selected: false,
        name: "BBBBB",
        email: "[email protected]",
        desc: "Description about BBBBB"
    }, {
        selected: false,
        name: "CCCCC",
        email: "[email protected]",
        desc: "Description about CCCC"
    }];


    $scope.toggleSelectedUser = function (user) {
        user.selected = !user.selected;
    };
    $scope.isSelectedUser = function (user) {
        return user.selected;
    };
});
Share:
16,354
Anup Vasudeva
Author by

Anup Vasudeva

I am passionate about .NET technologies, have pretty much exposure on C#, ASP.NET Web forms and MVC, WCF, WPF, Silverlight, JavaScript and jQuery.

Updated on July 24, 2022

Comments

  • Anup Vasudeva
    Anup Vasudeva almost 2 years

    Can we attach ng-controller and ng-repeat to the same DOM element? Fiddle

    Here is the HTML:

    <table>
        <tbody ng-controller="UserController" ng-repeat="user in users" ng-click="toggleSelectedUser()" ng-switch on="isSelectedUser()">
            <tr>
                <td>{{user.name}}</td>
                <td>{{user.email}}</td>
            </tr>
            <tr ng-switch-when="true">
                <td colspan="2">
                    {{user.desc}}
                </td>
            </tr>
        </tbody>
    </table>
    

    Here is the code:

    angular.module("myApp", [])     
        .controller("UserController", ["$scope", function($scope) {
            $scope.users = [
                {name : "Anup Vasudeva", email : "[email protected]", desc : "Description about Anup Vasudeva"},
                {name : "Amit Vasudeva", email : "[email protected]", desc : "Description about Amit Vasudeva"},
                {name : "Vijay Kumar", email : "[email protected]", desc : "Description about Vijay Kumar"}
            ];
            $scope.selected = false;
    
            $scope.toggleSelectedUser = function() {
                $scope.selected = !$scope.selected;
            };
    
            $scope.isSelectedUser = function() {
                return $scope.selected;
            };
        }]);
    

    I think it makes sense to bind ng-controller and ng-repeat to the same DOM element. The scope created by ng-repeat can be managed by the controller. What I want is the variable selected should be unique for each scope.

  • Anup Vasudeva
    Anup Vasudeva over 10 years
    Thanks for the reply. In your first solution, do you think there would be an issue if my number of users scales? In your second solution, the ng-controller and ng-repeat are in the different DOM element. Would it be possible to have both ng-controller and ng-repeat in the same DOM element?
  • Maxim Shoustin
    Maxim Shoustin over 10 years
    As you see if we talk about functionality 2nd way work as well. About 1st solution, you can't put main controller to ng-repeat. Because ng-repeat DOM element represents item aka user, not users. We need someone who will manage all users model. So as I know - no way.
  • Homer
    Homer almost 10 years
    How would this look using the "controller as" syntax?
  • Matthias
    Matthias over 9 years
    Can you explain the purpose of ng-init="user=user"? It seems redundant, but if the ngRepeat changed to ng-repeat="singleUser in users" then you could change it to ng-init="user=singleUser" without changing the controller code. Is that the only reason?