Reputation: 2418
I'm trying to implement my game in canvas to angular directive, but I can't figure out how to implement my class "prototype"
my code is in coffeeScript but javascript is not a problem :)
my directive:
angular.module("tetris", []).directive("tetris", ($timeout) ->
restrict: "E"
transclude: true
scope: {}
link: ($scope, $element, $log) ->
game = new Game($element.children()[0],$element.children()[1],$element.children()[2])
game.initGame()
document.onkeydown=(e)->
game.keyDown(e);
document.onkeyup=(e)->
game.keyUp(e);
return
template:"<div class=\"row relative\" id=\"canvas\">"+
"<canvas class=\"absolute\" width=\"120\" height=\"250\" style=\"z-index:1\">"+
"Your browser is not supporting canvas"+
"</canvas>"+
"<canvas class=\"absolute\" width=\"120\" height=\"250\" style=\"z-index:2\"></canvas>"+
"<canvas class=\"absolute\" width=\"120\" height=\"250\" style=\"z-index:3\"></canvas>"+
"</div>"
replace: true
)
and this is my eventHandler class which i want to implement to directive to just promote variables and events to scope. This class is called from Game class.
basically I want promote variables from Game prototype to parent scope of directive
class EventHandler
constructor:->
@scoreElm = document.getElementById('score')
@levelElm = document.getElementById('level')
@bombElm = document.getElementById('bomb')
@nextElm = document.getElementById('next')
updateScore:(score)->
@scoreElm.innerHTML=score
setBombCountDown:(time)->
@bombElm.style.display.block
@bombElm.innerHTML=time
hideBombCountDown:->
@bombElm.style.display.none
//EDIT:
I figured out how to do it, but I don't feel that this is a right way. Do you have any suggestion how to do it better?
angular.module("tetris", []).directive("tetris", ($timeout) ->
restrict: "E"
transclude: true
scope: false
link: ($scope, $element, $log) ->
class EventHandler
updateScore:(score)->
$scope.score = score
$scope.$apply()
setBombCountDown:(time)->
$scope.bombTime=time
$scope.$apply()
hideBombCountDown:->
$scope.bombTime=null
$scope.$apply()
game = new Game($element.children()[0],$element.children()[1],$element.children()[2],EventHandler)
game.initGame()
document.onkeydown=(e)->
game.keyDown(e);
document.onkeyup=(e)->
game.keyUp(e);
return
template:"<div class=\"row relative\" id=\"canvas\">"+
"<canvas class=\"absolute\" width=\"120\" height=\"250\" style=\"z-index:1\">"+
"Your browser is not supporting canvas"+
"</canvas>"+
"<canvas class=\"absolute\" width=\"120\" height=\"250\" style=\"z-index:2\"></canvas>"+
"<canvas class=\"absolute\" width=\"120\" height=\"250\" style=\"z-index:3\"></canvas>"+
"</div>"
replace: true
)
Upvotes: 1
Views: 743
Reputation: 325
Right now your directive is manipulating its parent scope by adding arbitrary members to it directly, which creates a sort of implicit interface between your directive and the outside world. This approach is prone to errors and is hard to maintain.
When building reusable directives it's common to use an isolate scope that exposes its members via attributes on the directive's element. This way the parent scope can decide which of its attributes to bind with your directive's members.
As an example to illustrate this, here's how you could restructure your tetris
directive:
.directive('tetris', function ($timeout) {
return {
restrict: 'E',
scope: {
score: '=',
bombTime: '='
},
link: function (scope, el) {
// Same code as in your example
}
};
};
The major difference here is that the scope is isolate
(meaning our directive has no way to "pollute" the parent scope) and we have the score
and bombTime
attributes bound to attributes on our directive's dom element, which the parent scope can access safely.
Note - There doesn't seem to be a reason for using transclude: true
, since your directive doesn't incorporate the contents given to it by the parent view into its behavior in any way.
Using the directive now looks like this:
<tetris score="myScore" bomb-time="myBombTime"></tetris>
<span>Score: {{myScore}}</span>
<span>Time: {{myBombTime}}</span>
Now the user of your directive can choose which scope members to associate with the game's score
and bombTime
attributes (myScore
and myBombTime
in this example to illustrate that they belong to the parent scope).
While this approach to writing directives may seem too verbose at first, it's important if you ever plan to change the structure of the page your directive lives in, place your directive into a page which has already been set up with different scope members for keeping score and time, or if you plan to write unit tests for your code.
Upvotes: 1