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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.