2

I'm learning angular by making a browser-based Sudoku solver. There are many ways to solve my problem, but I'm looking for the most angular way to do it.

My html creates a 9x9 grid of text inputs, using a nested ng-repeat. The behavior that I'm looking to achieve is this: after a user enters a number into a text input (which uses a directive called "grid-cell"), an onBlur event will fire and the number will be added to the controller's 9x9 array (identified in the controller as $scope.grid, which initializes as an array of arrays of nine empty strings)

My html looks like this. I feel reasonably good about it:

<div ng-controller="SudokuController">
    <table>
        <tr ng-repeat="row in dimension">
            <td ng-repeat="col in dimension">
                <grid-cell row="{{ row }}" col="{{ col }}"></div>
            </td>
        </tr>
        <tr>
            <td id="submit" colspan="9">
                <button ng-click="submit()" id="submit">Submit</button>
            </td>
        </tr>
    </table>
</div>

My angular code looks like this (I don't feel like I should need much more than a single controller and a directive):

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

app.controller('SudokuController', function($scope) {
    $scope.dimension = [1,2,3,4,5,6,7,8,9];
    $scope.grid = [];
    for (var i = 1; i<=$scope.dimension.length; i++) {
        $scope.grid[$scope.grid.length] = ['','','','','','','','',''];
    }
    $scope.addNumber = function(row, col, val) {
        $scope.grid[row][col] = val; // I would like it to work this way
    };
    $scope.submit = function() {
        // insert sudoku-solving algorithm here
    };
});

app.directive('gridCell', function() {
    return {
        restrict: "EA",
        template: '<input type="text" />',
        replace: true,
        scope: {
            row: '@',
            col: '@'
        },
        link: function(scope, elem, attrs) {
            elem.bind('blur', function() {
                //how do I correctly write this?
                scope.addNumber(scope.row, scope.col, elem[0].val);
            });
        }
    }
});

This does not work; using elem.bind() in link on the directive can't seem to talk to the controller. However, I'm not even sure if this is even the "right" way to approach it. I am also wondering if I should be doing something like this:

<td ng-repeat="col in dimension">
    <grid-cell row="{{ row }}" col="{{ col }}" ng-blur="addNumber(row, col, getThisValueSomehow)"></div>
</td>

Thanks in advance.

1

2 Answers 2

1

you can update the grid inside the grid-cell directive:

app.controller('MainCtrl', function($scope) {
    $scope.dimension = [1,2,3,4,5,6,7,8,9];
    $scope.grid = [];
    for (var i = 1; i<=$scope.dimension.length; i++) {
        $scope.grid[$scope.grid.length] = ['','','','','','','','',''];
    }
    $scope.submit = function() {
        // insert sudoku-solving algorithm here
    };
});

app.directive('gridCell', function() {
    return {
        restrict: "EA",
        template: '<input type="text" ng-model="value" />',
        replace: true,
        scope: {
            row: '&',
            col: '&',
            grid: '='
        },
        link: function(scope, elem, attrs) {
            elem.bind('blur', function() {                   
                scope.$apply(function () {
                  if (scope.value)
                  {
                    scope.grid[scope.row()][scope.col()] = scope.value;
                  }
                });                    
            });
        }
    }
});

html:

<tr ng-repeat="row in dimension">
  <td ng-repeat="col in dimension">
    <grid-cell row="row" col="col" grid="grid"></grid-cell>
  </td>
</tr>

http://plnkr.co/edit/peDjFbKGm6ydO4E45b5u?p=preview

Sign up to request clarification or add additional context in comments.

1 Comment

This DOES work nicely. I would like to know to easily and cleanly call a controller function though, I could see myself using that by more than one directive. Also, I'd like to keep the grid inside the controller, it doesn't add value being mentioned as an attribute.
0

try the third method from: http://ngtutorial.com/create/ways-to-pass-variable-from-directive-to-controller.html#/toc_4

The example uses an alias, so the controller's instance can be reference from the scope.

<!DOCTYPE HTML>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.3/angular.min.js"></script>
<script type="text/javascript">

var app = angular.module("MyApp", []);

//
// our controller
//
app.controller("MyController", MyController)
function MyController($scope) {

    var _private_var1 = "**value is not set**";

    this.MyMethod = function(var1) {
        _private_var1 = var1;

   }

    this.GetVar1 = function() {
        return _private_var1;
    }

}

app.directive({
    "mySampleBtn": mySampleBtnDirective,
    "mySampleTxt": mySampleTxtDirective
});

//
// our directives
//
function mySampleBtnDirective(){
    return {
        restrict: 'EA',
        template: "<button ng-click='CallMethod()'>{{name}}</button>",
        link: function(scope, element) {

            var ctrl = element.attr('ctrl');
            scope.name = element.attr('name');

            scope.CallMethod = function() {             
                alert( scope[ctrl].GetVar1() );
            }

        }
    }
};

function mySampleTxtDirective(){
    return {
        restrict: 'EA',
        template: "<input ng-model='theText'/>",
        link: function(scope, element) {

            var ctrl = element.attr('ctrl');

            scope.$watch("theText", function(newVal){
                scope[ctrl].MyMethod( scope.theText );
            });


        }
    }
};

</script>

<body ng-app="MyApp">

<div ng-controller="MyController as ctrlName">
    theText: <my-sample-txt ctrl="ctrlName"></my-sample-txt>, invokes <i>ctrlName.MyMethod( theText )</i><br/>
    <br/>
    <my-sample-btn name="Sample One" ctrl="ctrlName"></my-sample-btn>, invokes <i>alert( ctrlName.GetVar1() )</i><br/>
    <br/>
    => ctrlName.GetVar1() = {{ ctrlName.GetVar1() }}
</div>

</body>

</head>
</html>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.