I am new to AngularJS. I am using nested ng-repeat to generate multiple forms. And form data is json which is dynamic. So the problem is Data binding for TEXT, TEXTAREA and SELECT input is not working.
JSFiddle: http://jsfiddle.net/gTc5v/10/
HTML:
<div ng-controller="ctrl">
<div ng-repeat="form in forms">
<h2>{{form.name}}</h2>
<div ng-repeat="field in form.form_fields">
<div ng-If="showElement(field,'RADIO')">
<div>
<h3>{{field.caption}}</h3>
<span ng-repeat="option in field.optionValues">
<input ng-model="field.answer" type="radio" value="{{option}}" >{{option}}
<br/></input>
</span>
</div>
</div>
<div ng-If="showElement(field,'TEXT')">
<input ng-model="field.answer" type="text" placeholder="{{field.caption}}" />
</div>
<div ng-If="showElement(field,'PARAGRAPH_TEXT')">
<textarea ng-model="field.answer" placeholder="{{field.caption}}"></textarea>
</div>
<div ng-If="showElement(field,'LIST')">
<div>
<h3>{{field.caption}}</h3>
<select ng-model="field.answer">
<option ng-repeat="option in field.optionValues">{{option}}</option>
</select>
</div>
</div>
<div ng-If="showElement(field,'CHECKBOX')">
<div>
<h3>{{field.caption}}</h3>
<span ng-repeat="option in field.optionValues">
<input ng-checked="field.answers.indexOf(option) != -1" ng-click="toggleCheck(field.answers,option)" type="checkbox">{{option}}
<br/></input>
</span>
</div>
</div>
</div>
<br/>
<div ng-repeat="field in form.form_fields">
{{field.caption}} : {{field.answer}}{{field.answers}}
</div>
<br/>
</div>
AangularJS:
angular.module('myApp', []).directive('ngIf', function () {
return {
link: function (scope, element, attrs) {
if (scope.$eval(attrs.ngIf)) {
// remove '<div ng-if...></div>'
element.replaceWith(element.children());
} else {
element.replaceWith(' ');
}
}
};
});
function ctrl($scope) {
$scope.fields = [{
"caption": "Gender",
"questionType": "RADIO",
"optionValues": ["Male", "Female"],
"fieldPriority": "INCLUDE"
}, {
"caption": "City",
"questionType": "TEXT",
"optionValues": "",
"fieldPriority": "INCLUDE"
}, {
"caption": "Address",
"questionType": "PARAGRAPH_TEXT",
"optionValues": "",
"fieldPriority": "INCLUDE"
}, {
"caption": "Nationality",
"questionType": "LIST",
"optionValues": ["Indian", "American"],
"fieldPriority": "INCLUDE"
}, {
"caption": "Tea/Coffee",
"questionType": "CHECKBOX",
"optionValues": ["Tea", "Coffee"],
"fieldPriority": "INCLUDE"
}];
angular.forEach($scope.fields, function(field) {
if(field.questionType == 'CHECKBOX'){
field.answers = [];
} else{
field.answer = "";
}
});
$scope.forms = [{"name":"Form 1","form_fields" : angular.copy($scope.fields)},{"name":"Form 2","form_fields" : angular.copy($scope.fields)}];
$scope.showElement = function (field, type) {
return field.questionType == type;
};
$scope.toggleCheck = function (answer, option) {
if (answer.indexOf(option) === -1) {
answer.push(option);
} else {
answer.splice(answer.indexOf(option), 1);
}
};
}
thanks
Try to remove element.replaceWith below in your directive link function.
element.replaceWith(element.children());
You don't need to call replaceWith() in directive because directive has wrapped the template content and it will be included once compiled.
Here is a workable jsfiddle demo
Do you really need your own ng-If directive? Your example works fine if you use the built-in ng-if.
Note that in either case you need to write it as a lower-case ng-if in your HTML.
Update:
jsfiddle
Related
I'm trying to add a class to a parent container each time an "advanced" link is clicked.
So with jQuery I would just write something like..
$(this).closest('.row').addClass('overlay');
or
$(this).closest('section').addClass('overlay');
But it seems to be getting a little complex with Vue to just add a class to the parent container of the item that is clicked. I'm sure there is a more simple way to go about it.
Here is an example of my code.
<div id="app">
<section v-bind:class="{ overlay: i == sectionActive && rowActive == null }" v-for="(section, i) in sections">
Advanced
<div class="advanced-fields" v-bind:class="{ overlay: i == sectionActive && rowActive == null }">
<fieldset>
<label>
ID
<input type="text" name="section[id]" v-model="section.id">
</label>
</fieldset>
<fieldset>
<label>
Class
<input type="text" name="section[css_class]" v-model="section.css_class">
</label>
</fieldset>
</div>
<div class="row" v-bind:class="{ overlay: i == sectionActive && row_i == rowActive }" v-for="(row, row_i) in section.rows">
Advanced
<div class="advanced-fields" v-bind:class="{ overlay: i == sectionActive && row_i == rowActive }">
<fieldset>
<label>ID</label>
<input type="text" name="" v-model="row.id">
</fieldset>
<fieldset>
<label>CSS Class</label>
<input type="text" name="" v-model="row.css_class">
</fieldset>
</div>
</div>
</section>
<pre>{{ $data }}</pre>
</div>
<script>
new Vue({
el: "#app",
data: {
"sections": [{
"id": "section-1",
"css_class": "",
"rows": [{
"id": "",
"css_class": ""
}, {
"id": "",
"css_class": ""
}]
}, {
"id": "section-2",
"css_class": '',
"rows": [{
"id": "",
"css_class": ""
}]
}],
sectionActive: null,
rowActive: null,
columnActive: null
},
methods: {
toggleAdvanced: function(index) {
this.sectionActive = this.sectionActive === index ? null : index;
this.rowActive = null;
this.columnActive = null;
},
toggleRowAdvanced: function(section, row) {
var sectionIndex = this.sections.indexOf(section);
var rowIndex = section.rows.indexOf(row);
this.sectionActive = this.sectionActive === sectionIndex ? null : sectionIndex;
this.rowActive = this.rowActive === rowIndex ? null : rowIndex;
}
}
});
</script>
I need to do the same thing for columns but as you can see, it is getting too overly complicated. Any ideas on how to simplify this?
I know it would be easier to add a data attribute to each row, but I am saving the hash to a database and don't want to add in unnecessary data just for a UI toggle.
https://jsfiddle.net/ferne97/4jbutbkz/
I took a different approach and built several re-usable components. This removes all the complicated logic that you are putting into your Vue.
Vue.component("expand-link",{
template:`Advanced`,
data(){
return {
expanded: false
}
},
methods:{
toggle(){
this.expanded = !this.expanded
this.$emit('toggled', this.expanded)
}
}
})
Vue.component("expanded-fields",{
props:["details", "expanded"],
template:`
<div class="advanced-fields" :class="{overlay: expanded}">
<fieldset>
<label>
ID
<input type="text" name="section[id]" v-model="details.id">
</label>
</fieldset>
<fieldset>
<label>
Class
<input type="text" name="section[css_class]" v-model="details.css_class">
</label>
</fieldset>
</div>
`
})
Vue.component("expandable-section", {
props:["section"],
template:`
<section>
<expand-link #toggled="onToggle"></expand-link>
<expanded-fields :details="section" :expanded="expanded"></expanded-fields>
<expandable-row v-for="row in section.rows" :key="row" :row="row"></expandable-row>
</section>
`,
data(){
return {
expanded: false
}
},
methods:{
onToggle(val){
this.expanded = val
}
}
})
Vue.component("expandable-row",{
props:["row"],
template:`
<div class="row">
<h3>Row</h3>
<expand-link #toggled="onToggle"></expand-link>
<expanded-fields :details="row" :expanded="expanded"></expanded-fields>
</div>
`,
data(){
return {
expanded: false
}
},
methods:{
onToggle(val){
this.expanded = val
}
}
})
And the template simply becomes
<div id="app">
<expandable-section v-for="section in sections" :key="section" :section="section"></expandable-section>
<pre>{{ $data }}</pre>
</div>
Here is your fiddle updated.
var jsonData ='[
{"type":"product",
"id":1,"label":"Color",
"placeholder":"Select Jean Color",
"description":"",
"defaultValue":"Brown",
"choices":[{
"text":"Denim",
"price":"$0.00",
"isSelected":"false"
},
{
"text":"Black",
"price":"$0.00",
"isSelected":"true"
},
{
"text":"Brown",
"price":"$0.00",
"isSelected":"false"
}],
"conditionalLogic":
{
"actionType":"show",
"logicType":"all",
"checkbox":true,
"rules":[{
"fieldId":2,
"operator":"or",
"value":"Regular"
},
{
"fieldId":3,
"operator":"or",
"value":"Low"
}]
}},
{
"type":"product","id":2,"label":"Color","placeholder":"Select Color","description":"Color Descrioton","defaultValue":"Red","choices":[{"text":"Red","price":"200","isSelected":"true"}],"conditionalLogic":""},{"type":"product","id":3,"label":"Select Fit","placeholder":"Select Jean Fit","description":"","defaultValue":"Fit","choices":[{"text":"Regular","price":"$0.00","isSelected":"false"},{"text":"Skinny","price":"$10.00","isSelected":"false"},{"text":"Fit","price":"$5.00","isSelected":"false"}],"conditionalLogic":{"actionType":"show","logicType":"all","checkbox":true}},{"type":"product","id":4,"label":"Select Rise","placeholder":"Select Rise","description":"","defaultValue":"Low","choices":[{"text":"High","price":"$29.00","isSelected":"false"},{"text":"Low","price":"$0.00","isSelected":"true"}],"conditionalLogic":""},{"type":"product","id":5,"label":"Size","placeholder":"Select Size","description":"","defaultValue":"Size 36","choices":[{"text":"Size 30","price":"100.00","isSelected":"false"},{"text":"Size 32","price":"100.00","isSelected":"true"},{"text":"Size 34","price":"100.00","isSelected":"true"},{"text":"Size 36","price":"100.00","isSelected":"false"}],"conditionalLogic":""}]';
$scope.attributes = jsonData;
HTML
<div class="col-sm-6" ng-repeat="product_attribute in attributes">
<div class=" form-group">
<label class="font-noraml">{{product_attribute.label}}</label>
<select class="form-control m-b" name="option_choices" ng-selected="option.isSelected=='true'" ng-model="option.price" ng-options="option.price as option.text for option in product_attribute.choices">
<option value="">{{product_attribute.placeholder}}</option>
</select>
</div>
</div>
Above I have posted my JSON and HTML code. I want to show all the attributes choices in my dropdown with default selected value. Please check my HTML I have tried ng-selected="option.isSelected=='true'" to default select my choices options. But that is not working.
Screenshot:
You must have defaultValue as object that comprising all properties.Here is your solution ->
jsfiddle
For example:
"defaultValue": {
"text": "Black",
"price": "$0.00",
"isSelected": "true"
}
I'm building a form of radio question types.
Here is code for view:
<div class="form-group" ng-class="{ 'has-error': form.$submitted && form[field.id].$invalid }" ng-if="field.type === 'radio'">
<label for="{{field.id}}">{{field.title}}</label>
<br>
<label ng-repeat="value in field.values">
<input type="radio" id="{{field.id}}" name="field.id" ng-model="formData[field.id]" value="{{value.title}}"> {{value.title}}</label>
<p class="form-group-note" ng-if="field.info" ng-bind="field.info"></p>
<div ng-show="form.$submitted" ng-cloack>
<span class="help-block" ng-show="form['{{field.id}}'].$error.required" ng-if="field.validations.required">Please enter a value, this field is required</span>
</div>
Selected Value is : {{formData[field.id]}}
</div>
JSON data I'm feeding is
{
"groups": [
{
"id": "4_2",
"title": "Passport",
"sections": [
{
"id": "4_2_section",
"fields": [
{
"id": "select_id",
"title": "Select type of question",
"type": "select",
"info": "Always select \"Yes\"",
"size": {
"width": 100,
"height": 1
},
"validations": {
"required": true
},
"values": [
{
"id": 0,
"title": "Not Selected"
},
{
"id": 1,
"title": "Yes"
},
{
"id": 2,
"title": "No"
}
]
}
]
The result I get for the radio questions is like following:
As you can see, all radio buttons are aligned horizontally.
How do I align them vertically?? I mean one radio button on one row.
Add a style to your HTML
.display-block {
display: block;
}
<label class="display-block" ng-repeat="value in field.values">
<input type="radio" id="{{field.id}}" name="field.id" ng-model="formData[field.id]" value="{{value.title}}"> {{value.title}}
</label>
Watch what you want to repeat.
<ul>
<li ng-repeat="value in field.values">
<label for="{{field.id}}"> {{value.title}}</label>
<input type="radio" id="{{field.id}}" name="field.id" ng-model="formData[field.id]" value="{{value.title}}">
</li>
</ul>
Wrap your inputs in ul, li. Below code will help for sure :
HTML:
<form name="myForm" ng-controller="MyCtrl">
<p>Favorite Beatle</p>
<ul>
<li ng-repeat="person in people">
<label>{{person.name}}
<input type="radio" ng-model="$parent.name" name="name" value="{{person.name}}" required />
</label>
</li>
</ul>
<p><tt>myForm.$invalid: {{myForm.$invalid}}</tt></p>
<button ng-disabled="myForm.$invalid">Submit</button>
</form>
JavaScript :
function MyCtrl($scope) {
$scope.people = [{
name: "John"
}, {
name: "Paul"
}, {
name: "George"
}, {
name: "Ringo"
}];
}
JsFiddle : https://jsfiddle.net/nikdtu/zp2131zw/
I am working with Kendo UI and angular grid application. In my application I define Kendo TabStrip. In first tab I have Kendo UI grid with data and second tab contains appropriate textbox fields, which are to be filled when user select some row in a grid. My grid is populated with data when load page and it work perfectly, but data binding to textbox fields not working. How to bind data on textbox fields when user select row in a grid?
This is my JSON data (which is in separate file):
[
{ "Id": 1, "AccountNo": "10236", "PostingDate": "20.01.2015", "MaturityDate": "24.01.2015", "Description": "description1", "DocumentType": "doc1" },
{ "Id": 2, "AccountNo": "10648", "PostingDate": "26.01.2015", "MaturityDate": "28.01.2015", "Description": "description2", "DocumentType": "doc2" },
{ "Id": 3, "AccountNo": "10700", "PostingDate": "22.01.2015", "MaturityDate": "25.01.2015", "Description": "description3", "DocumentType": "doc3" },
{ "Id": 4, "AccountNo": "10810", "PostingDate": "24.01.2015", "MaturityDate": "27.01.2015", "Description": "description4", "DocumentType": "doc4" },
{ "Id": 5, "AccountNo": "10101", "PostingDate": "29.01.2015", "MaturityDate": "30.01.2015", "Description": "description5", "DocumentType": "doc5" },
{ "Id": 6, "AccountNo": "10364", "PostingDate": "25.01.2015", "MaturityDate": "31.01.2015", "Description": "description6", "DocumentType": "doc6" }
]
This is my angular service (which is in separate file):
angular.module("app").factory('myService', function ($http) {
return {
getAll: function (onSuccess, onError) {
return $http.get('/Scripts/app/data/json/master/masterGridData.js').success(function (data, status, headers, config) {
onSuccess(data);
}).error(function (data, status, headers, config) {
onError(data);
});
}
}
});
This is my controller (which is in separate file):
var app = angular.module("app", ["kendo.directives"]).controller("myController", function ($scope, myService) {
$scope.tabStrip = null;
$scope.$watch('tabStrip', function () {
$scope.tabStrip.select(0);
});
$scope.masterDataSource = new kendo.data.DataSource({
transport: {
read: function (options) {
url = "/Scripts/app/data/json/master/masterGridData.js",
myService.getAll(function (data) {
options.success(data);
}).error(function (data) {
options.error(data);
})
}
},
schema: {
model: {
id: "Id",
fields: {
Id: { type: "number" },
AccountNo: { type: "string" },
PostingDate: { type: "string" },
MaturityDate: { type: "string" },
Description: { type: "string" },
DocumentType: { type: "string" }
}
}
},
pageSize: 16
});
$scope.gridMaster = {
columns: [
{ field: "Id", hidden: true },
{ field: "AccountNo", title: "Account No", width: "77px", template: '<div style="text-align:left;">#= kendo.toString(AccountNo) #</div>' },
{ field: "PostingDate", title: "Posting Date", width: "70px" },
{ field: "MaturityDate", title: "Maturity Date" width: "70px" },
{ field: "Description", title: "Description", width: "170px" },
{ field: "DocumentType", hidden: true }
],
dataSource: $scope.masterDataSource,
selectable: true,
filterable: true,
scrollable: true,
pageable: {
pageSize: 16,
//refresh: true,
pageSizes: ["50", "100", "200", "All"]
},
toolbar: [{
name: "create"
}]
};
});
This is my HTML:
<html>
<head>
<!-- css and javaScript files -->
</head>
<body ng-app="app" ng-controller="myController">
<div class="divH3Style">
<h3 class="h3LabelForm">Grid Master</h3>
</div>
<div id="tabstrip" class="k-tabstrip-wrapper" data-kendo-tab-strip="tabStrip">
<ul>
<li>Overview</li>
<li>Update</li>
</ul>
<div id="tabstrip-1">
<div id="gridMaster" kendo-grid k-options="gridMaster" k-data-source="masterDataSource">
</div>
</div>
<div id="tabstrip-2" style="overflow: hidden">
<div id="tabStrip2Half1">
<div class="divHeightStyle">
<label for="accountNumber" class="labelTextSize">Account Number:</label>
<input id="accountNumber" type="number" class="k-textboxField" name="accountNumber" ng-model="masterDataSource.data.AccountNo" placeholder="Account Number" />
</div>
<div class="datepickerStyle">
<label for="postingDate" class="labelTextSize">Posting Date:</label>
<input id="postingDate" class="k-datetimepickerMaster" name="postingDate" ng-model="masterDataSource.data.PostingDate" />
</div>
<div class="divHeightStyle">
<label for="desccription" class="labelTextSize">Description:</label>
<input id="desccription" type="text" class="k-textboxField" name="description" placeholder="Description" ng-model="masterDataSource.data.Description" />
</div>
<div class="datepickerStyle">
<label for="maturityDate" class="labelTextSize">Maturity Date:</label>
<input id="maturityDate" class="k-datetimepickerMaster" name="maturityDate" ng-model="masterDataSource.data.MaturityDate" />
</div>
</div>
<div id="tabStrip2Half2">
<div class="divHeightStyle">
<label for="documentType" class="labelTextSize">Document Type:</label>
<input id="documentType" type="text" class="k-textboxField" name="documentType" placeholder="Document Type" ng-model="masterDataSource.data.DocumentType" />
</div>
<div>
<button type="button" id="saveDataMasterGrid" class="k-button buttonSaveCancel" onclick="saveDataMasterGrid()">Save</button>
<button type="button" id="cancelDataMasterGrid" class="k-button buttonSaveCancel" onclick="cancelButtonMasterGrid()">Cancel</button>
</div>
</div>
</div>
</div>
</body>
</html>
Any help will be very useful.
I am solve that problem. I was added change event function in $scope.gridMaster:
$scope.gridMaster = {
...
change: function () {
var dataItem = this.dataItem(this.select());
$scope.accountNumber = dataItem.AccountNo;
$scope.postingDate = dataItem.PostingDate;
$scope.description = dataItem.Description;
$scope.maturityDate = dataItem.MaturityDate;
$scope.documentType = dataItem.DocumentType;
}
}
And I was change ng-model in my HTML page:
<div id="tabstrip-2" style="overflow: hidden">
<div id="tabStrip2Half1">
<div class="divHeightStyle">
<label for="accountNumber" class="labelTextSize">Account Number:</label>
<input id="accountNumber" type="number" class="k-textboxField" name="accountNumber" ng-model="accountNumber" placeholder="Account Number" />
</div>
<div class="datepickerStyle">
<label for="postingDate" class="labelTextSize">Posting Date:</label>
<input id="postingDate" class="k-datetimepickerMaster" name="postingDate" ng-model="postingDate" />
</div>
<div class="divHeightStyle">
<label for="desccription" class="labelTextSize">Description:</label>
<input id="desccription" type="text" class="k-textboxField" name="description" placeholder="Description" ng-model="description" />
</div>
<div class="datepickerStyle">
<label for="maturityDate" class="labelTextSize">Maturity Date:</label>
<input id="maturityDate" class="k-datetimepickerMaster" name="maturityDate" ng-model="maturityDate" />
</div>
</div>
<div id="tabStrip2Half2">
<div class="divHeightStyle">
<label for="documentType" class="labelTextSize">Document Type:</label>
<input id="documentType" type="text" class="k-textboxField" name="documentType" placeholder="Document Type" ng-model="documentType" />
</div>
<div>
<button type="button" id="saveDataMasterGrid" class="k-button buttonSaveCancel" onclick="saveDataMasterGrid()">Save</button>
<button type="button" id="cancelDataMasterGrid" class="k-button buttonSaveCancel" onclick="cancelButtonMasterGrid()">Cancel</button>
</div>
</div>
</div>
i have a list of a few radio buttons and a input, and for some reason i can't get the selected radio value.
here is my example, or jsfiddle. If you look in the console and submit the form you can see that even if you select a radio button the response is undefined, but the input value comes in ok
var SampleApp;
(function (SampleApp) {
var app = angular.module('sampleApp', ['ionic']);
app.controller('MainCtrl', function ($scope) {
$scope.tests = [{
"id": "1"
}, {
"id": "2"
}, {
"id": "3"
}];
$scope.addResource = function (settings) {
console.log(settings);
};
});
})(SampleApp || (SampleApp = {}));
<link href="http://code.ionicframework.com/1.0.0-beta.6/css/ionic.css" rel="stylesheet"/>
<script src="http://code.ionicframework.com/1.0.0-beta.6/js/ionic.bundle.js"></script>
<div>
<div ng-app="sampleApp" ng-controller="MainCtrl">
<ion-content style="display:block">
<form ng-submit="addResource(settings)">
<div class="list list-inset" ng-repeat="test in tests">
<ion-radio ng-model="settings.id" value="{{test.id}}">{{test.id}}</ion-radio>
</div>
<br/>
<div class="list" style="margin: 5px 10px;">
<label class="item item-input item-stacked-label"> <span class="input-label">Email</span>
<input type="text" ng-model="settings.email">
</label>
</div>
<button class="button button-positive">Save</button>
</form>
</ion-content>
</div>
</div>
You need to pre-initialize settings in your controller so that settings is not created inside the child scope if ng-repeat, instead it is inherited from controller scope, also use ng-value="test.id" instead of value=interpolation (value="{{test.id}}"), it binds the given expression to the value of your radio, so that when the element is selected, the ngModel of that element is set to the bound value.
var SampleApp;
(function (SampleApp) {
var app = angular.module('sampleApp', ['ionic']);
app.controller('MainCtrl', function ($scope) {
$scope.settings = {}; //Initialize model here
$scope.tests = [{
"id": "1"
}, {
"id": "2"
}, {
"id": "3"
}];
$scope.addResource = function () {
console.log($scope.settings);
};
});
})(SampleApp || (SampleApp = {}));
<link href="http://code.ionicframework.com/1.0.0-beta.6/css/ionic.css" rel="stylesheet"/>
<script src="http://code.ionicframework.com/1.0.0-beta.6/js/ionic.bundle.js"></script>
<div>
<div ng-app="sampleApp" ng-controller="MainCtrl">
<ion-content style="display:block">
<form ng-submit="addResource()">
<div class="list list-inset" ng-repeat="test in tests">
<ion-radio ng-model="settings.id" ng-value="test.id">{{test.id}}</ion-radio>
</div>
<br/>
<div class="list" style="margin: 5px 10px;">
<label class="item item-input item-stacked-label"> <span class="input-label">Email</span>
<input type="text" ng-model="settings.email">
</label>
</div>
<button class="button button-positive">Save</button>
</form>
</ion-content>
</div>
</div>