Reputation: 1696
I think what I am trying to do is fairly simple, but I can't seem to determine the correct architecture using Angular.
I'd like to have a MessageCenter, so that any controller or other piece of Angular code can add a message. I thought that the correct way to do this would be with a MessageCenter service. I would inject the service where needed, and call MessageCenter.add(). The problem that I am having is how to compile the cloned DOM directive, since $compile requires a $scope, which I do not have from a service.
Here is what I am trying. I am not sure what the correct approach is for this.
<div>
<div id="msg-proto" message-center-msg class="alert alert-success"></div>
</div>
Then:
.factory('MessageCenter', function($compile) {
var that = {}
that.add = function(type, message) {
var n = $("#msg-proto"),
n2 = n.clone();
n.parent().append($compile(n2));
}
return that;
});
And I am injecting and calling the service:
.controller('SomeCtrl', function(MessageCenter) {
MessageCenter.add('some msg');
});
I have a message-center-msg
directive defined that adds some behaviors to the element. But it needs to be $compile
'd in order for that to happen, and I am not sure how to make this all work. I only ever end up with "Error: Argument 'scope' is required" from the service.
How can I setup a global message center, that is able to clone/add messages and have the directive on the message dom element processed?
Upvotes: 2
Views: 4139
Reputation: 21
I implemented something pretty identical a while back and recently made this public as a bower component. Maybe you can use or work off of it: https://github.com/IanShoe/angular-message-center
Happy coding!
Upvotes: 2
Reputation: 3826
Your service shouldn't interact with the DOM directly as you're trying to do. If I were designing such a system I would:
Here's what I mean (plunk here: http://plnkr.co/Eps1Gy)
index.html:
<!doctype html>
<html>
<head>
<script src="bower_components/angular/angular.js"></script>
<script src="scripts/app.js"></script>
<script src="scripts/controllers/messages.js"></script>
<script src="scripts/controllers/click.js"></script>
<script src="scripts/services/messageService.js"></script>
</head>
<body ng-app="messagesApp">
<h1>Messages</h1>
<div ng-controller="MessagesCtrl">
<ul ng-repeat="message in messages">
<li>{{message}}</li>
</ul>
</div>
<a ng-href="#" ng-controller="ClickCtrl" ng-click="addMessage()" >Add a new message!</a>
</body>
</html>
messageService.js:
function MessageService() {
this.messages = [];
this.add = function(msg) {
this.messages.push(msg);
}
}
angular.module('messagesApp')
.service('messageService', MessageService);
messages.js:
angular.module('messagesApp')
.controller('MessagesCtrl', function ($scope, messageService) {
$scope.messages = messageService.messages;
});
click.js:
angular.module('messagesApp')
.controller('ClickCtrl', function ($scope, messageService) {
var count = 0;
$scope.addMessage = function() {
messageService.add('Test message ' + count++);
}
});
app.js:
angular.module('messagesApp', [])
.config(function ($routeProvider) {
$routeProvider
.otherwise({
redirectTo: '/'
});
});
Like I said, you can replace the HTML for handling the messages with a custom directive if you want some complex interaction, but the important thing is that you don't try to muck with the view from your service. The service should interact only with a well-defined model.
Upvotes: 3
Reputation: 576
You can open your mind :)
On your call to the MessageCenter move the $scope as parameter like MessageCenter.add("", $scope);
Or ... you can expose the MessageCenter to the scope globally on startup something like this guy suggested
Upvotes: 0