AngularJS: Programmatically adding nested form (ngForm) - angularjs

I have a specific problem where I need to add nested form in a form built with angular.
Statically (non-programmatically), I can the do the following:
<form>
...
<ng-form>
<input ng-model="myModel" required>
</ng-form>
...
</form>
And validation error in the nested form invalidates the outer form. Which is exactly what I need.
But I can't seem to do this programmically via a directive. In the template I have the following
<div dynamic-nested-form="">
</div>
And I have the following:
(function () {
angular
.module('controls')
.directive('dynamicNestedForm', dynamicNestedForm);
function dynamicNestedForm($compile) {
return {
restrict: 'A',
link: linkedFunction
};
function linkedFunction($scope, element) {
var nestedForm = angular.element('<ng-form><input ng-model="myModel" required></ng-form>');
element.append($compile(nestedForm)($scope));
}
})();
The form does get injected in the DOM and it is in invalid state, in Chrome element view, I see
<div dynamic-nested-form="" class="ng-scope">
<ng-form
class="ng-pristine ng-scope ng-invalid ng-invalid-required">
<input ng-model="myModel" required=""
class="ng-pristine ng-untouched ng-invalid ng-invalid-required">
</ng-form>
</div>
But outer form does not get invalidated. When the outer form loaded does it not see the inner form?
What am I doing wrong?

Right,
So,
Basically did a little debugging through Angular and it just happens that the nested form could not find the outer form controller. (The outer form controller is needed in ngModelDirective in its preLink)
The reason it cannot find it is because this preLink happens during compile (obviously, it's a preLink) and my compile was done before the nested form was attached.
So as a solution, instead of compiling before attaching, I do it after, like
var nestedForm = angular.element('<ng-form><input ng-model="myModel" required></ng-form>');
$compile(element.append(nestedForm ).contents())($scope);
Well, I think I am recompiling it.
Anyhoo, this then makes the inner control find the outer form and my problem is solved.

Related

How use multiply ng-models in AngularJS

I have textarea with ng-model 'wordset' and ng-change="onChange()"
<div>
<textarea ng-model="wordset" ng-change="onChange()"
class="form-control app-word-set"
placeholder="Enter Word Set" rows="4">
</textarea>
</div>
I have button which added new textarea in this div. I needed that already added textarea includes the same on change method that my first textarea i have. But it should use ng-model...
I want to use on method in my angularJS controller that gets values from every textarea by foreach like this:
$scope.wordSetTextarea = angular.element(document.getElementsByClassName('app-word-set'));
$scope.onChange = function() {
angular.forEach($scope.wordSetTextarea, function(value, key) {
console.log(value);
});
}
Is this possible?
With the AngularJS framework, multiple elements are added with the ng-repeat directive:
<div ng-repeat="item in itemArr">
<textarea ng-model="item.wordset"
ng-change="onChange(item,$index)"
name="'item' + $index"
class="form-control app-word-set"
placeholder="Enter Word Set" rows="4">
</textarea>
</div>
<button ng-click="addNewTextarea()">Add input</button>
$scope.itemArr = [{}];
$scope.addNewTextarea = function() {
$scope.itemArr.push({});
};
New AngularJS developers often do not realize that ng-repeat, ng-switch, ng-view, ng-include and ng-if all create new child scopes, so [data hiding problems] often shows up when these directives are involved ... [they] can be easily avoided by following the "best practice" of always have a '.' in your ng-models.
For more information, see
AngularJS ng-repeat Directive API Reference -
What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

How to get the uib-popover to open or close when the focus of the input field changes

I have a input field that has the uib-popover control on it. I have followed the documentation on how to get the directive to open but I have noticed some discrepancies in the documentation as well as examples on plnker and SO questions here.
Within my hmtl I have the inputs set as follows:
<div class="form-group col-xs-6 col-sm-6 col-md-6 col-lg-6">
<label>Password</label><input type="{{user.inputType}}" ng-blur="user.validatePassword(user.newUser.password, user.newUser.confirmPassword)" placeholder="Enter Password" id="password" required class="form-control" ng-model="user.newUser.password" uib-popover-template="'myPopoverTemplate.html'" uib-popover-trigger="'focus'"/>
</div>
<div class="form-group col-xs-6 col-sm-6 col-md-6 col-lg-6">
<label>Confirm Password</label>
<input type="{{user.inputType}}" ng-keyup="user.validatePassword(user.newUser.password, user.newUser.confirmPassword)" placeholder="Confirm Password" id="confirmPassword" required class="form-control" ng-model="user.newUser.confirmPassword"/>
<span style="color: red">{{user.message}}</span>
</div>
Most examples as well as the SO questions on here are using an older library as attributes are not prefaced with uib-*.
This code/directive currently works and renders but it only work or appears when clicking in the field and then clicking in the same field to close the popover. I have tried both the focus trigger and the oustsideClick trigger. Both have the same result of not rendering or closing the popover unless clicking in the field.
versions of the frameworks used are:
angularjs 1.5.8
ui-bootstrap 1.3.3
Changing the trigger to match earlier examples were popover-trigger is used vs. uib-popover-trigger is used disables the popover
I have created a working plunker that demonstrates what is happening.
Any suggestions on what I am missing or what I need to change.
Thanks in advance
According to tooltip.js description, in order to set a custom trigger,
it needs to be specified via trigger option passed to the $tooltipProvider.options method. In your case for focus trigger it will be:
app.config(['$uibTooltipProvider', function ($uibTooltipProvider) {
$uibTooltipProvider.options({ trigger: 'focus' });
}]);
Updated plunker that shows how to trigger tooltip on focus handler.
There's a problem of your code, please modify like below:
app.js:
(function() {
var app = angular.module('app', ['ui.bootstrap']);
app.controller('main',[ '$scope', main]);
function main($scope)
{
vm = this;
vm.message = 'hello';
vm.dynamicPopover = {
content: 'Hello, World!',
templateUrl: 'myPopoverTemplate.html',
title: 'Title'
};
}
}());
index.html
<input class="form-control" uib-popover-template="vm.dynamicPopover.templateUrl" popover-trigger="focus"/>
Actually, you can not just pass the template's id to the uib-popover-template attribute, you need to create an object to map it waited to pass.

Angular - Watching a form whether its $dirty - performance

In my Angular app, I'm setting the placeholders in my form through some code in the controller (showing particular text for particular times of day).
When a user begins typing into any field of that form, I want all placeholders to be cleared.
To do this I understand I need to use $dirty using $watch
$scope.$watch('myForm.$dirty', function() {
//clear the placeholders
}, true);
My question is watch quite performance intensive in this situation or is there a more optimised way?
Thanks.
If you use $dirty and $watch it will work but it will clear all placeholders before you begins type or on controller load.
So, you can try this its work for me.
<div ng-controller="Ctrl">
<input type="text" ng-model="name1" ng-change="change()" placeholder={{placeholder1}}>
<input type="text" ng-model="name2" ng-change="change()" placeholder={{placeholder2}}>
<input type="text" ng-model="name3" ng-change="change()" placeholder={{placeholder3}}>
</div>
function Ctrl($scope) {
$scope.placeholder1="name";
$scope.placeholder2="city";
$scope.placeholder3="address";
$scope.change=function() {
$scope.placeholder1="";
$scope.placeholder2="";
$scope.placeholder3="";
};
}

angularjs access another form from directive

<ng-form name="innerForm">
<select ng-model="obj1" ng-options="i as i for i in selectTrueFalse" my-drective">...</select>
</ng-form>
<ng-form name="innerForm">
<select ng-model="obj2" ng-options="i as i for i in selectTrueFalse" my-drective">...</select>
</ng-form>
Two select elements above are wrapped around separate form element. In angularjs directive we can access the current form as mentioned below but as far as I know it only gives access to the current form. I need to access other form in order to access the element inside other form e.g. if the directive is being evaluated for obj1 how to access obj2 at that time. Is there a way?
app.directive('myDirective'), function() {
return {
require: '^form'

AngularJS - dynamic naming using $index in expression

I have a directive in which i create many ng-form's and i name each form based on the $index (from the ng-repeat). My problem is that i want to show an error container (that contains the error message) when the form is invalid but i cant find how to properly reference the form.
Here is my code:
<ng-form name="innerForm{{$index}}">
<label ... ><input name="input" ... />
<div class="error-container" ng-show="'innerForm'+$index.input.$invalid">
// show the error message
</div>
</ng-form>
I want 'innerForm'+$index.input.$invalid to be evaluated as innerForm5.input.$invalid for example.
I have made many attempts but i can't get it to work. What is the correct way to reference my dynamically named form?
please see here : http://jsbin.com/jocane/4/edit
<div ng-controller="firstCtrl">
<div ng-repeat="object in allMyObjects">
<ng-form name="innerForm{{$index}}">
<input type="text" ng-model="object.name" required name="userName">
<div ng-show="{{'innerForm'+$index}}.userName.$invalid">
error
</div>
</form>
</div>
First thing is that, ng-form creates an isolated scope. So if you give a ng-form static name, it will be ok. No need to append $index or anything.
ng-show="innerForm.input.$invalid"
Code snippet to find form controller using element
var elmParent = $(domEle).parents('form, ng-form, [ng-form]');
var formCtrl = null;
var elemCtrl = null;
if (elmParent.length) {
var elm = angular.element(domEle);
formCtrl = elm.scope().$parent[elmParent.attr('name') || elmParent.attr('ng-form')];
}
Hope this will help

Resources