I know I'm missing the AngularJS way to do this correctly.
On mouseover
in my tagsPanel's view I capture the tag object and first send it into my tagsPanel controller:
tagsPanel.html
<ul>
<li ng-repeat="(k, m) in tags"
ng-mouseover="hoverTag(m)"
ng-mouseleave="leaveTag(m)">
<div class="tag">{{m.term}}</div>
<tags-hover ng-model="m"></tags-hover>
</li>
</ul>
tagsPanel Controller:
vs.hoverTag = function(tagObj) {
TagDetailsFactory.saveTagDetails(tagObj);
};
tagDetailsFactory The last step in my Factory here is what I'm concerned about:
tagsHover = ScopeFactory.getScope('tagsHover');
tagsHover.hoveringTag(tag);
Full Factory code:
(function() { "use strict";
angular.module('tagDetailsFactory', [])
.factory('TagDetailsFactory', [
'ApiFactory',
'ScopeFactory',
function(ApiFactory,
ScopeFactory) {
// Init ScopeFactory:
// ------------------
var tagsHover = {};
return {
saveTagDetails : saveTagDetails
};
////////////////////////////////////////////////////////////////////////
function saveTagDetails(tag) {
if (tag.percent != undefined) {
var conditional = tag.percent.toString().charAt(0);
tag.direction = returnDirection(conditional);
}
function floorFigure(figure, decimals) {
if (!decimals) decimals = 2;
var d = Math.pow(10, decimals);
return (parseInt(figure * d)/d).toFixed(decimals);
};
ApiFactory.getTagData(tag.term_id).then(function(data) {
/*
Ton of tag updating code here
// Algorithm to get percentage change:
increase = tag.quantity - tag.previous_quantity;
increase = increase / tag.previous_quantity * 100;
increase = floorFigure(increase, 2);
etc etc...
*/
tagsHover = ScopeFactory.getScope('tagsHover');
tagsHover.hoveringTag(tag);
});
};
}]);
})();
^ As you can see above, I crudely grab the scope of the tagsHover directive, then target a function in there and pass on the updated tag
object.
Below is my entire tagHoverDirective
and markup, what would be the ideal Angular Way to pass the updated tag object into the tagHoverDirective
function hoveringTag
?
(function() { "use strict";
angular.module('tagHoverDirective', [])
.directive('tagsHover', function() {
return {
templateUrl : "tags/tagsHover.html",
restrict : "E",
replace : true,
link: function($scope, el, attrs) {
// console.debug($scope, attrs);
},
scope:{
tag:'=ngModel'
},
controller : ['$scope',
'ScopeFactory',
'TagDetailsFactory',
function($scope,
ScopeFactory,
TagDetailsFactory) {
// Init tagsHover scope:
// ---------------------
var vs = $scope;
ScopeFactory.saveScope('tagsHover', vs);
vs.hoveringTag = function(t) {
t.tagsHoverDisplay = true;
};
vs.leavingTag = function(t) {
t.tagsHoverDisplay = false;
};
}]
}
});
})();
Markup for tagHover.html
<div class="tags-hover-container" ng-show="tag.tagsHoverDisplay">
<div class="tag-info-padding">
<div class="tweets" ng-hide="!tag.quantity">
<div class="percentage" ng-hide="tag.length">
{{tag.tweet_percentage}}%
</div>
<div class="tweet-count">{{tag.quantity}} tweets</div>
</div>
<div class="tweets-direction" ng-show="!tag.quantity">
0 tweets
</div>
</div>
</div>
I hacked together a basic example of notifying the directive of an update via callback based on what type of tag it's interested in observing. I put in three text fields, the identifier text field is the one that actually matters.
First the markup, which is just an empty directive, whose template will render without the tag data initially, the form will tag in the values that we'll be storing.
<sample-directive></sample-directive>
<form ng-submit="addNewTag()">
<input placeholder="tag type identifier" type="text" ng-model="tag.tagIdentifier">
<input placeholder="tag name" type="text" ng-model="tag.tagName">
<input placeholder="tag message" type="text" ng-model="tag.tagMessage">
<input type="submit" value="Add new tag">
</form>
The controller for adding the tag
.controller('mainCtrl',function($scope, sampleService) {
$scope.tag = {
tagName: "",
tagIdentifier: "",
tagMessage: ""
}
$scope.addNewTag = function() {
var tag = {};
tag.message = $scope.tag.tagMessage;
tag.name = $scope.tag.tagName;
sampleService.setTag(tag, $scope.tag.tagIdentifier);
}
})
The main idea is here
.directive('sampleDirective', function(sampleService) {
return {
restrict: 'E',
link: function(scope, iElement, iAttrs) {
sampleService.registerTagCallback("tagsHover",tagCallback);
function tagCallback(tag) {
scope.tag = tag;
}
},
template: "<div>{{tag.message}}</div>"
}
})
This directive (when it's registered and it's link function is activated) will use that service to register a callback that should execute inside your factory when you set a tag from inside of that factory. That could be done like so
.service('sampleService', function() {
return {
setTag: setTag,
registerTagCallback, registerTagCallback
}
var callbacks;
var tags;
function setTag(tag, identifier) {
if (!tags || tags.length == 0)
tags = [];
tags.push({
tag: tag,
identifier: identifier
});
angular.forEach(callbacks, function(callback) {
if (callback.identifier == identifier) {
callback.method(tag);
}
});
}
function registerTagCallback(identifier, callback) {
if (!callbacks || callbacks.length == 0)
callbacks = [];
callbacks.push({
identifier: identifier,
method: callback
});
}
})
Everytime you set a tag, you check your registered callbacks to see if anyone is interested in receiving the tag, and invoke that callback with the tag that was passed in. The directive then updates it's scope internally from the registered callback functions param.
Here's a fiddle I put together with a working example. the tag identifier needs to match the directives callback identifier (which is "tagsHover" in this case), then you should see the directive update with the name and message of the tag