AngularJS: Generate a form dynamically in AngularJS - javascript

I am trying to generate a HTML form.
I have an object which contains an array of form elements like
{
"formFields": [
{
"type": "select",
"label": "Fabric",
"name": "fabric",
"options": [
"Georgette",
"Crepe",
"Net",
"Lace"
]
},
{
"type": "text",
"label": "Weight",
"name": "weight",
"options": []
}
]
}
I want to generate a form which has fields in accordance with the above object i.e. it should generate a Select labelled Fabric with drop down options "Georgette","Crepe","Net","Lace" and an input element of type text with label Weight.
What is the best way to do this in AngularJS?

I would make a directive which accepts a form field object as input and $compiles a template based on the input.
Html:
<div my-input="settings"></div>
Js:
angular.module('myApp').directive('myInput', ['$compile', function($compile) {
return {
restrict: 'EA',
require: 'ngModel',
link: linkFn,
scope: {
config: '=myInput'
}
};
function linkFn($scope, $element, $attrs, ngModelCtrl) {
init();
function init() {
$scope.model = {};
var template = getTemplate();
template.attr('ng-model', 'model.value');
$compile(template)($scope, function(clonedElem) {
$element.html(clonedElem);
});
}
function getTemplate() {
switch($scope.config.type) {
case 'text':
return '<input type="text"/>';
case 'select':
return '<input type="select" ng-options="option in config.options">';
}
}
}
}]);
This is from the top of my head so the code might be wrong but you get the idea.

You can refer to the sample here. Please find the code below:
HTML:
<div ng-app="app" ng-controller="test">
<form name="myForm" ng-submit="validateForm(myForm.$valid)">
<div ng-repeat="item in formData.formFields">
<div ng-if="item.type == 'text'">
<label>{{item.label}}: </label>
<input type="{{item.type}}" name="{{item.name}}">
</div>
<div ng-if="item.type == 'select'">
<label>{{item.label}}: </label>
<select name="{{item.name}}">
<option ng-repeat="opt in item.options" value="{{opt}}">{{opt}}</option>
</select>
</div>
<br>
</div>
</form>
</div>
JS:
var app = angular.module('app', []);
app.controller('test', function ($scope) {
$scope.formData = {
"formFields": [
{
"type": "select",
"label": "Fabric",
"name": "fabric",
"options": [
"Georgette",
"Crepe",
"Net",
"Lace"
]
},
{
"type": "text",
"label": "Weight",
"name": "weight",
"options": []
}
]
};
$scope.validateForm = function(isValid){
/*..*/
}
});

Related

why is ng-model not checking default radio button?

Below is the fiddle i am working:
http://jsfiddle.net/3c0dxf4d/
The ng-model has an object and the ng-value maps to object, why is my default value {"id":1,"name":"Bill"}
not getting selected by default.
Check out this fiddle http://jsfiddle.net/roz98eda/
var app = angular.module("app", []);
app.controller("ctrl", function($scope) {
$scope.customers = [{
"id": 1,
"name": "Bill"
}, {
"id": 2,
"name": "Bob"
}, {
"id": 3,
"name": "Biff"
}];
$scope.customer = {};
$scope.currentCustomer = {
"id": 1
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl">
<table>
<tr ng-repeat="theCustomer in customers">
<td>
<input type="radio" ng-model="$parent.currentCustomer.id" ng-value="theCustomer.id">{{theCustomer.name}}</td>
</tr>
</table>
<br>
<div>{{currentCustomer}}</div>
</div>
</div>
Because you've put the initial value to
$scope.currentCustomer = {
"id": 1,
"name": "Bill"
};
Just remove or change it.
Please check following code please.
app.controller("ctrl", function ($scope) {
$scope.customers = [{
"id": 1,
"name": "Bill"
}, {
"id": 2,
"name": "Bob"
}, {
"id": 3,
"name": "Biff"
}];
$scope.customer = {};
*$scope.currentCustomer = {
"id": 1,
"name": "Bill"
};*
})
Change
<input type="radio" ng-model="$parent.currentCustomer" name="foo" ng-value="theCustomer" id="{{theCustomer.id}}">
To
<input type="radio" ng-model="$parent.currentCustomer.id" name="foo" ng-value="theCustomer.id" id="{{theCustomer.id}}">{{theCustomer.name}}</td>
From ng-value docs
It is mainly used on input[radio] and option elements, so that when
the element is selected, the ngModel of that element (or its select
parent element) is set to the bound value.

Customizing ngTagsInput & autoComplete directives (AngularJS)

I am new to AngularJS and am currently working on an input field, which can accept multiple tags at a time along with the auto-complete feature, which display the available tags as dropdown options. For this I am using the ngTagsInput directive I found on the web(http://mbenford.github.io/ngTagsInput/), which gives me a custom HTML element <tags-input>. This works beautifully:
index.html:
<script>
var app = angular.module('plunker', ['ngTagsInput']);
app.controller('MainCtrl', function($scope, $http) {
$scope.tags = [
{ text: 'Tag1' },
{ text: 'Tag2' },
{ text: 'Tag3' }
];
$scope.loadTags = function(query) {
return $http.get('tags.json');
};
});
</script>
<div ng-app="plunker" ng-controller="MainCtrl">
<tags-input ng-model="tags" add-on-paste="true" display-property="text" placeholder="Add a Tag" add-from-autocomplete-only="true">
<auto-complete max-results-to-show="4" min-length="2" source="loadTags($query)"></auto-complete>
</tags-input>
</div>
tags.json:
[
{ "text": "Tag1" },
{ "text": "Tag2" },
{ "text": "Tag3" },
{ "text": "Tag4" },
{ "text": "Tag5" },
{ "text": "Tag6" },
{ "text": "Tag7" },
{ "text": "Tag8" },
{ "text": "Tag9" },
{ "text": "Tag10" }
]
However I wanted to use the standard HTML <input> element instead of the custom <tags-input> element which comes along with the directive, so with a lot of help and using <script src="https://code.jquery.com/jquery-3.1.0.js" integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk=" crossorigin="anonymous"></script> I was able to do it here:
Here is the new index.html:
<script>
var app = angular.module('plunker', ['ngTagsInput']);
app.controller('MainCtrl', function($scope, $http) {
$scope.tags = [
{ "id":1, "tagname": 'Tag1' },
{ "id":2, "tagname": 'Tag2' },
{ "id":3, "tagname": 'Tag3' },
{ "id":4, "tagname": 'Tag4' }
];
$scope.loadTags = function(query) {
return $http.get('tags.json');
};
});
app.directive('tagsInputAttr',
function($compile){
return {
restrict: 'A',
require: '?ngModel',
scope:{
ngModel: '='
},
link: function($scope, element, attrs, controller) {
var attrsText = '';
$.each($(element)[0].attributes, function(idx, attr) {
if (attr.nodeName === "tags-input-attr" || attr.nodeName === "ng-model")
return;
attrsText += " " + attr.nodeName + "='" + attr.nodeValue + "'";
});
var html ='<tags-input ng-model="ngModel" ' + attrsText + '></tags-input>';
e =$compile(html)($scope);
$(element).replaceWith(e);
}
};
}
);
</script>
<div ng-app="plunker" ng-controller="MainCtrl">
<input tags-input-attr ng-model="tags" add-on-paste="true" display-property="tagname" placeholder="Add tags here..." add-from-autocomplete-only="true">
<auto-complete max-results-to-show="3" min-length="2" source="loadTags($query)"></auto-complete>
</input>
</div>
And the new tags.json:
[
{ "id":1, "tagname": "Tag1" },
{ "id":2, "tagname": "Tag2" },
{ "id":3, "tagname": "Tag3" },
{ "id":4, "tagname": "Tag4" },
{ "id":5, "tagname": "Tag5" },
{ "id":6, "tagname": "Tag6" },
{ "id":7, "tagname": "Tag7" },
{ "id":8, "tagname": "Tag8" },
{ "id":9, "tagname": "Tag9" },
{ "id":10, "tagname": "Tag10" }
]
As you can notice,the new directive tagsInputAttr, which wraps the <tags-input> provides the same functionality and can be used inside <input> tag as an attribute along with the rest of attributes such as ng-model, display-property etc. So I don't have to use the <tags-input> element directly. The problem is that the <auto-complete> placed inside the <input> tag doesn't work.
For this I need to alter my directive, considering the following:
Note: I do not want to use jquery for this
My question is how do I wrap the <auto-complete> inside the same <input tags-input-attr> element:
Either as an attribute inside the same <input tags-input-attr> element
or as an attribute inside a standard HTML element like <div> or <span>, wrapped inside the same <input tags-input-attr> element.
If not the above two, then as last resort, as the <auto-complete> tag wrapped inside the same <input tags-input-attr> element
All help is appreciated.
Thanks in advance.
I made some changes on the previus directive and now it accepts all kind of transformation from attribute to element directive.
You still have the elem-as-attr attribute, but now you have to specify the value of it, that it will be the element that will replace.
Example:
<div elem-as-attr="tags-input"></div>
Your application JavaScript:
var app = angular.module('plunker', ['ngTagsInput']);
app.controller('MainCtrl', function($scope, $http) {
$scope.allTags = [
{ "id":1, "tagname": "Tag1" },
{ "id":2, "tagname": "Tag2" },
{ "id":3, "tagname": "Tag3" },
{ "id":4, "tagname": "Tag4" },
{ "id":5, "tagname": "Tag5" },
{ "id":6, "tagname": "Tag6" },
{ "id":7, "tagname": "Tag7" },
{ "id":8, "tagname": "Tag8" },
{ "id":9, "tagname": "Tag9" },
{ "id":10, "tagname": "Tag10" }
];
$scope.myTags =[
$scope.allTags[2],
$scope.allTags[4],
$scope.allTags[8]
];
$scope.loadTags = function(query) {
return $scope.allTags;
};
});
The directive code:
app.directive('elemAsAttr', function($compile) {
return {
restrict: 'A',
require: '?ngModel',
replace: true,
scope: true,
compile: function(tElement, tAttrs) {
return function($scope) {
var attrs = tElement[0].attributes;
var attrsText = '';
for (var i=0; i < attrs.length; i++) {
var attr = attrs.item(i);
if (attr.nodeName === "elem-as-attr") {
continue;
}
attrsText += " " + attr.nodeName + "='" + attr.nodeValue + "'";
}
var hasModel = $(tElement)[0].hasAttribute("ng-model");
var innerHtml = $(tElement)[0].innerHTML;
var html = '<' + tAttrs.elemAsAttr + attrsText + '>' + innerHtml + '</' + tAttrs.elemAsAttr + '>';
var e = hasModel ? $compile(html)($scope) : html;
$(tElement).replaceWith(e);
};
}
}
});
HTML code:
Element way:
<tags-input ng-model="myTags" add-on-paste="true" display-property="tagname" placeholder="Add a Tag" add-from-autocomplete-only="true">
<auto-complete max-results-to-show="10" display-property="tagname" min-length="2" source="loadTags($query)"></auto-complete>
</tags-input>
Attribute way:
<div elem-as-attr="tags-input" ng-model="myTags" add-on-paste="true" display-property="tagname" placeholder="Add tags here..." add-from-autocomplete-only="true">
<div elem-as-attr="auto-complete" max-results-to-show="10" display-property="tagname" min-length="2" source="loadTags($query)"></div>
</div>
Plunker:
https://plnkr.co/edit/9TqsXy
Note that you cannot use the input element for the tagsInput
because the input element does not have the closing tag in HTML. So
you will not be able to put the auto-complete element inside it.

node tree from json data using angularjs using recursive function

I want to create a node tree from a json.
index.html should load node tree recursively from person.json
and now the method is going into infinite loop.
please help me.
app.js
(function() {
var app = angular.module("app", ['directive.cusTree', 'directive.cnode']);
app.controller("Contrl", function($scope, $http) {
$scope.some = function() {
return $http.get('person.json').success(function(data) {
$scope.nodetree = data;
return data;
});
}
});
})();
person.json
{
"person": [{
"id": 1,
"name": "A1" ,
"child": [{
"id": 1.1,
"name": "A11",
"child": [{
"id": 1.11,
"name": "A111"
}, {
"id": 1.12,
"name": "A112"
}, {
"id": 1.13,
"name": "A113"
}]
}, {
"id": 1.2,
"name": "A12"
}, {
"id": 1.3,
"name": "A13",
"child": [{
"id": 1.31,
"name": "A131"
}, {
"id": 1.32,
"name": "A132"
}, {
"id": 1.33,
"name": "A133"
}]
}]
}, {
"id": 2,
"name": "B2"
}, {
"id": 3,
"name": "C3"
}, {
"id": 4,
"name": "D4"
}
]
}
item.html
<div ng-click="show(show)" ng-init="show=true" class="container" ng-repeat="node in nodedata" ng-if="nodedata.length>0">
<ul>
{{node.name}}
<br>
<div ng-if="node.child.length>0">
<custree nodedata="node.child"> </custree>
</div>
</ul>
</div>
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="app.js" type="text/javascript"></script>
<script src="cusTree.js" type="text/javascript"></script>
<script src="cnode.js" type="text/javascript"></script>
</head>
<body>
<div ng-controller="Contrl as n">
<div ng-init="nodetree=some()">
<div ng-repeat="node in nodetree">
<div class="container">
<custree nodedata="node"> </custree>
</div>
<br>
</div>
</div>
</div>
</body>
</html>
cusTree.js
angular.module('directive.cusTree',[])
.directive('custree',function(){
return{
restrict :'E',
scope: {
nodedata:'='
},
templateUrl:"item.html",
controller:function($scope){
//console.log("new="+ JSON.stringify($scope.nodedata));
}
};
});
If you are creating tree with AngularJS, you have to create 2 directives as below:
app.directive('nodeTree', function () {
return {
template: '<node ng-repeat="node in tree"></node>',
replace: true,
restrict: 'E',
scope: {
tree: '=children'
}
};
});
app.directive('node', function ($compile) {
return {
restrict: 'E',
replace: true,
templateUrl: 'partials/node.html', // HTML for a single node.
link: function (scope, element) {
/*
* Here we are checking that if current node has children then compiling/rendering children.
* */
if (scope.node && scope.node.children && scope.node.children.length > 0) {
var childNode = $compile('<ul class="tree" ng-if="!node.visibility"><node-tree children="node.children"></node-tree></ul>')(scope);
element.append(childNode);
}
},
controller: ["$scope", function ($scope) {
// This function is for just toggle the visibility of children
$scope.toggleVisibility = function (node) {
node.visibility = !node.visibility;
};
// Here We are marking check/un-check all the nodes.
$scope.checkNode = function (node) {
node.checked = !node.checked;
function checkChildren(c) {
angular.forEach(c.children, function (c) {
c.checked = node.checked;
checkChildren(c);
});
}
checkChildren(node);
};
}]
};
});
For More details you can checkout Github Link, its have working demo.

How to get the menu key from the json in angularjs

I want the get the "menu1" or "menu2" fields and so on, how to do it in angularjs?
the json is following:
{
"menu1": [
{
"item": "1",
"Auth": "content/articleList",
},
{
"item": "2",
"Auth": "content/articleList",
}],
"menu2": [
{
"item": "3",
"Auth": "publish/cacheSetting",
},
{
"item": "4",
"Auth": "publish/juggleList",
}]
}
You could do like below:
<div ng-repeat="(key, menu) in menus">
{{ key }}
<div ng-repeat="item in menu">
{{item.item}} : {{item.Auth}}
</div>
</div>
If you have a Service and want to use it in a Controller then you'd do the following to load JSON in the service, and call it from a Controller.
App.factory("MenuService", [ '$http', function($http) {
return {
getMenus: function() {
return $http.get( '/menuService' );
}
}
}]);
App.controller("SomeController", ['$scope', 'MenuService', function($scope,MenuService) {
$scope.doSomething = function() {
MenuService.getMenus().success( result ) {
$scope.menu1 = result.menu1;
$scope.menu2 = result.menu2;
// You got menu1 and menu2 now do something
}
}
}]);

Iterate over multiple array elements

I want to iterate over multiple array elements named urls and names. Below is the code where only urls are iterated. How can I use names also with urls?
JS
var app = angular.module('plunker',[]);
app.controller('Nav', function($scope) {
$scope.names = [
{
"name" : "myname 1",
},
{
"name": "myname 2"
}
]
$scope.urls = [
{
"url" : "http://google.com",
},
{
"url": "http://cnn.com"
}
];
});
HTML
<body ng-controller="Nav">
{{urls}}
{{names}}
<div ng-repeat="link in urls">
<input type="text" ng-model="link.url" />
<input type="text" /> //Want name here
</div>
</body>
Plnkr : http://plnkr.co/edit/OXXdFK2JElVaAh0uYmxG?p=preview
See answer in this plunker: http://plnkr.co/edit/OhnEpFjUVfLVUQw4TP3Z?p=preview
Add the ng-model like this:
<input type="text" ng-model="names[$index].name"/>
Every ng-repeat has some special properties exposed on local scope, see: http://docs.angularjs.org/api/ng.directive:ngRepeat
The one we are using is:
$index - iterator offset of the repeated element (0..length-1)
So as long as both arrays have the same length, this should work.
Can you change the code:
var app = angular.module('plunker',[]);
app.controller('Nav', function($scope) {
$scope.names = [
{
"name" : "myname 1",
},
{
"name": "myname 2"
}
]
$scope.urls = [
{
"url" : "http://google.com",
},
{
"url": "http://cnn.com"
}
];
});
to
var app = angular.module('plunker',[]);
app.controller('Nav', function($scope) {
$scope.urls = [
{
"name" : "myname 1",
"url" : "http://google.com"
},
{
"name": "myname 2",
"url": "http://cnn.com"
}
]
});
then in your HTML
<body ng-controller="Nav">
<div ng-repeat="url in urls">
<label>{{url.name}}<input type="text" ng-model="url.url" /></label>
</div>
</body>
http://plnkr.co/edit/6ne9jetDZTt4QhPctUpq?p=preview ?

Categories

Resources