Dynamic forms in AngularJs - javascript

Im working on some dynamic forms using AngularJs and appear to have gotten a bit stuck with validation and the form "name" registering on the scope.
What I'm looking to achieve is following.
<my-form-helper form="someObjectWhichRepresentsFormItems" handle-submit="true">
When the handel-submit is true, I would like the directive to include a wrapper around the elements the directive normally creates (a list of inputs, etc)
The problem is, when ever I dynamically add the <form> in the template, the scope does not see the form, however if I simply put in the form tag every time it does..
Also, my ideal solution was to use the directive to wrap the element in a <form> tag using the link function however the result was the same and I can re-look at this once I have it working from within the template.
E.g
<form name="testForm" ng-if="handelSubmit" >
Vs
<form name="testForm">
in the my-form-helper directives controller function the scope
.directive('myFormHelper', function ($http, $compile) {
return {
controller: function($scope){
$scope.submit = function(){
// Trying to access $scope.testForm
// only works if I take out the ng-if,
// even when handelSubmit == true
console.log($scope);
alert('Form submitted..');
$scope.form.submitted = true;
}
$scope.cancel = function(){
alert('Form canceled..');
}
},
templateUrl: 'app/forms/directive-templates/form/form.html',
restrict: 'E',
scope: {
form:'=',
handelSubmit:'='
}
};
});
Html tempalte is
<form name="testForm" ng-if="handelSubmit" >
<!-- The for name derly should be name="{{form.name}}" -->
<div ng-repeat="field in form.fields">
<form-field field="field">
</div>
<div class="form-actions" ng-show="handelSubmit">>
<button type="button" ng-click="submit()">Submit Form</button>
<button type="button" ng-click="cancel()">Cancel</button>
</div>
</form>
<div ng-if="!handelSubmit">
<div ng-repeat="field in form.fields">
<form-field field="field">
</div>
</div>

Related

Angular Clicking on Text to show div tag

I was wondering how to accomplish this with Angular as it seems that ng-click is something to use, then ng-model seems like that could be used.
I want to click on Text and then have a div show its contents and it is not working
My fiddle https://jsfiddle.net/gdxwtoL7/
<div class="well" ng-controller="MyController">
<a class="btn btn-primary" ng-model="selMe" ng-click="handleAnchorClick()">Enter Address</a>
</div>
<br>
<br>
<div ng-if="selMe">
adfadf
</div>
simple module and controller
angular.module('myapp', []);
angular.module('myapp').controller('MyController', MyController)
function MyController($scope) {
}
You're not doing anything inside the ng-click function, and you have the ng-if outside of the controller linked to the variable inside it.
https://jsfiddle.net/gdxwtoL7/1/
HTML
<div class="well" ng-controller="MyController">
<a class="btn btn-primary" ng-click="handleAnchorClick()">Enter Address</a>
<br>
<br>
<div ng-if="selMe">
adfadf
</div>
</div>
JS
angular.module('myapp', []);
angular.module('myapp').controller('MyController', MyController)
function MyController($scope) {
$scope.handleAnchorClick = function () {
$scope.selMe = true
}
}
The controller has to be aware of the div you want it to show.
the ng-if is waiting for the value of the selme which you can alter from the controller.
The ng-model binds your data to your controller in adding two-way data binding.
I made a little enhancement to your code to toggle the div when the text is clicked multiple times.
https://jsfiddle.net/gdxwtoL7/2/
<div class="well" ng-controller="MyController">
<a class="btn btn-primary" ng-model="selMe" ng-click="handleAnchorClick(selMe)">Enter Address</a>
<br>
<br>
<div ng-if="selMe">
adfadf
</div>
</div>
angular.module('myapp', []);
angular.module('myapp').controller('MyController', MyController)
function MyController($scope) {
$scope.handleAnchorClick = function (selMe) {
$scope.selMe = !selMe
}
}
The ngModel directive binds an input,select, textarea (or > custom form control) to a property on the scope using NgModelController, which is created and exposed by this directive.
ngModel is responsible for:
Binding the view into the model, which other directives such as input, textarea or select require.
Providing validation behavior (i.e. required, number, email, url).
Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
Setting related css classes on the element (ng-valid, ng-invalid, ng-dirty, ng-pristine, ng-touched, ng-untouched, ng-empty, ng-not-empty) including animations.
Registering the control with its parent form.
The ngClick directive allows you to specify custom behavior when an element is clicked.
Note : we need ng-click to capture the event and manipulate the data stored in ng-model.
Here is simple code without the need of controller:
<div class="well">
<a class="btn btn-primary" href="" ng-click="show=true">Enter Address</a>
</div>
<br>
<br>
<div ng-show="show">
adfadf
</div>
As #MikeHughesIII already pointed out, outside of your controller you can't reach $scope variables.
I am adding a quick snippet made after Mike's answer for completeness sake, showing a show/hide (toggle) approach, where the function sets the visibility variable to the opposite of its current status (true or false) when the function is invoked.
Hope that helps to clarify the issue.
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.6.2" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="myController">
<h1>Hello {{hello}}!</h1>
<a href ng-click="toggleDivVisibility()">Enter your address</a>
<br>
<textarea ng-if="visible" name="address" id="address" cols="30" rows="5"></textarea>
</div>
<script>
angular.module('myApp', []);
angular.module('myApp')
.controller('myController', myController);
function myController($scope) {
$scope.hello = "world";
$scope.visible = false;
$scope.toggleDivVisibility = function() {
$scope.hello = 'mondo';
$scope.visible = !$scope.visible;
}
}
</script>
</body>
</html>

AngularJS Dynamic Template with indexed scope variable arrays

I'm using AngularJS and trying to create a form where I can dynamically add new inputs, similar to this fiddle: http://jsfiddle.net/V4BqE/ (Main HTML below, working code in fiddle).
<div ng-app="myApp" ng-controller="MyCtrl">
<div add-input>
<button>add input</button>
</div>
</div>
I would like to be able to use a HTML template for my form since the input I'm adding is ~300 lines long. My issue is I cannot figure out how to index the scope variable containing the data when used in a template. I've tried to make my own modified version of the above code on plnkr http://plnkr.co/edit/4zeaFoDeX0sGTuBMCQP2?p=info . However, when I click the button no form elements appear.
Online (plnkr) I get a 404 not found for my template.html, but I think that is just a plnkr limitation. On my machine with a Python HttpServer I get an Error: [$parse:syntax] for the $templateRequest and a TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'. when using the $http.get method.
Any advice for getting the indexed scope variable array to work with an external html file?
Thanks, JR
Edit: Update plnkr link
You can do it without directive & external template
what you are trying to do does not require a directive (it actually much simple with the basic angularjs tools)
http://plnkr.co/edit/LVzGN8D2WSL2nR1W7vJB?p=preview
html
<body>
<div class="container" ng-app="myApp" ng-controller="MyCtrl">
<button class="btn btn-primary" type="button" ng-click="addPhoneInput()">add input</button>
<form>
<div ng-repeat="item in telephoneNumbers">
<hr>
<input type="text" ng-model="item.phone">
</div>
</form>
<hr>
<div class="well">
<h4>Phone Numbers,</h4>
<p ng-repeat="item in telephoneNumbers">{{item.phone}}</p>
</div>
</div>
</body>
js
var app = angular.module('myApp', []);
app.controller('MyCtrl', ['$scope', function($scope) {
// Define $scope.telephone as an array
$scope.telephoneNumbers = [];
$scope.addPhoneInput = function() {
$scope.telephoneNumbers.push({});
};
// This is just so you can see the array values changing and working! Check your console as you're typing in the inputs :)
$scope.$watch('telephoneNumbers', function(value) {
console.log(value);
}, true);
}]);
If you insist using a directive,
http://plnkr.co/edit/BGLqqTez2k9lUO0HZ5g1?p=preview
phone-number.template.html
<div>
<hr>
<input type="text" ng-model="ngModel" >
</div>
html
<body>
<div class="container" ng-app="myApp" ng-controller="MyCtrl">
<button class="btn btn-primary" type="button" ng-click="addPhoneInput()">add input</button>
<form>
<phone-number ng-repeat="item in telephoneNumbers" ng-model="item.phone"></phone-number>
</form>
<hr>
<div class="well">
<h4>Phone Numbers,</h4>
<p ng-repeat="item in telephoneNumbers">{{item.phone}}</p>
</div>
</div>
</body>
js
var app = angular.module('myApp', []);
app.controller('MyCtrl', ['$scope', function($scope) {
// Define $scope.telephone as an array
$scope.telephoneNumbers = [];
$scope.addPhoneInput = function() {
$scope.telephoneNumbers.push({});
};
// This is just so you can see the array values changing and working! Check your console as you're typing in the inputs :)
$scope.$watch('telephoneNumbers', function(value) {
console.log(value);
}, true);
}]);
app.directive('phoneNumber', function(){
return {
replace:true,
scope: {
ngModel: '=',
},
templateUrl: "phone-number.template.html"
}
});

AngularJS directive to add other directives not functional

I am trying to construct a directive that adds form groups to a particular div. I'm attempting doing this by binding a directive to a button in my html. My application is VERY simple as this is all I'm trying to do at the moment. Similar to this fiddle Anyway, my app initiates fine and the home controller is included. Also I get 200 status codes on the inclusion of my directive, and my code throws no errors. Here is my html:
<form id="addFields">
<div class="row">
<div class="col-xs-4">
</div>
<div class="col-xs-4 text-center">
<div addInputFieldsButton></div>
<input id="fieldName" placeholder="Enter field name:"></input>
<button id="addFieldBtn" addInputFields><span class="glyphicon glyphicon-plus"></span></button>
</div>
<div class="col-xs-4">
</div>
</div>
</form>
<div id="reviewFields">
</div>
Notice I am attempting both to add the button that is supposed to add input fields, and just bind the directive that adds input fields to an existing button as an attribute. Neither work.
addInputFields directive:
(function () {
angular.module('reviewModule')
.directive('addInputFields', addInputFields);
addInputFields.$inject = ['$log'];
function addInputFields ($log) {
return function (scope, element, attr) {
$log.debug('binding click event to add review button now.');
element.bind('click', function ($compile) {
$log.debug('button bound.');
angular.element(document.getElementById('reviewFields')).append($compile("<button>YOU MADE A BUTTON, COOL BRO</button>")(scope));
});
}
}
})()
and my directive for attempting to add a button that has the above binding:
(function () {
angular.module('reviewModule')
.directive('addInputFieldsButton', addInputFieldsButton);
addInputFieldsButton.$inject = ['$log'];
function addInputFieldsButton ($log) {
return {
restrict : 'E',
template : '<input id="fieldName" placeholder="Enter field name:"></input>\
<button id="addFieldBtn" addInputFields><span class="glyphicon glyphicon-plus"></span></button>'
};
};
})()
I copied the fiddle almost exactly, and really have no idea why nothing is happening while attempting to use either of these directives. Forgive me if my error is obvious, I am still pretty new to AngularJS.
I believe your second directive is not defined correctly on UI, it should be - separated with smaller case add-input-fields instead of addInputFields.
Code
(function () {
angular.module('reviewModule')
.directive('addInputFieldsButton', addInputFieldsButton);
addInputFieldsButton.$inject = ['$log'];
function addInputFieldsButton ($log) {
return {
restrict : 'E',
template : '<input id="fieldName" placeholder="Enter field name:"></input>\
<button id="addFieldBtn" add-input-fields><span class="glyphicon glyphicon-plus"></span></button>'
//^^^^^^^^^^^^^^^here is change
};
};
})()

Angular data - having trouble binding updated values to scope (using slider)

I'm trying to use Angular to create a tool that accepts user input from a slider tool, and automatically updates an "estimate" field whenever the values are changed. However, the data only seems to be binding on the way to the view; the user input is displayed, but doesn't seem to register in the scope.
Here's the HTML:
<h3 ng-controller="calcController" ng-model="estimate" class="floating-menu">${{estimate}}</h3>
<form ng-controller="calcController" method="post" action="/estimate">
<div class="container">
<div class="vals">${{values.insurance}}</div>
<div id="insurance-slider">
<slider floor="0" ceiling="250" value="125" step="25" precision="2" ng-model="values.insurance" ng-change="changeVal()" translate="currencyFormatting"></slider>
</div>
<div class="container">
<div class="vals">${{values.lease}}</div>
<div id="lease-slider">
<slider floor="0" ceiling="250" value="125" step="25" precision="2" ng-model="values.lease" translate="currencyFormatting"></slider>
</div>
</div>
</form>
Here's the Angular:
//create the module
angular.module('Breeze', ['ngRoute', 'ngResource', 'ui.slider'])
.config(function($routeProvider) {
$routeProvider
// route for the calculator page
.when('/', {
templateUrl: 'partials/calculator.html',
controller: 'calcController'
});
})
// create the controller and inject Angular's $scope
.controller('calcController', function($scope, $http) {
$scope.values = {
// Default values
insurance: 125,
lease: 125,
};
$scope.estimate = $scope.values.insurance + $scope.values.lease
}
// Formatting for scale bar
$scope.currencyFormatting = function(value) {
return value.toString() + " $";
}
})
I've tried adding a $watch and a $broadcast function but can't seem to get them working.
This is what it looked like in Angular:
$scope.$watch("values", function(newVal, oldVal, scope) {
scope.estimate = addVals();
})
$scope.changeVal = function() {
$scope.estimate = addVals();
console.log('hi')
$scope.estimate = $scope.values.insurance + $scope.values.lease
}
Ideas?
You are using two nested ng-controller="calcController"in your HTML. Hence two instanciations of the controller (two $scope) which will have their own estimate value, so when you update the inner estimate, the upper one is not aware of that.
You should remove the second ng-controller in your <form> (if the controller were different, then you would put the estimate variable in an object such as $scope.model.estimate so properties inheritance between the two controllers actually work. But in your case there is no reason to have two controllers).
Also, you should remove that ng-model="estimate" in your <h3> tag, which has no effect.
Just setting them equal in the beginning wont work. In your view, instead of having an estimate field just use this
<div ng-controller="calcController" ng-app="Breeze">
<h3 ng-model="estimate" class="floating-menu">${{values.insurance + values.lease }}</h3>
<form method="post" action="/estimate">
<div class="container">
<div class="vals">${{values.insurance}}</div>
<div id="insurance-slider">
<slider floor="0" ceiling="250" value="125" step="25" precision="2" ng-model="values.insurance" ng-change="changeVal()" translate="currencyFormatting"></slider>
</div>
<div class="container">
<div class="vals">${{values.lease}}</div>
<div id="lease-slider">
<slider floor="0" ceiling="250" value="125" step="25" precision="2" ng-model="values.lease" translate="currencyFormatting"></slider>
</div>
</div>
</div>
</form>
</div>
This will keep them adding as the values change.
Another thing that would be breaking is that you have two controller calls to the same controller and no app. Need to change it as below with the new div and remove the rest of the ng-controllers.
Also, make sure that you have the ui slide javascript file included on your page.
Did you try to bind to object instead of primitive type?
$scope.values.estimate = $scope.values.insurance + $scope.values.lease
see similar question here:
Angularjs: 2 way binding not working in included template
Here is a fiddle http://jsfiddle.net/U3pVM/9330/

angular bootstrap modal masks forms

I am trying to get at an angular form in scope to verify validations etc.
Base Case
Let us say I have the following HTML:
<body ng-controller='MyAwesomeController'>
<form name="fooForm">
<textarea ng-model="reason" required=""></textarea>
</form>
<div class='btn btn-primary' ng-click="submit()" ng-class="{'btn-disabled': true}">Awesome Submit Button</div>
</body>
And the following controller
angular.module('MyAwesomeController', '$scope', function(scope){
scope.submit = function(){
console.debug(scope)
}
})
This will work, and upon inspection, scope will contain a fooForm key.
Let us now say that I introduce an angular ui bootstrap modal into the mix like so:
Broken Case
<body ng-controller="AwesomeParentController">
<div class="btn btn-primary" ng-click="open()">Open the Modal</div>
</body>
with the following two controllers:
.controller('AwesomeParentController', ['$scope', '$modal', function(scope, modal){
scope.open = function(){
options = {
windowClass: 'modal discontinue-modal',
templateUrl: 'modal.html',
controller: 'AwesomeModalController'
}
modalInstance = modal.open(options)
modalInstance.result.then(function(){
console.debug("result!")
})
}
}])
.controller("AwesomeModalController", ['$scope', '$modalInstance', function(scope, modalInstance){
scope.submit = function(){
console.debug(scope)
}
}])
with the following modal.html:
<form name="fooForm">
<textarea ng-model="reason" required=""></textarea>
</form>
<div class='btn btn-primary' ng-click="submit()">Awesome Submit Button</div>
When the first button is clicked, a modal opens, the second button click prints a scope, which does NOT contain fooForm, rather fooForm resides on scope.$$childTail
Plunkr: http://embed.plnkr.co/jFGU0teIbL3kUXdyTPxR/preview
Possible Fix
<form name="fooForm">
<div ng-controller ="JankyFormController">
<textarea ng-model="reason" required=""></textarea>
<div class='btn btn-primary' ng-click="submit()">Awesome Submit Button</div>
</div>
</form>
.controller('JankyFormController', ['$scope', function(scope){
scope.models['fooForm'] = scope.fooForm
}])
Plunkr: http://embed.plnkr.co/BAZFbS7hFRhHm8DqOpQy/preview
Why is the form being masked? What would be a clean way to get at it without having to create a nested child controller? what if I want to bind ng-class to the forms validity? Would I now have to construct a watch around ($$childTail).fooForm.$valid?
Update: angular ui-bootstrap 0.12.0 fixes the problem - transclusion scope becomes the same as controller's scope, no need for $parent.. Just upgrade.
Before 0.12.0:
Angular-UI modals are using transclusion to attach modal content, which means any new scope entries made within modal are created in child scope. This happens with form directive.
This is known issue: https://github.com/angular-ui/bootstrap/issues/969
I proposed the quick workaround which works for me, with Angular 1.2.16:
<form name="$parent.userForm">
The userForm is created and available in modal's controller $scope. Thanks to scope inheritance userForm access stays untouched in the markup.
<div ng-class="{'has-error': userForm.email.$invalid}"}>

Categories

Resources