I have a directive for flashmessages
'use strict';
angular.module('diplomovaPraceFrontendApp')
.directive('flashMessages', () ->
directive:
restrict: 'E'
replace: true
template: '<div ng-repeat="m in messages" id="flash-messages">' +
'<div class="alert {{m.level}}">' +
'<span class="">{{m.text}}</span>' +
'</div>' +
'</div>'
controller: ($scope, $rootScope) ->
$rootScope.$on('flash:message', (_, messages, done) ->
$scope.messages = messages
done()
)
)
and when I call in my controller $rootScope.$emit('flash:message', messages, someFunction); it isn't caught by $rootScope.$on() set up in directive, although if I put this in application.run() it works correctly.
Any ideas what I'm missing? Thanks for any advice
I have edited the question:
I do of course use an shared service, here's my code http://pastie.org/private/es25rvo0zvejuw9yx3acja (sorry, gist.github.com seems to be broken for me atm)
I was following this tutorial http://chiragchamoli.com/post/61277736964/building-a-flash-message-with-angular-js
Though it seems it doesn't call the directive at all, since replace is set to true and I still see <flash-messages> in code inspector.
Plunkr version: http://plnkr.co/edit/buRaguEyTMuhUM9Z4Jsw?p=preview
I already gave a fix in #angularjs earlier, but here it is for posterity:
http://plnkr.co/edit/Fb9FYSXgU0t93w7i2B8q?p=preview
The problem is that MainCtrl is instantiated before the directive, so the $scope event gets fired before the directive sets a listener on $scope, so the directive never gets the event listened to here.
The problem is that your non directive controller function is called before the directive controller. Thus, the message is being sent before the the directive has registered for alerts.
A simple solution to this would be, instead of using events, use a shared service. Services are singletons, so any state is shared between all usages of that service. Using a service only makes sense if your all of your flashMessage directives need a shared state. If this solution does not fit your needs, help me better understand your requirements.
Working plunker
Javascript:
var app = angular.module('plunker', [])
.controller('MainCtrl', function ($scope, alertsService) {
alertsService.add({
text: 'I am an alert',
level: 'high'
});
})
.service('alertsService', function () {
this.alerts = [];
this.add = function (message) {
this.alerts.push(message);
}.bind(this);
})
.directive('flashMessages', function (alertsService) {
return {
restrict: 'E',
replace: true,
template: '<div ng-repeat="m in messages" id="flash-messages">' +
'<div class="alert {{m.level}}">' +
'<span class="">{{m.text}}</span>' +
'</div>' +
'</div>',
scope: true,
link: function ($scope) {
$scope.messages = alertsService.alerts;
}
};
});
HTML:
<body ng-controller="MainCtrl">
<flash-messages></flash-messages>
</body>
Related
I can't get an ng-if to evaluate inside a directive. I was first calling a method that returned a boolean, but whatever the result the ng-if does not pick up the result, it always evaluates to false.
To try and pin point the issue I tried a simple inline expression (below) but even this always evaluates to false. When I remove the ng-if, the div shows. What am I doing wrong?
.directive('handsUpVideoOverlay', function() {
return {
restrict: 'E',
replace: true,
scope: false,
template: '<div class="handsOffOverlay" ng-if="1>0">' +
'</div>',
link: function($scope) {
}
};
})
UPDATE
This code works perfectly as a standalone Jsfiddle. It looks like the problem is something to do with the fact I am adding this directive as a child of another directive. The parent directive is 3rd party and is manually transcluding it's children.
UPDATE
Ok I got this working. The problem was the parent directive was removing any child directive and then adding it back without compiling them. I had to take my own copy of the 3rd party parent directive and change this in the link function:
// Make transcluding work manually by putting the children back in there
ng.element(element).append(oldChildren);
to this:
// Make transcluding work manually by putting the children back in there
for(var i = 0; i < oldChildren.length; i++) {
var template = oldChildren[i].outerHTML;
var linkFn = $compile(template);
var content = linkFn($scope);
$element.append(content);
}
UPD:
Since the question leads to directive in directive, and without $compile, angular won't know about the ng-if directive.So in the link function, build the elements to let template cimpiled.
OLD ANSWER(can ignore)
Your directive isn't end or you didn't post the entire code block.
This work as expected.
var app = angular.module("app", []);
app.controller("myCtrl", function($scope) {
$scope.test = 111;
});
app.directive('handsUpVideoOverlay', function() {
return {
restrict: 'E',
replace: true,
scope: false,
template: `<div class="handsOffOverlay" ng-if="1>0">Test Directive</div>`,
link: function($scope) {
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl">
<hands-up-video-overlay></hands-up-video-overlay>
</div>
Solved please check Jsfiddle link
Js code
app.directive('handsUpVideoOverlay', function() {
return {
restrict: 'E',
replace: true,
scope: false,
template: '<div class="handsOffOverlay" ng-if="1>0">lol' +
'</div>',
link: function($scope) {
}
};
});
html
<hands-up-video-overlay></hands-up-video-overlay>
Hope this will help you
I am creating a custom directive in AngularJS. This directive should open a popup to display data. The code for the popup is in another html page and the custom directive injects the code into my main page. I am able to open the popup however I cannot display the existing data anywhere in the pop up.
Normally, I am able to display the data in the main page however the data just do not want to go into the html injected by the custom directive.
Like this I do not get any error however it does not pass the data.
Note: I had to trim some of the code here to simplify it.
This is my custom directive:
function updateCandidatePopup() {
var directive = {};
directive.restrict = "E";
directive.scope = {};
directive.templateUrl = "UpdateCandidatePopup.html";
directive.controller = function ($scope) {
$scope.SingleCandidate;
}
return directive;
}
This is where I register it:
myApp.directive("updateCandidatePopup", UpdateCandidatePopup);
This is how I use the directive in the mainpage
<update-candidate-popup value="SingleCandidate" class="modal fade" ng-model="SingleCandidate"
id="myUpdateModal"
role="dialog"
popup-data="SingleCandidate">
zxc</update-candidate-popup>
This is the UpdateCandidatePopup.html:
<div> {{SingleCandidate.FirstName}} </div>
This is the to display the data in the pop up controller: (FYI it is still trimmed)
myApp.controller('CandidatesController', function ($scope, $http, EmployerService, CandidateService) { //we injected localservice
//Select single data for update
$scope.getSingleData = function (C_ID) {
alert(C_ID);
$http.get('http://localhost:49921/api/Candidates/?C_ID=' + C_ID).success(function (data) {
$scope.SingleCandidate = data;
$scope.FName = $scope.SingleCandidate.FirstName;
alert($scope.SingleCandidate.FirstName);
alert($scope.FName);
}).error(function () {
$scope.error = "An Error has occured while loading posts!";
});
};
});
Sorry wrong !, answered your question, here I leave I found a code that will serve for your problem. In the background to the template you want to take, you let a controller and in the statement of your policy, put you going to do with those values, I think in your case is just printing.
myApp.directive('editkeyvalue', function() {
return {
restrict: 'E',
replace: true,
scope: {
key: '=',
value: '=',
accept: "&"
},
template : '<div><label class="control-label">{{key}}</label>' +
'<label class="control-label">{{key}}</label>' +
'<input type="text" ng-model="value" />'+
'<button type="button" x-ng-click="cancel()">CANCEL</button>' +
'<button type="submit" x-ng-click="save()">SAVE</button></div>',
controller: function($scope, $element, $attrs, $location) {
$scope.save= function() {
console.log('from directive', $scope.key, $scope.value);
$scope.accept()
};
}
}
});
jsFiddle
Solved the problem like below. It was only to inject to $scope in the directive controller.
myApp.directive("updateCandidatePopup", function () {
return {
templateUrl : "UpdateCandidatePopup.html",
restrict: 'E',
controller: function ($scope) {
}
}
});
I'm having troubles updating a variable on my controller's $scope from within a directive binding to those variables. For instance, I'm trying update the value with a setter-function whenever I move my mouse, but the value never gets updated.
I have made a fiddle here: http://jsfiddle.net/23hdsueb/
Any ideas how to set variables on the parent scope from my directive?
HTML:
<body ng-app="testApp" ng-controller="TestController">
<p>
outside directive
</p>
<input type="text" name="name" ng-model="obj.value">
<p>
value: {{obj.value}}
</p>
<test value="obj.value" set-value="setValue"></test>
</body>
JS:
angular.module('testApp', [])
.controller('TestController', ['$scope', function ($scope) {
$scope.obj = {
value: 'initial value'
};
$scope.setValue = function (val) {
$scope.obj.value = val;
};
}])
.directive('test', ['$document', function ($document) {
return {
restrict: 'E',
scope: {
value: '=',
setValue: '='
},
template: '<div class="test">inside directive<br>value: {{value}}</div>',
link: function (scope, element, attrs) {
$document.on('mousemove', function (event) {
scope.setValue(event.pageX + ' : ' + event.pageY);
});
}
};
}]);
Sorry for not giving the right answer in the first try, I had not understood the question properly. The answer is that your whole code is right except that you're missing one line of code. Normally you don't need to add it, but you do need to add it here.
$document.on('mousemove', function (event) {
scope.setValue(event.pageX + ' : ' + event.pageY);
scope.$apply();
});
While $document is an angularized variable, $document.on is not really the angular way of attaching event handlers to events.
Why is it good to follow angularized ways? Because they automatically run scope.$apply() when the command completes. The apply() function triggers a digest cycle which is responsible for angular's powers. The digest cycle among many things, checks if there are binded variables which need to be updated or not.
If you use ng-mousemove then you won't need the scope.$apply() line because the ng-mousemove directive triggers the digest cycle when it is fired.
What you are missing is, syntax for calling function of controller via directive /or/ How to call method in attribute of directive:
TL:DR you may go through beautiful explanation about same by Dan Wahlin
In your directive:
.directive('test', ['$document', function ($document) {
return {
restrict: 'E',
scope: {
value: '=',
setValue: '&'//for function it should be &
},
template: '<div class="test">inside directive<br>value: {{value}}</div>',
link: function (scope, element, attrs) {
$document.on('mousemove', function (event) {
//argVal should be exact name in the function call mentioned in directive's attribute
scope.setValue({argVal: event.pageX + ' : ' + event.pageY});
});
}
};
}]);
And your directive use should be like: Please Note: argVal should match exactly as in call to function from within link function call
<test value="obj.value" set-value="setValue(argVal)"></test>
For detail please refer: SO question
I am trying to expand on the bootstrap ui library with my own custom control. This control will be used in an AngularJS app. Currently, I'm getting stuck on the scoping.
My plunker is here
This plunker is a simplified version of a more complex control. The concept that I'm trying to highlight is the scoping. You will notice that the custom control, my-query, is pre-populated with the value of myController.$scope.query. You will also see that the query is put in the page underneath the custom control. As I type, the value does NOT get updated. Why? My code looks like the following:
myApp.directive('myQuery', [function() {
return {
restrict:'E',
transclude: true,
scope: {
query: '='
},
template: '<div ng-controller="myQueryController"><input type="text" ng-model="query" /><button ng-click="go_Click()">go</button></div>'
};
}]);
myApp.controller('myQueryController', ['$scope', function($scope) {
$scope.go_Click = function() {
$scope.$emit("goClicked");
};
}]);
What am I doing wrong?
In your directive template, you are adding an additional controller which is adding in another scope. That is what is causing the problem. Instead of doing it that way, move the controller logic into either a controller function or a link function defined on your directive, either will work.
Try this. Here's an example using a controller function. Note that I moved your original myQueryController inside the directive and removed the ng-controller directive from the myQuery directive's template.
'use strict';
var myApp = angular.module('myApp', []);
myApp.controller('myController', ['$scope', function($scope) {
$scope.queryValue = 'test';
$scope.$on('goClicked', function() {
$scope.performAction();
});
$scope.performAction = function() {
alert('Using ' + $scope.queryValue);
};
}]);
myApp.directive('myQuery', [function() {
return {
restrict:'E',
transclude: true,
scope: {
query: '='
},
template: '<div><input type="text" ng-model="query" /><button ng-click="go_Click()">go</button></div>',
controller : function ($scope) {
$scope.go_Click = function() {
$scope.$emit("goClicked");
};
}
};
}]);
<div ng-controller="myQueryController">
A controller creates a new scope. So <input type="text" ng-model="query" /> doesn't use query from the directive's scope but from the controller's scope. Instead of using a controller you can define the go_Clickfunction in the directive's link method.
Do you need this?:
http://plnkr.co/edit/6IrlnXvsi2Rneee0hGC8?p=preview
scope: {
model: '='
}
The problem was that you used a primitive type which was passed by value into your directive. Always use complex types which are passed by reference.
I have CKEDITOR, a controller and a directive. This is the method of the controller which should add ng-show and remove ng-hide from the <span>:
$scope.deleteEditorAndSave = () ->
angular.forEach CKEDITOR.instances, (editor) ->
id = editor.element.getAttribute('data_id')
text = editor.getData()
field = editor.element.getNameAtt()
html_field = $(editor.element.$)
html_field.val(text)
showing = editor.element.getAttribute('ng-show')
console.log showing
$timeout( ->
html_field.trigger('input')
$scope.save_field(text, id, field, 'no_call')
editor.destroy()
angular.forEach allClaims(), (claim) ->
console.log "CLAIM", claim
claim[showing.split('.')[1]] = false
)
And I want to call this method from the directive. When I try to do this, the <span> element doesn't re-render. Does anyone know how this can be solved?
Thanks in advance.
I think the recommended practice is to resolve DOM in the directives, once thing that can help you is a directive with isolated scope, this will allow you to use & in your directives scope like this:
<body ng-controller="MainCtrl">
<div user-name="" callme="enableEditor()"></div>
<div>
add
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', ['$scope', function ($scope) {
$scope.enableEditor = function() {
alert("123");
};
}]);
myApp.directive("userName", function() {
return {
restrict: "A",
scope: {
value: "=userName",
callme:"&"
},
template: '<div class="click-to-edit">' +
'Edit' +
'</div>'
};
});
The attribute callme="enableEditor()" is used to pass the method to the scope directive, the directive scope uses & to indicate it is method callme:"&". Another example:
method2="someMethod()" like
scope: {
value: "=userName",
callme:"&",
method2:"&"
},template: '<div class="click-to-edit">' + 'Edit' + 'Save' + '</div>'
This is the recommended way to communicate directives with controllers.