What is the purpose of the $addControl method in AngularJS FormController? - javascript

I was browsing the Angular docs and I came across the FormController class. I see that there are a couple of methods for adding controls and removing them, namely $addControl() and $removeControl(). I assume these will be used for dynamically adding and removing form controls, but how exactly do I go about using them?

formController tracks a set of child input controls in it for the purposes of setting the controllers of these child input controls on the scope, and of tracking the $dirty/$pristine, $valid/$invalid, etc... status of the form based its child controls.
This API is called by ngModelController - which is how Angular implements its built-in (and provides hooks for custom) input controls and by formController - of sub-forms to register themselves with their parent formController.
If you implement custom input controllers that require: "ngModel" (i.e. custom input controls that support the ngModel abstraction layer), then this is done for you.
And for the vast majority of cases, this is sufficient. But, presumably, one could implement their own ngModel-like directive, then this API could be used to register the non-ngModel control with a formController.

Related

angular default drop down value 2 way bound

I have a drop down in Angular that is a 2 way bound FORM control. When the form loads all I want to do is set a default value on the bound value so it displays that value in the drop down. When I change the value in the drop down and print to console I see the binding is correct. However the first time it loads the default value is not displayed in the drop down (not preselected) . In this sample when the form loads "cow" should be the default selected item but it is not working on page load. Please advise what is wrong in the code.
https://stackblitz.com/edit/angular-error-initial
Programmattically assign the value to your FormControl...
ngAfterViewInit(){
this.animalControl.setValue(this.animals[2]);
this.animalControl.markAsTouched();
console.log('FormControl Value: '+JSON.stringify(this.animalControl.value))
}
Stackblitz
https://stackblitz.com/edit/angular-error-initial-atr11t?embed=1&file=app/select-hint-error-example.ts
Revision
Use of ngModel with FormControl has been decprecated and is removed from Angular 7... you should start getting use to accessing values from the FormControl.
console.log('FormControl Value: '+JSON.stringify(this.animalControl.value))
https://next.angular.io/api/forms/FormControlName#use-with-ngmodel
This has been deprecated for a few reasons. First, developers have
found this pattern confusing. It seems like the actual ngModel
directive is being used, but in fact it's an input/output property
named ngModel on the reactive form directive that simply approximates
(some of) its behavior. Specifically, it allows getting/setting the
value and intercepting value events. However, some of ngModel's other
features - like delaying updates withngModelOptions or exporting the
directive - simply don't work, which has understandably caused some
confusion.
In addition, this pattern mixes template-driven and reactive forms
strategies, which we generally don't recommend because it doesn't take
advantage of the full benefits of either strategy. Setting the value
in the template violates the template-agnostic principles behind
reactive forms, whereas adding a FormControl/FormGroup layer in the
class removes the convenience of defining forms in the template.
To update your code before v7, you'll want to decide whether to stick
with reactive form directives (and get/set values using reactive forms
patterns) or switch over to template-driven directives.

What directives and other features are available for inputs with a ControlValueAccessor?

I have a custom component. It has a ControlValueAccessor injected into it as a provider. This is the generic standard way to make custom components represent form inputs.
For a more precise example the component actually implements ControlValueAccessor and injects itself into itself. This detail should not be important.
I know that this makes the following things available for this component out of the box:
ngModel directive for two-way data binding
several css-classes managed by Angular: ng-dirty/ng-pristine, ng-valid and ng-touched
Is there any other directives available out of the box besides ngModel? I would expect at least something like blur and change events to work.

How do I handle interaction between the DOM and the Controller?

The more I read (and try to test) Angular apps, I'm seeing that it is bad practice for a controller to refer to the DOM. (e.g. this blog post).
I must be missing something big, because if the controller can't access the DOM (e.g. by "regular" javascript calls like document.getElementsByClassName), then I don't understand how to do a lot of things I'd consider very basic.
Here's a contrived, simple example that has some of the same problems that my app does:
I have a directive that is simply a red box (a div with some styles applied), and uses ng-transclude. So I'd use it in my html file like <red-box>Text that goes in the red box</red-box>
A button, when clicked, changes the color of all red boxes to blue. I would have something like this in my html file: <intput type="button" value="Make Them Blue" ng-click="makeThemBlue"/>
In the controller's makeThemBlue function, I'd (for example) find all of the divs by class name, and change the class to something else which makes them blue
Now consider that my real app is much more complicated - many "boxes" consisting of nested directives, that can be dragged around, and have their positions saved. The controller reads all of the saved settings, and lays everything out according to how the user saved it.
How would I do something like either of the above examples without having the controller access the DOM?
Here are my key rules:
Directives - For solid components and for DOM manipulation.
Services - For business logic and saving state. Directives, Controllers, Services etc. should use them.
Controllers - A views helper. No business logic should be executed inside. For complicated issues use a service.
In your case a box should be a directive.
You directive will use an observable service and register for the click event.
When the click event occurs, the observer will notify all the registered directive instances that it was clicked, and you should apply to that in your directive.
You should use Directives for this purpose.
See the documentation for Directive
Angular Directives
It gives you a built in jquery like functionality to Access Dom
the link function in Directive is amazing to Manipulate Dom with the same syntax(almost) as jquery.
Further You can Maintain Chunks of Functionality By Making Services , So you can separate each login plus you have Access to Dom and can manipulate them easily
Injecting services to controller function of directive will give you to maintain code reuseability.
By having the DOM access the controller (or natively from within the directive). This is what the declarative paradigm is all about.
If your box needs to change it's color, have it read that value, or a class, or whatever you need, from a value in the controller, or in the directive itself.
In a very basic sense:
<my-directive color="getColor()"></my-directive>

Communicating between multiple layers of nested directives

It's a bit complex so bear with me. I've got three layers of directives:
Top layer - a popup directive
Middle layer - a switch-pane directive *
Bottom layer - one of several views
The top layer is just some popup that represents a wizard in my app.
The middle layer is a directive I've made that acts as a stack of views - you can "push" and "pop" views. The "top" view is displayed and the rest are pushed aside and blurred.
The bottom layer is a bunch of views that are normally unrelated to each other, which are dynamically loaded and displayed in the switch-pane according to what the user does.
So far, this works, BUT: currently, the top layer's $scope has an array property that represents all the views the switch-pane should display, passed to the switch-pane directive as an attribute, and the switch-pane directives $watches it and updates itself.
This is OK but I don't think it's good enough - I'd like the switch-pane directive itself to manage it's stack of views, and only expose a push and pop API.
Here are a few ways I thought of:
Using $broadcast / $emit - the top layer will $broadcast a "push" event and the switch-pane will catch it and do whatever it needs
Using a service (to subscribe and fire the "push" event - this is just like using $broadcast but doesn't propagate throughout the scope tree
Using a service that allows the switch-pane directive to register an API of it's own. Using some way of identifying it such as an attribute or even element ID
Using angular.element().scope() to get access to the switch-pane's inner workings
Frankly, I don't like any of these methods much. Certainly I want to avoid being tied to the DOM, so the last 2 are worst.
Any other ways to do this? Which is the most Angular-ish way to expose a directive's API, considering we don't really have access to a certain instance of a directive except via DOM?
You could check how ng-form, ng-model and ng-input is implemented. Basically if the form has a name, for instance <form name="foo" ...> its controller is published to the current scope, in this case under $scope.foo variable. Once the controller is published you could use its API outside the ng-form directive.
You could also access this controller from the other directive if require: "^ngForm" option is specified.
Here is an example taken from my project. It's a sort of wrapper for jqGrid grid plugin written in jQuery: https://github.com/9ci/angle-grinder/blob/06856b0d940b572960025f06f470c2f40fdc0ceb/app/scripts/modules/gridz.coffee#L12

Is there a good way to 'Freeze' Angular scopes and children that are not being used

I am working on a project that basically consists of complicated form containers repeated in an ng-repeat. Each of these forms has enough functionality that it could easily be its own angular app, but instead they are repeated on the screen, and there can be up to 30 of them.
With 5 or more of these on the screen, performance becomes very slow. Although I tried to disbale some of our heavier features, it seems that the big performance wins just come from disabling the 'uninteractable' scopes.
I am looking for a good pattern to 'freeze' the unused scopes. I want them to maintain their state, but I don't want them to listen or be attached to anything until the user focuses on the form that they apply to.
Without seeing your code my first thought is to use a directive to control that.
You could use the bind to the forms, and or use $watch(using $watch would depend on your setup) so that you have finer control over the scope.
here are some links:
for directives in general
for $watch and other ways of controlling the scope
Are all the forms in the view port at the same time? If not, you could add / remove forms to the DOM (using ng-include f.e.) based on whether they are visible to the user. This way watches are removed and added again when needed.
Otherwise, if you have a lot of watches you specify in your form controller, you can unwatch when not focused and rewatch when getting back the focus. This won't reduce the number of watches created by the (native) directives inside your form. This number can perhaps be reduced by using bind-once.

Categories

Resources