How do I structure AngularJS directive for compound form fields? - angularjs

I would like to use AngularJS for some administration forms. It's very easy to bind view and model. I also like how you can add validation. But when I tried to prevent redundant code by introducing directives I hit a border. Could you please help me how I should structure code with AngularJS for this problem:
In my forms I have some compound fields. For example you can choose a country in a dropdown. Next to that dropdown you can enter a city in a textfield. When you choose another country the city name should be cleared. That's easy. Now I want autocompletion in the city textfield. When you enter a few characters you get suggestions for cities in the selected country. The autocompletion widget is the minor problem here, let's for simplicity say I would use JQueryUI for that.
I already managed to implement simple directives. My questions are:
1.) I like the functionality for validation in NgModelController. Can I somehow reuse this in my directive? It would be nice if I could add a "required" attribute to my directive. If it is set then all compound fields are required.
2.) How would I connect the directive with the surrounding model? For example I would like to edit a customer in an administration form and my "address" directive should display and edit the customer address.
3.) How would I connect the directive with my city lookup service? I need to give country and first typed letters of city to my service. Result is a list of city names that can be displayed in the autocompletion widget.

Here is the plunker that show you how to set require attribute.
Main idea is having required="{{isRequired}}" in the template and having isRequired as attribute for your directive.
template: '<input name="city" type="text" ng-model="city" placeholder="City" required="{{isRequired}}">'

Related

What's the best way to reduce repetition of form element attributes in angularjs?

I'm designing an AngularJS 1.5.x app that will have lots of forms and lots of fields per form. I'm finding that I'm repeating attributes a lot, e.g.:
<div class="form-group">
<label class="control-label" for="thing.Field1">Field 1</label>
<input class="form-control ctrl-md"
type="text"
id="thing.Field1"
name="Field1"
ng-model="thing.Field1"
ng-maxlength="30"
required
uib-tooltip="Field 1 is required"
tooltip-placement="right"
tooltip-trigger="none"
tooltip-is-open="thingForm1.Field1.$invalid && showValidationErr('Thing1')"
ng-blur="fieldBlur('Thing1')">
</div>
I've attempted to use a directive + template, and it works more or less, but it seems very complex and slow.
Is there a good way to make the input reusable? Or should I just get used to doing a lot of copy+paste in my editor?
In addition to reducing repetitive coding, it would be nice to be able to change all elements in one place, in the case that I want to change the tooltip position on all fields for example.
There is many ways. Have a look at this library http://angular-formly.com/. In my projects I use plain inputs and writing directives only in a complex cases, like 2 field controls. Or special field like card expiration date.
UPDATE
Ok, have a look at this pls. Angular: better form validation solution
You have 3 solutions:
To store your attributes as it is.
To store a group of attributes, commonly used together, in a directive. Or use directive which utilize array of properties. Like validation directive in my example.
To use directive with transclusion to utilize 1st and 2nd approach. So you can swap elements position (such as label, input control, error hint) in a single place. Plus you can easily create property like... preset on this directive and store attribute presets in it.
So, as I already told, there is many ways to reduce number of attribute repetition.

need to convert multi select model to text area model in html, in Angularjs application

In my angularjs application, I am using multi select dropdown and its model is coming as follows :
$scope.selectedValues = [{"id":2,"name":"Automatic"},{"id":4,"name":"Manual"}];
and this model is nicely getting displayed. There is no problem in that. Now during view application stage, I need to hide this multi select drop down and need to show the all names in $scope.selectedValues in text area. Without doing the manipulations in controller to get the model value for the text area, is it possible to extract names from $scope.selectedValues and display them as space separated values from html itself?
<textarea class="form-control" ng-model="allInsurancesNames" ng-if="needToDisbleMutiSelectDrpDown"></textarea>
Here the model allInsurancesNames should contain all names as space separated from model selectedValues. We can do any thing in html like using ng-repeats, or expressions, etc, whatever is needed to achieve this, but allInsurancesNames should not be populated from $scope.selectedValues in controller.
If you can create a function inside your controller, you can do something like this:
$scope.mapFunct= function(value){
return value.name;
};
And
<div ng-controller="ctrl" ng-init="allInsurancesNames=selectedValues.map(mapFunct).join(' ')">
Working Plunk

How to use FormController with directives

I'm trying to build widgets directives that play well with forms
The complete example is available at http://jsfiddle.net/jy81bchd/4/
The main idea is:
Write your own form, put widget in it
<form name="otherForm" novalidate ng-init="model = {name: 'test'}" novalidate>
<swif-widget-text name="name" required="required" input-model="model.name">
<span translate>Name</span>
</swif-widget-text>
</form>
The widget directive copy all attributes to the input and transculde the contennt in the the label.
My differents problems are:
1) I can't update the formController of the main form, it doesn't find the different inputs throw directives. The best should be to use formcontrolller.addControl but I don't have succeed with it
2) To work around 1 I have tried to make each widget a different form. This is working execpt formcontroller doesn t update after link has been called (attributed copied to input doesn't affect the controller).
In the fiddle I copied required attribute to the input but if you empty the field it's still valid according to the formcontroller.
I have added name="input" also because if I copy name attribute to the input the form controller doesn't find any input.
Conclusion:
From what I understand formcontroller is initialized and loaded before the link is called.
How could I change that ?
There are several posts about how to create your own form component that plays nicely with the form and the controller that is attached to that.
For instance: http://blog.revolunet.com/blog/2013/11/28/create-resusable-angularjs-input-component/
Examining your fiddle I have a few things you need to look at:
1) add a name and a controller to the form element in order to work with all form elements that live inside of it. Via the $scope that is injected in that controller you can handle the form elements (also add meaningfull names to your form elements).
2) And your custom component:
- should use ng-model instead of input-model.
- should require 'ngModel' in the Directive definition
- in the link phase you get a reference to the ngModelController, and check the link above to see how you should interact with that to achieve a two way binding like behaviour. (using $viewValue, $render and $setViewValue)

angularjs multilingual text field with ngmodel

I'm trying to implement a multilingual text input field with a little dropdown button to the left for selecting the language. For instance, when the dropdown menu shows 'de' the text field should be bound to model.multilingualData['de'].someField and so on.
My first approach was to set ngModel to model.multilingualData[selectedLanguage].someField. But when the user selects a different language without filling in the field correctly, no error is set on the form, because the model now points to a different object.
My next idea was to create an entire element directive without ngModel, but then I wouldn't be able to do other validations like ngMaxLength.
I couldn't find anything helpful on the web either. Any idea on how to implement this properly?
EDIT
Here's a little fiddle that illustrates the problem: http://jsfiddle.net/FZ2kg/
Not only that the form appears valid when you switch languages, the previous field value is also deleted, because the model is set to null when the field becomes invalid.
would be nice if you use this awesome external directive for multilanguage!
https://angular-translate.github.io/
I hope it helps
If you need to have form validation for all language variations and you're loading all languages at once in your model, can't you just create an input per language in the form and hide all but the currently selected language?
Here's a fiddle: http://jsfiddle.net/jvl70/w3rstmwd/5/
<form name="myForm">
<div ng-repeat="(lang, value) in model.multilingualData"
ng-show="lang==stuff.currentLanguage">
<ng-form name="innerForm">
<div ng-class="{ 'has-error': innerForm.anything.$invalid }">
<input type="text" name="anything" ng-model="value.someField" ng-maxlength="6"/>
</div>
</ng-form>
</div>
</form>
(At first I tried to use dynamic names for each input but found that the individual field $invalid wasn't available for dynamically named inputs. See this post to get around it: Dynamic validation and name in a form with AngularJS.
As an alternative to ng-form, you could use dynamic names for each input and try one of the custom directives on the link.)
I guess if there were many possible languages, this approach might get slower but it's ok for a few languages.

What's a good practice when building forms dynamically in AngularJS?

I've got some JSON data- an Array of Fields containing Input Type (input, dropdown, radio, checkbox, etc.), Label and whether they are required or not.
I'm doing an ng-repeat through the array to build the form. I'm trying to understand what's the best way to build different kinds of inputs based on the Input Type value.
In normal programming, I would do a
foreach (var field in FormData){
if (field.inputType == "dropdown"){
//logic to build dropdown using jQuery, etc..
}
}
In AngularJS, I can't really do if thens within an ng-repeat="field in FormData". What's the proper way to dynamically build out these different kinds of elements while looping through an array?
This question is very similar:
How can I use Angular to output dynamic form fields?
Many thanks for any suggestions.
In my application, I did use an ng-switch (see the answer from the very similar question) in my ng-repeat to achieve something similar to this. The only problem with this is to link to the model. If you want to bind to a property name that is stored in a variable (if you json contains an id for the field), you won't be able to something like this :
<input type="text" ng-model="formdata.{{elem.id}}" />
I found that you can do this instead :
<input type="text" ng-model="formdata[elem.id]" />

Resources