I want to initialize a button in my directive but call a function from controller while using that directive.
I have something like this,
directive:
html: Button
JS: MethodOnClick : '&'
while calling directive I have,
html: MethodOnClick = "method()"
JS: #scope.method = function(){}
It does not work, the function #scope.method is not executed at all. Can some one please help me with this? Thanks.
I believe you're looking for ng-click="method()". MethodOnClick isn't an valid event for a button. It's a little hard to see what you're asking - post the relevant code and it'll be easier to help you.
Related
For a mockup I need a simple mechanism like
ng-click="alert('Clicked')"
but the code above is not working, can someone help me? I don't want to touch the Controller..
Refer to previous answer, ng-click = "alert('Hello World!')" will work only if $scope points to window.alert i.e
$scope.alert = window.alert;
But even it creates eval problem so correct syntax must be:
HTML
<div ng-click = "alert('Hello World!')">Click me</div>
Controller
$scope.alert = function(arg){
alert(arg);
}
As far as I know, ng-click works only within your $scope. So you would only be able to call functions, defined in your $scope itself. To use alert, you may try accessing the window element, just by using window.alert('Clicked').
EIDT: Thanks to Ibrahim. I forgot. You shouldn't be able to use window.alert, as far as you won't define:
$scope.alert = window.alert;
In this case, using alert('Clicked') in your ng-clicked directive should work. But at the end, this method would not solve your problem of not touching your controller.
You can use
onclick="alert('Clicked')"
for debugging purposes
As Neeraj mentioned, the accepted answer does not work. Add something like this to your controller to avoid an Illegal Invocation error:
$scope.alert = alert.bind(window);
I've been following this tutorial and I want to update my code so that the alert is displayed when the button is clicked.
Here's my fiddle
How would I beind the element to a mouse click? I thought perhaps using something like:
.directive('enter', function(){
return function(scope, element, attrs){
element.bind("mouseclick", function(){
scope.$apply("sayHello()");
})
}
})
This doesn't work unfortunately. Should I be using ng-click in this case? I'm trying to follow best practice. Based on the tutorial I'm following it looks like this is the best way to do this as you can be sure the controller and directive are in the same scope.
I hope this makes sense, I'm new to Angular.
Yup, ng-click is what you want. With it, your button decalration simply becomes:
<button ng-click="sayHello()">sayHello</button>
Here's your updated fiddle, it no longer needs the enter directive!
Imagine these uses of a directive (in one HTML file):
<my-dir function="callMe()"/>
<my-dir function="someOtherFunction()"/>
Here's the important bit of the directive:
.directive('myDir', function () {
return {
scope: {
function: '&'
},
};
})
Here's the template HTML:
<div ng-click="function(5)">click here</div>
This does not work - While #function does indeed invoke #callMe and #someOtherFunction, there seems to be no way to pass the argument. How can I pass a method to the directive so that the directive can invoke the function and pass an argument?
It's pretty clear the HTML could simply refer to #callMe directly. But then the directive would not work for #someOtherFunction
EDIT - Here's a fiddle I was working on. I amended it with PSL's suggestions. It seems to work now!
Try this way:-
Specify the name of the argument in the directive arg:-
<my-dir callback="func1(arg)"></my-dir>
<my-dir callback="func2(arg)"></my-dir>
On your template provide the key value pair with the same key name as that of the function.
template:'<div ng-click="callback({arg:5})">click here</div>'
Demo
ng-onclick should be ng-click unless you are using some other angular component which provide an ng-onclick directive. Use closing tags for your directive elements.
Here's a plunk:
http://plnkr.co/edit/YQgijS?p=preview
The relevant bits:
I have no idea why this works, and I feel like it indicates there's something weird about & scope that I haven't learned yet.
When passing the variable in to the directive via an & scope, pass theFunction and not theFunction()
When calling the function in an ng-click, call fun()(args) instead of fun(args)
I know, weird, right? I'm sure there's something going on there. I asked a SO question about it a while ago, but got no response.
I'm a 2 week old Angular noob, and I've been attempting my first ever directive for over a day now. :P I've read this and this, which seem good directives introductions and a bunch of Stackoverflow answers, but I can't get this working.
<div ng-app="App" ng-controller="Main">
<textarea caret caret-position="uiState.caretPosition"></textarea>
{{uiState.caretPosition}}
</div>
and
angular.module('App', [])
.controller('Main', ['$scope', function ($scope) {
$scope.uiState = {};
$scope.uiState.caretPosition = 0;
}])
.directive('caret', function() {
return {
restrict: 'A',
scope: {caretPosition: '='},
link: function(scope, element, attrs) {
var $el = angular.element(element);
$el.on('keyup', function() {
scope.caretPosition = $el.caret();
});
}
};
});
Fiddle is here. I'm basically trying to get the caret position within a textbox. I'm using this jQuery plugin in the fiddle (source of the .caret() method, which should just return a number).
My questions are
Do I even need a directive here? Ben Nadel says the best directives are the ones you don't have to write (amen to that!)
If yes, is this the right way to go about the directive? Ie isolated scope with two-way bound variable?
If yes, when I run this code locally, I keep getting the error Expression 'undefined' used with directive 'caret' is non-assignable!. I've read the doc and as far as I can tell I've followed instructions for how to fix to no avail.
BONUS: Why in jsfiddle do I get no such error. The fiddle just fails silently. What's with that?
Thanks!
My solution was harder to find out here, but easier to implement. I had to change it to the equivalent of;
scope: {caretPosition: '=?'},
(Note that the question mark makes the attribute optional. Prior to 1.5 this apparently wasn't required.)
You were close.. The main problem is changing a scope variable inside an event that angular doesn't know about. When that event occurs, you have to tell angular that something changed by using scope.$apply.
$el.on('keyup', function() {
scope.$apply( function() {
scope.caretPosition = $el.caret();
});
});
Working fiddle here
For the questions:
yes, I don't think there's a way around having to write a directive for this.
in this case, it seems fine.
not sure, there are no problems in the jsfiddle. It sounds like you're setting carat="something" somewhere? check to make sure the html is the same as what's in the fiddle.
same as 3
Also note that if you click and move the caret, it won't update because it's only listening for keyup.
The docs specify that you can expost the form to the scope with name. so lets say I have this form:
<form name="myForm" ng-submit="submit()">
<input type="text" ng-model="somemodel"/>
<button ng-submit="submit()"></button>
</form>
but I'm trying to access the form through the controller with $scope.myForm and fail:
$scope.submit = function(){
console.log($scope.myForm) // or form[0] or scope.myForm[0] etc..
if(!$scope.myForm.$dirty){
//do this and that
}
//do something
}
they all fails as undefined. how is this done? ultimately I would like to also call $setPristine() from the controller. but I just can't find out where the form is hiding. is he even available to the controller or just in the view scope?
using angular 1.2.5
EDIT: this doesn't work also when the form name is with the dot notation: myForms.myform
Another edit: after forking the suggested plunker I found out that the form doesn't exists on the $scope before submitting, but does exist after.
So I guess the refined question should be: is this the expected behavior? Is there a workaround this (in the ctrl and not in a directive)?
If this is expected and no workaround so I'll move the custom validation checks to specific directives - since in the directives (if they require:"^form") the form is available before submit.
Thanks!
Answer this issue seems to be when the form is added to the scope. from the plunkers it's obvious that not in the beginning, but attached later, after the Parent Ctrl got loaded. can we control the loading order? I don't know. seems the place for this kind of form scope manipulation should be a directive and not the ctrl
One reason you might not be able to see the form on your Controller $scope is if your <form> is inside a directive that introduces a new child scope. e.g. if the form is inside an ng-if or ng-switch.
In other words, in the example below, myForm ends up getting published to the child scope introduced by the ng-if and you won't be able to see it on the MyController $scope.
<div ng-controller="MyController">
<div ng-if="true">
<form name="myForm"></form>
</div>
</div>
One way to deal with this is to use dot-notation to publish the form into an object wrapper instead of putting it directly on the scope.
<div ng-controller="MyController">
<div ng-if="true">
<form name="myStuff.myForm"></form>
</div>
</div>
And in MyController, make sure an object is there so it will publish to it instead of the child scope.:
$scope.myStuff = {};
Take a look at this egghead.io video that does a good job explaining this: https://www.youtube.com/watch?v=DTx23w4z6Kc
If you change the reference from $scope.form to $scope.myForm, then that is defined, as in this plunker. Unless you have a parent form called myForms that you haven't mentioned, you won't be able to access it as $scope.myForms.myform.
Edit: the refined question asks whether it's expected behaviour that on the form's parent controller that the form is undefined when the controller is first run. The answer is yes, it's not added until later.
The question suggests that the form is only defined when the form is submitted. This isn't quite accurate. The form is added on the scope before this: there is nothing special about the submit function. You should be able to quite happily call form functions in response to other button presses. In the parent controller you can have
$scope.makeTheFormPristine = function() {
$scope.myForm.$setPristine();
}
As can be seen in the following Plunkr:
http://plnkr.co/edit/dDVrez9UP1GYRIDrUxJs?p=preview
Edit: from the comments, if you want a variable in the parent scope, say notification, to depend on a property of the form, you can setup a manual watcher:
$scope.$watch('myForm.$pristine', function(newValue, oldValue) {
if (newValue) {
$scope.notification = 'Pristine';
} else {
$scope.notification = 'Not pristine'
}
});
You can see this at http://plnkr.co/edit/2NMNVAfw8adlpFRGuSoC?p=preview . However, I would be a bit careful about putting too much "view" logic into the controller. It might be better placed in the template, or maybe event a custom directive. This is probably beyond the scope of this question, and maybe better on https://codereview.stackexchange.com/.
Also: I don't think you need the 2 ng-submit attributes, both on the button and the form, but I suspect that isn't related to this issue.
Oops. Just re-read your Question and realized that you are using the wrong scope path. As already indicated by Matt, the form will be pusblished to $scope.myForm and not $scope.form.myForm.