Create a form container with Angular JS component - javascript

I am using Angular JS 1.5.6 and I would like to use only component and not directive.
I have more than 5 views using a form in my application. The style of the form is exactly the same in all the views, it is only the content of the form that changes. I have made components (input, button, etc..) and I would like to create a component for the form, it would be like a container component, in which I can put different components. In view A, the form will contain 1 button and 1 input, in view B the form will contain 2 button and 2 inputs, etc... But I don't know if it is possible to do it and how to dow it. I have plnkered this. I would like to create a component for the form.
<!DOCTYPE html>
<html ng-app="MyApp">
<head>
<link data-require="bootstrap-css#3.3.6" data-semver="3.3.6" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.6/angular.min.js"></script>
<script src="script.js"></script>
<script src="myInput.js"></script>
<script src="myButton.js"></script>
</head>
<body ng-controller="MyCtrl">
<div class="container">
<h2>My form</h2>
<form role="form">
<my-input label="Firstname"></my-input>
<my-input label="Lastname"></my-input>
<my-button label="Submit"></my-button>
</form>
</div>
</body>
</html>

There are two basic problems need to be solved in your form container:
Dynamic template for your form
Handle summit should be run outside of your component.
I solve two problems by
Read template via attrs property
Pass handler function as a binding property
Code for form component:
angular.module('MyApp').component('myForm', {
templateUrl: function($element, $attrs) {
return $attrs.template; // read template file from component attribute
},
controller: MyFormController,
bindings: {
label: '#',
summitHandler: '&' // call to function outside of component via this property
}
});
In MyFormController, we need handle summit event by calling to summitHandler function passed via binding property:
function MyFormController($scope, $element, $attrs) {
var ctrl = this;
ctrl.summitForm = function(data) {
// call handler with data summited
var handler = ctrl.summitHandler();
handler(data);
}
}
That all for our form container component.
Now you can add my-form with:
<my-form label="Personal Info" template="myForm.html" summit-handler="ctrl.myFormHandler"></my-form>
Property ctrl.myFormHandler will be a function handling event in myCtl with:
ctrl.myFormHandler = function(data){
console.log('receive test summit:' + data);
ctrl.dataReceived = data;
}
See it run here in Plunk.
Typing in the age field, you can see it will be passed to outside of form. Extend it with more features as you want.

Look at this project:
http://kelp404.github.io/angular-form-builder/
If you want to implement this yourself you will need to create a template dynamically and compile it before it's injected into the DOM.
This project can be useful:
https://github.com/incuna/angular-bind-html-compile
It's directive but it will inject and compile your form elements (components or directives) for you:
<div bind-html-compile="formElements.input1"></div>

Related

How import and use method from outer js-file into AngularJS?

I should like to use functional from this file https://ahunter.ru/js/min/ahunter_suggest.js
The documentation says that
1. import files
<head>
...
<script src="path_to_scripts/jquery.min.js"></script>
<script src="path_to_scripts/ahunter_suggest.js"></script>
...
</head>
Add id for input field
<div>
<input id="js-AddressField" placeholder="Введите адрес">
</div>
Add script
<script>
var options = { id : 'js-AddressField' };
AhunterSuggest.Address.Solid( options );
</script>
I added file to my project directory and added link into index.html, but i can't call
AhunterSuggest.Address.Solid( options ); in one of my controller.
How will do it right?
In your AngularJS controller, u have a method for initialising the controller right? If so, you have to call 3rd party library initialiser (3rd code in your message)
Make sure to call this, after dom is ready and angularJS controller initiated.
Your angular controller before (atm)
init() {
this.xxx()
}
Angular Controller after: (should be sth like this)
init() {
this.xxx()
this.initialiseAHunterSuggest()
}
initialiseAHunterSuggest() {
var options = { id : 'js-AddressField' }
AhunterSuggest.Address.Solid( options )
}

How to apply the AngularJS controller to the included page using ng-include

I have two html pages, main.html and nested.html, so the nested.html is actually included in main.html, this way:
main.html starts
<div ng-include src="'nested.html'"></div>
main.html ends
FULL CODE
nested.html
<html>
<h1> I am included, but controller not sending the values to me. </h1>
<input type="text" id="myDatePicker"/>
</html>
main.html
<html ng-controller="MainCtrl">
<h1> I am main.html and controller working properly on me, please take care of my child **given below** </h1>
<div ng-include src="'nested.html'"></div>
</html>
Controller Code:
(function() {
'use strict';
angular.module('bootstrapApp').controller('MainCtrl', MainCtrl);
function MainCtrl($scope, $location, $http, $compile) {
$("#myDatePicker").datepicker({
orientation: 'left'
});
}
});
The Controller code makes the text field a datepicker using jquery. The input field is in nested.html page. If the controller works on the included page, the textfield becomes a datepicker. That's it
Please guide me through how do I apply the same controller MainCtrl to included nested.html page
Thanks.
There are 2 important things to mention here:
The controller scope of the MainCtrl is already applied to the included nested.html. You can review that in the following Plunker: https://embed.plnkr.co/cvILaL3VvnJ5c1YDHDT1/.
You shouldn't use a jQuery datepicker from out of a AngularJS controller. Instead use a datepicker, which is intended for AngularJS (e.g. angular-datepicker)
from what I can guess,you must be using $scope.someVar and you are trying to render it in ng-include. Try creating an obj inside the controller like below:
$scope.obj= {
someVar : 'Value'
};
and use it in ng-include i.e. nested.html as {{obj.someVar}}.
Update 1
As per your new update, try AngularJS datepicker and let me know.
Try to add controller into nested.html file like:
<div ng-controller="MainController">
<h1> I am included, but controller not sending the values to me. </h1>
<input type="text" id="myDatePicker"/>
</div>
And from your comment:

Angular JS: data binding with dynamically added html elements

I am facing problems with two way data binding in angular js. Here is the sample code.
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body ng-app="">
<div>
<p>Input something in the input box:</p>
<p>Name: <input type="text" ng-model="name"></p>
<div id="jack">
</div>
<script>
$("document").ready(function(){
$("#jack").append("<p ng-bind='name'></p>");
});
</script>
</body>
</html>
Over here I am dynamically adding a paragraph with ng-bind to a div called jack using jQuery
For some reason when I type something in input box it is not reflecting in paragraph with ng-bind property.
I am a novice in angular js and would request you to provide me a simple solution to tackle this issue.
You cannot use jQuery to modify DOM outside Angular this way. Angular does not know about that binding as it was not compiled by Angular.
To solve this particular sample, simply remove the jQuery script and change the HTML to this:
<p>Input something in the input box:</p>
<p>Name: <input type="text" ng-model="name"></p>
<p ng-bind="name"></p>
The example above will work, but I imagine this was not a real-world example. If you post a more specific scenario, I can update my answer to help you solve that.
Edit
Based on your comment, I would create a simple directive to which you could pass your template, it would compile the template and inject the compiled template in the DOM.
Directive:
function myTemplateCompile($compile) {
return {
restrict: 'E',
link: link
}
function link(scope, elem, attrs) {
attrs.$observe('template', (template) => {
elem.html(template);
$compile(elem.contents())(scope);
});
}
}
HTML
<my-template-compile template="any HTML you might need including bindings"></my-template-compile>
You can then change the template attribute of the directive on the fly and it will re-compile and update the DOM based on the new value.
The example above should point you in the right direction. I just have to warn you, that this might be quite dangerous. If the content you are injecting is coming from some user input, this might have very severe security implications. Please make sure you are not exposing your application to attacks.
Well first we need to define the app and create custom directive.
var myApp=angular.module('myApp',[])
.controller('myCtrl',function($scope){
$scope.name="Your name";
})
myApp.directive('myDirective', function() {
return {
restrict: 'E',
template: '<span data-ng-bind="name"></span>'
};
});
After this you need to use above created directive as like below
<my-Directive></my-Directive>

Dynamically add angular attributes to old html forms

I have a project where I'm currently trying to refactor an old system that was hinged on jquery from the ground up with angular 1.x. However, there are a lot of old HTML forms that I'd like to reuse the bulk of so I don't want to recreate them. I'd love it if there was a way to keep it purely angular, but I'm honestly at a loss of how I'd do that (or whether or not I can). I'm fairly new to angular so there are a lot of inner workings to it that I'm still not privy to.
I've searched around on google and other places including here and I can't really even find other people talking about it. That tells me that either I'm searching badly or it's something that I should probably not be working towards.
All the html pages have identically id'd fields so I feel I can reliably base things on that. For example: all forms with first name text boxes have an id of "cl_fname".
Is there anyway that I can accomplish: getting the form, adding an ng-model="cl_fname" or something to the relevant tag and then display the form? I've gotten to the point where I can get the html page, hold it in the scope and then display using ng-bind-html, but figuring out how to add angular attributes to specific elements I can't figure out.
You can achieve this with jQuery and the attr() method.
I created a plunker here that demonstrates adding angular to an existing "plain" html form.
In the example, I'm using id selectors, but you could use any combination of selectors to ensure you get the right elements.
The below is a quick code snippet from my Plunker example:
HTML:
<div ng-app="myApp">
<form id='myForm1' data-test="test2">
<span>First Name:</span>
<input type="text" id="myForm1_firstName" />
<input type="submit" id="myForm1_Submit" value="Go!" />
</form>
</div>
JS:
// set up angular
var myApp = angular.module('myApp', []);
myApp.controller('MyForm1Controller', ['$scope', function($scope) {
$scope.firstName = 'Angular Working!';
}]);
// use jQuery to add the relevent attributes to our form
var jqMyForm1 = $('form#myForm1');
var jqTxtFirstName = jqMyForm1.find('input[type="text"]#myForm1_firstName');
//add controller to form
jqMyForm1.attr('ng-controller', 'MyForm1Controller');
//bind the textbox to the angular 'firstName' variable
jqTxtFirstName.attr('ng-model', "firstName");
EDIT:
just realised you want to load the html form dynamically.
Version 2 of the plunker (here) will now dynamically load a HTML form from an external resource (separate html page), inject it into the current page, add the angular bindings to it, and then get angular to recognise it.
The key to getting angular to recognise the form is the use of the $compile object (angular $compile documentation).
Again, quick snippets of the code in use:
HTML (main page):
<div ng-app="myApp" ng-controller="LoadingController"></div>
HTML (myForm1.html):
<form id='myForm1' data-test="test2">
<span>First Name:</span>
<input type="text" id="myForm1_firstName" />
<input type="submit" id="myForm1_Submit" value="Go!" />
</form>
JS:
// set up angular
var myApp = angular.module('myApp', []);
// main controller for loading the dynamic form
myApp.controller('LoadingController', ['$scope','$http','$compile', function($scope,$http,$compile) {
$scope.loadHtmlForm = function(formURL) {
$http.get(formURL).then(function successCallback(response){
var jqForm = $(response.data);
var jqTxtFirstName = jqForm.find('input[type="text"]#myForm1_firstName');
//add controller to form
jqForm.attr('ng-controller', 'MyForm1Controller');
//bind the textbox to the angular 'firstName' variable
jqTxtFirstName.attr('ng-model', "firstName");
$('div').append(jqForm);
$compile(jqForm[0])($scope);
});
}
$scope.loadHtmlForm('myForm1.html');
}]);
// form controller for managing the data
myApp.controller('MyForm1Controller', ['$scope', function($scope) {
$scope.firstName = 'Angular Working!';
}]);

Data binding not working in AngularJS with input tag

Below is my angularjs code,
When I try to enter any text in the textbox, it doesn't appear in the binding.
<!DOCTYPE html>
<html lang="en">
<head>
<script src="js/angular.js"></script>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.controller('cont', ['$scope', function($scope) {
}]);
</script>
</head>
<body ng-app="myApp">
<input type="text" ng-bind="user"></input>
<p>{{user}}</p>
</body>
</html>
You would need to use ng-model directive for 2-way binding. ng-bind is just one-way which updates the bound element with data (model value) when a digest cycle happens. Without ng-model when you update the textbox there wont be any digest cycle. Angular has directive definition for types like input and other form controls which requires optional ng-model directive. And these element directives registers events like change/input etc only if it gets the optional ng-model controller on the target element. And when you have them it uses ng-model controller to set the view-value, model-value and triggers the digest cycle when that event occurs. Of course with the new angular versions there is an ng-model-options which you can set at the element level or at a global level to specify when do you want the model value update (and form validation) to happen.
So do:-
<input type="text" ng-model="user" name="user"></input>
<p>{{user}}</p>
Though not an issue in your case, you are missing the usage of ng-controller="cont". Without it all properties will be attached to the rootScope in your case. So may be:
<body ng-app="myApp">
<div ng-controller="cont">
<input type="text" ng-model="user" name="user"></input>
<p>{{user}}</p>
</div>
</body>

Categories

Resources