Angular offers great possibilities for reading the status of an input field, like $dirty, $touched and so on. However, as far as I found out, that works only when accessing an input element by its name, like $scope.formName.inputFieldName.
But I need to access an input element defined in a directive template inside the directive's controller by the element's ID (and without the form name). Whatever I try, I don't get the special input form handles.
Something like
var myElement = $document[0].getElementById('my_id');
console.log(angular.element(myElement));
doesn't work. I get the DOM element itself, wrapped inside generic object context, but not the input handles Angular offers.
Use angular.element(myElement).controller("ngModel") to access the model controller of an input element.
For more information, see AngularJS element function API Reference -- jqLite Extras
Related
I am having an issue with an application I am working on. In my app I have a module that has a number of costom directives. On one of my pages I have a controller that I am using and in this controller I am adding a call to one of my directives to the page as a new element by creating a var to hold the compiled code and then appending it to the page. The code below is working perfectly.
var compiledeHTML = $compile("<repoertcountrycompare></repoertcountrycompare>")($scope);
$(ReportToLoad).append(compiledeHTML);
Where I am having an issue is when I need to add directive that has attributes that I need to set. In the code below I am trying to add a directive called TableauReport to the page with three attributes.
var compiledeHTML = $compile("<tableau-report rname='Consumer-Brand' countrystr='United States' reportqtr='2016 Q1'></tableau-report>")($scope);
$(ReportToLoad).append(compiledeHTML);
I have tested the directive by including the tag on the page and it works perfectly with these attributes but when I use the code below to add it to the page from the controller I get the following errors:
arguments = Accessing the 'arguments' property of a function is not
allowed in strict mode
caller = Accessing the 'caller' property of a function or arguments
object is not allowed in strict mode
Has anyone else ran into this kind of issue before and know how to work around it?
I was able to figure this out. I needed to use # in my Directive rather than = for the attributes I was passing to the directive.
I'm using Google Maps within an Angular app. As per Google Maps's API, I pass a string to a google.maps function containing HTML, and the library displays it on the map as an InfoView.
I'm adding a button to this view, and I want this view to be bound to a controller in my Angular app, however, even if I pass <div ng-controller="MyController">...</div>, when the Maps library attaches it to the DOM, Angular is not notified.
How can I force the compilation of this element (over which I have very little control: just passing a string)?
For AngularJS to "notice" the directive, it'd need to compile the element, which is not done automatically except when bootstrapping the application. So either the API needs to allow you to pass DOM Element to it (and you need to $compile and link it before you pass it on), or you need to find the element after it is added to DOM and then $compile and link it. AFAIK, if you cannot do either of those, then what you're asking is impossible.
If you manage to get hold of the element, compiling it is as simple as calling
$compile(element)($scope);
where $scope is the scope you want to link it to (possibly even $rootScope).
i'm beginning angular developer and i have application with some controllers.
Is it possible to find out what is the Name the controller of some html if there is no "ng-controller" statement?
Like in case this is custom directive or the html loaded by ui-router?
p.s. i know about batarang but i dont see the name of the controller, only the scope
Thanks
If you can get hold of the DOM element you are interested in, you can run in the console:
angular.element(...).controller().constructor
This will print the constructor function (i.e. the function you have registered as controller) in the console; clicking on it (at least on Firebug) gets you to the source.
.controller() is an addon to the jQuery API from Angular. To get hold of the element in interest, the simplest thing is document.getElementById(). You can even use Firebug (or equivalent) to place an id in that element and then use getElementById().
Also for Firefox/Firebug let me propose the excellent angscope plugin.
I have two custom element directives:
<my-directive-parent></my-directive-parent> //only one
and
<my-directive-child></my-directive-child> //variable count
The my-directive-parent directive has its own controller and templateUrl properties defined in its directive definition object. The my-directive-child directive has its own link, scope, templateUrl and require properties defined in its directive definition object. The fourth parameter passed into the link function is the parent my-directive-parent's controller. This is working as expected.
Based on user input, instances of my-directive-child are appended to or removed from the parent my-directive-parent DOM via the my-directive-parent's controller. Since I'm using ngView for the top-level page, changing pages results in Angular automatically cleaning up the various directives and their controllers of the top-level page. Upon returning, the controller for the page re-runs, but the user appended views do not show up (since they were previously wiped out automatically by Angular). I'm using a service which holds the data representing the collection of my-directive-child instances that were previously added.
My issue lies in restoring the my-directive-child directives based on this cached service data. Currently, when the my-directive-parent directive's controller is run, I'm getting the service data array that represents the instances to be added, looping through it, and adding a new my-directive-child instance to the DOM. This is working, but upon restoring each instance I need to pre-populate it with data that was previously entered by the user (of which is recorded in the service). Currently, when adding to the DOM this way each instance is in a "blank" state instead of a desired pre-populated state.
So I have the data needed to recreate the child directive instances, but I'm unable to pre-populate them with the necessary data.
I've tried to place a custom attr on the my-directive-child in an effort to forward the data to the instance's scope, but I learned from Angular's API docs that:
The new scope rule does not apply for the root of the template since the root of the template always gets a new scope
Questions:
Is my approach wrong? What should I be doing?
Is there a way to pass attrs defined on the actual element directive into the directive's scope that represents the inner template HTML?
How do I ensure a custom directive within a custom directive properly pre-populates itself?
Thank you in advance for any ideas, answers, or thoughts that might lead to a solid solution.
I have a simple case:
<div ng-controller="myController">
<tabset>
<tab>
<form name="myForm"></form>
</tab>
</tabset>
</div>
and now, in myController method, I would like to access myForm to call $setPristine:
$scope.myForm.$setPristine()
but I can not. tabset / tab creates isolated/inherited scope. It's just a sample, but I run into this problems when using angular js directives that create isolated scopes many times.
How can I get over this issue? In the past I did something like this (with ng-table that also creates new scope):
ng-init="$parent.saataaTable = this"
but it's far from perfect.
This was one of the most difficult concepts for me to get around and my solution is simple but kind of difficult to explain so bear with me.
Solution 1: Isolate Scopes
When you are only dealing with only isolate scopes (scope: {...}) or no scope (scope: false), you're in luck because the myForm will eventually be there. You just have to watch for it.
$scope.$watch('myForm', function(val) {
if (myForm) {
// now I can call $setPristine
}
});
Solution 2: Child Scopes
This is when you set scope: true or transclude: true. Unless you perform a custom/manual transclusion you will not get myForm on the controller's scope.
The trick is to access the form's controller directly from the form element. This can be done by the following:
// at the form element
element.data('$formController');
// or at the control (input, select, etc.)
element.inheritedData('$formController');
// where 'element' is a jqLite element (form or ng-form)
This sets you up for a new issue: how do we know when and how we can get that element and it's data.
A quick answer is that you need to set up a dummy $watch on your controller's scope to look for (in your case) myForm. When this watch is processed you will then be able to attempt to locate the form. This is necessary due to the fact that typically when your controller first executes the FormController won't yet be on the element's data object.
A quick and simple way to find the form is to simply get all of the forms. NOTE: if there are multiple forms within the element you'll have to add some logic to find the right one. In this case our form is a form element and it's the only one. So, locating it is fairly easy:
// assuming you have inject $element into your controller
$element.find('form').data('$formController');
// where $element is the root element the controller is attached to
// it is injected just like '$scope'
Once you have the controller you can access everything you would normally. It is also important to note that Solution 2 will always work once that FormController is on the element.
I have set up a Plunk to demonstrate the code here, but please note that is a demonstration so not all best practices were kept in mind.
EDIT
I found it important to note that if you don't want to worry about the scopes of the nested directives you can just watch the form name on the scope and handle things there.
$scope.$watch('myForm', function(val) {
if (angular.isDefined(val)) {
// now I have access
} else {
// see if i can `find` the form whose name is 'myForm'
// (this is easy if it is a form element and there's only one)
// then get the FormController for access
}
}
I could not make it work using the answer above, but I found a work-around.
In the form, I created a hidden input field with a ng-model and ng-init that set its value to the form. Then in my submit function in the controller I can access the formController via this ng-model
So, in the HTML, I create a hidden field inside the form:
<input id="test" ng-model="data.myForm" ng-init="data.myForm=myForm" hidden>
And in the Controller I can get hold of the formController via data.myForm
$scope.data.myForm.$setPristine();
It is probably not very good, so I will instead avoid to rely on the $pristine and $dirty properties of the formController and find another way to detect if the form has changed (using a master copy of the object, like they do in the sample in the documentation)