I am trying to use check list where ng-model will bind the selected data based on two dynamic value.
My code is:
<div ng-repeat="individualFieldInfo in formInatiatingData">
<div ng-repeat="individualListItem in individualFieldInfo.list"
<input type="checkbox" ng- model=
"userGivenData[individualFieldInfo.fieldName][individualListItem.value]">
{{individualListItem}}
</div>
</div>
Here,
userGivenData[individualFieldInfo.fieldName][individualListItem.value]"
is not workng.
My JSON is:
$scope.userGivenData={
}
$scope.formInatiatingData = [
{
type:"checkList",
fieldName:"Fruit",
list : [
{
id:1,
value:"mango"
},
{
id:2,
value:"Banana"
},
{
id:3,
value:"Jackfruit"
}
]
}
]
For single dynamic binding userGivenData[individualFieldInfo.fieldName] is working. But, for two dynamicvalues, its not working.
I am searching for a way where if a user check a checkbox, it will be binded in userGivenData.fieldName.value
angular.module('myApp', [])
.controller('MyController', function($scope){
$scope.someComplex = {
someInnerObj: {
thisIsAProperty: 'withSomeValue'
}
};
$scope.thing1 = 'someInnerObj';
$scope.thing2 = 'thisIsAProperty';
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyController">
<pre>{{someComplex|json}}</pre>
<pre>{{someComplex[thing1][thing2]}}</pre>
<input type="text" ng-model="someComplex[thing1][thing2]"/>
</div>
In a test case this does work... can you output some more of the data objects values you show there like I did in the sample here using the json filter and pre tags?
Related
This question is specific to AngularJS 1.6.5 and above and does not apply to any previous versions.
I have a form with some ng-options selects and some text inputs. When a user selects the None / Custom option, a text input appears to the right. Once the user starts typing in that input, the None / Custom option is deselected and a new unknown option is added.
This is the result of a bug fix introduced in AngularJS 1.6.5. Previously when the user typed in the input, the None / Custom select remained selected.
My question is: how can I recreate the original pre-1.6.5 behavior?
To see an example of what I am talking about, check out these two fiddles. The first fiddle demonstrates the original behavior, the second demonstrates the current (undesired) behavior. Simply select the None / Custom option and type something in the adjacent text input.
Example Fiddles
AngularJS 1.6.4 example (good)
AngularJS 1.6.5 example (bad)
HTML code:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.min.js" type="text/javascript"></script>
</head>
<body ng-app="myapp">
<div class="wrapper" ng-controller="someCtrl">
<div class="row" ng-repeat="t in utmTags">
<div class="col-xs-1"><label style="color:#666;">{{t}}</label></div>
<div class="col-xs-2">
<select class="form-control flat inline-dropdown skinny" ng-options="o.value as o.key for o in utmOptions" ng-model="company[t]">
<option ng-value="company[t]" label="None / Custom"></option>
</select>
</div>
<div class="col-xs-2">
<input type="text" class="form-control flat skinny" style="width:180px;" placeholder="Add tag, without spaces" ng-if="!utmVariables.includes(company[t])" ng-model="company[t]" ng-pattern="utmRegex" />
<div ng-if="utmVariables.includes(company[t])">e.g. {{utmOptionsExamples[company[t]]}}</div>
</div>
</div>
</div>
</body>
</html>
Javascript Code:
var app = angular.module('myapp', ['ui.bootstrap']);
app.controller('someCtrl', ['$scope', function($scope) {
$scope.utmOptions = [
{ key: '{AmbassadorFirstNameLastInitialID}', value: '{AmbassadorFirstNameLastInitialID}' },
{ key: '{AdminFirstNameLastInitial}', value: '{AdminFirstNameLastInitial}' },
{ key: '{ContentDomain}', value: '{ContentDomain}' },
{ key: '{ShareDate}', value: '{ShareDate}' },
{ key: '{ShareID}', value: '{ShareID}' },
{ key: '{SocialPlatform}', value: '{SocialPlatform}' }
];
$scope.utmOptionsExamples = {
'{AmbassadorFirstNameLastInitialID}': 'TracJ-1318',
'{AdminFirstNameLastInitial}': 'ShanA',
'{ContentDomain}': 'entrepreneur.com',
'{ShareDate}': '2017-08-23',
'{ShareID}': '79131',
'{SocialPlatform}': 'Twitter'
};
$scope.utmRegex = /^[a-z0-9\-\._]*[\{\}]{0}$|^{[a-z0-9\-\._]*}$/i;
$scope.utmTags = ['utm_medium', 'utm_source', 'utm_campaign', 'utm_term', 'utm_content'];
$scope.utmVariables = ['{AmbassadorFirstNameLastInitialID}', '{AdminFirstNameLastInitial}', '{ContentDomain}', '{ShareDate}', '{ShareID}', '{SocialPlatform}'];
$scope.company = {
utm_medium: undefined,
utm_source: undefined,
utm_campaign: undefined,
utm_term: undefined,
utm_content: undefined
}
}]);
app.run();
when i click next button, i want to remove checkbox checked status,
but i don't use vue to remove it.
this is demo code:
new Vue({
el: "#app",
data: {
id:0,
items: [
{'id':1,'name':'chk-a','options':['a1','b1','c1','d1']},
{'id':2,'name':'chk-b','options':['a2','b2','c2','d2']},
{'id':3,'name':'chk-c','options':['a3','b3','c3','d3']},
{'id':4,'name':'chk-d','options':['a4','b4','c4','d4']},
]
},
methods: {
next: function (id) {
if(id<this.items.length){
this.id++
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<h1>Checkbox</h1>
<div v-for="item in items[id].options">
<input type="radio" name="chk" :id="id">
<label for="two">{{item}}</label>
</div>
<button #click="next(id+1)">Next</button>
</div>
It looks like you have four sections to a form and want the user to select one option from each form. If so, consider having a field selected_option or something similar and use v-model:
new Vue({
el: "#app",
data: {
id:0,
items: [
{'id':1,'name':'chk-a','options':['a1','b1','c1','d1'], 'selected_option': ''},
{'id':2,'name':'chk-b','options':['a2','b2','c2','d2'], 'selected_option': 'c2'},
{'id':3,'name':'chk-c','options':['a3','b3','c3','d3'], 'selected_option': ''},
{'id':4,'name':'chk-d','options':['a4','b4','c4','d4'], 'selected_option': ''},
]
},
methods: {
next: function (id) {
if(id<this.items.length){
this.id++
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<h1>Checkbox</h1>
<div v-for="item in items[id].options">
<input type="radio" :name="items[id].name" :id="id" v-model="items[id].selected_option" :value="item">
<label for="two">{{item}}</label>
</div>
<button #click="next(id+1)">Next</button>
</div>
Note that the above has 'c2' automatically selected in the second section. This is to show you an example of default values for v-model using checkboxes and to show you what the expected data will look like. Retrieving these values later can be done easily by iterating through items and pulling out your corresponding selected_option values.
Likewise, you can remove the checked status by simply setting the value to '' for any given selected_option field.
i want to get the selected checkboxes in my loop, for that check box i have to retrive the amount field onclick.
Here is my HTML script :
<div ng-repeat="$item in items">
Amount :<input ng-model="$item.daily_data.payment_amount">
Check : <input type=checkbox ng-model="checkAmount[$item.daily_data.id]" ng-value="$item.id" >
</div>
<input type="button" ng-click="checkNow()" />
The below script showing all check boxes . i want the only selected one.
JS Script :
$scope.checkAmount = {};
$scope.checkNow(){
console.log($scope.checkAmount);
}
First of all to use functions with $scope you should do something like this:
$scope.checkNow = function() {
...
}
or
$scope.checkNow = checkNow;
function checkNow() {
...
}
About your problem:
You could bind the checkboxes to a property (something like checked), so you can have the items that are checked easily in your controller.
Then, to calculate the total of all checked amount , I' suggest you to use Array.prototype.filter() + Array.prototype.reduce().
Here's a demo based on your original code:
(function() {
angular
.module("app", [])
.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$scope'];
function MainCtrl($scope) {
$scope.checkNow = checkNow;
$scope.checkAmount = {};
$scope.items = [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
}
];
function checkNow() {
$scope.total = $scope.items.filter(function(value) {
return value.checked;
}).reduce(function(a, b) {
return a + b.amount;
}, 0);
}
}
})();
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<div ng-repeat="$item in items">
<label>
Amount: <input type="number" ng-model="$item.amount">
</label>
<label>
Check: <input type=checkbox ng-model="$item.checked">
</label>
</div>
<button type="button" ng-click="checkNow()">Check now</button>
<hr>
<label for="total">Total</label>
<input type="number" id="total" disabled ng-model="total">
</body>
</html>
I want to render a form, based on a dynamic field configuration:
$scope.fields = [
{ title: 'Label 1', type: 'text', value: 'value1'},
{ title: 'Label 2', type: 'textarea', value: 'value2'}
];
This should output something that behaves like:
<div>
<label>{{field.title}}<br />
<input type="text" ng-model="field.value"/>
</label>
</div>
<div>
<label>{{field.title}}<br />
<textarea ng-model="field.value" rows="5" cols="50"></textarea>
</label>
</div>
The simple implementation would be to use if statements to render the templates for each field type. However, as Angular doesn't support if statements, I'm lead to the direction of directives. My problem is understanding how the data binding works. The documentation for directives is a bit dense and theoretical.
I've mocked up a bare bones example of what I try to do here: http://jsfiddle.net/gunnarlium/aj8G3/4/
The problem is that the form fields aren't bound to the model, so the $scope.fields in submit() isn't updated. I suspect the content of my directive function is quite wrong ... :)
Going forward, I need to also support other field types, like radio buttons, check boxes, selects, etc.
The first problem you are running into regardless of the directive you are trying to create is using ng-repeat within a form with form elements. It can be tricky do to how ng-repeat creates a new scope.
This directive creates new scope.
I recommend also instead of using element.html that you use ngSwitch instead in a partial template.
<div class="form-row" data-ng-switch on="field.type">
<div data-ng-switch-when="text">
{{ field.title }}: <input type="text" data-ng-model="field.value" />
</div>
<div data-ng-switch-when="textarea">
{{ field.title }}: <textarea data-ng-model="field.value"></textarea>
</div>
</div>
This still leaves you with the problem of modifying form elements in child scope due to ng-repeat and for that I suggest using the ngChange method on each element to set the value when an item has changed. This is one of the few items that I don't think AngularJS handles very well at this time.
You might consider Metawidget for this. It uses JSON schema, but is otherwise very close to your use case. Complete sample:
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js" type="text/javascript"></script>
<script src="http://metawidget.org/js/3.5/metawidget-core.min.js" type="text/javascript"></script>
<script src="http://metawidget.org/js/3.5/metawidget-angular.min.js" type="text/javascript"></script>
<script type="text/javascript">
angular.module( 'myApp', [ 'metawidget' ] )
.controller( 'myController', function( $scope ) {
$scope.metawidgetConfig = {
inspector: function() {
return {
properties: {
label1: {
type: 'string'
},
label2: {
type: 'string',
large: true
}
}
}
}
}
$scope.saveTo = {
label1: 'value1',
label2: 'value2'
}
$scope.save = function() {
console.log( $scope.saveTo );
}
} );
</script>
</head>
<body ng-controller="myController">
<metawidget ng-model="saveTo" config="metawidgetConfig">
</metawidget>
<button ng-click="save()">Save</button>
</body>
</html>
The type attribute can be changed when the element is out of DOM, so why not a small directive which removes it from DOM, changes it type and then add back to the same place?
The $watch is optional, as the objective can be change it dynamically once and not keep changing it.
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.rangeType = 'range';
$scope.newType = 'date'
});
app.directive('dynamicInput', function(){
return {
restrict: "A",
link: linkFunction
};
function linkFunction($scope, $element, $attrs){
if($attrs.watch){
$scope.$watch(function(){ return $attrs.dynamicInput; }, function(newValue){
changeType(newValue);
})
}
else
changeType($attrs.dynamicInput);
function changeType(type){
var prev = $element[0].previousSibling;
var parent = $element.parent();
$element.remove().attr('type', type);
if(prev)
angular.element(prev).after($element);
else
parent.append($element);
}
}
});
span {
font-size: .7em;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="MainCtrl">
<h2>Watching Type Change</h2>
Enter Type: <input ng-model="newType" /><br/>
Using Type (with siblings): <span>Before</span><input dynamic-input="{{newType}}" watch="true" /><span>After</span><Br>
Using Type (without siblings): <div><input dynamic-input="{{newType}}" watch="true" /></div>
<br/><br/><br/>
<h2>Without Watch</h3>
Checkbox: <input dynamic-input="checkbox" /><br />
Password: <input dynamic-input="{{ 'password' }}" value="password"/><br />
Radio: <input dynamic-input="radio" /><br/>
Range: <input dynamic-input="{{ rangeType }}" />
</div>
Tested in latest Chrome and IE11.
I have multiple select boxes and I'm using angular JS. Each select box needs to have a different selected value. So far everything I've seen has elements that share the same selected value. In order to achieve this a scope is used. Since I can potentially have hundreds of drop downs... actually thousands... what is the correct approach here? Create a scope for each one? Try to have one scope that mutates with each select box?Here is an example with what I have jsfiddle.Any help is much appreciated.
Thanks
function MyCntrl($scope) {
$scope.colors = [
{name:'black'},
{name:'red'},
{yellow:'yellow'}
]
$scope.isSel = $scope.colors[1];
}
You need to bind each select box to its own scope. You can do it manually, binding each one to a new object instead of the same isSel, or you can use a ng-repeat like so:
http://jsfiddle.net/zmU8R/9/
html:
<div ng-app="">
<div ng-controller="MyCntrl">
<div ng-repeat="control in controls">
<select ng-model="control.isSel" ng-options="c.name for c in colors"></select><br />
</div>
<hr />
<div ng-repeat="control in controls">
{{control.id}}: {{control.isSel}}
</div>
</div>
</div>
script:
function MyCntrl($scope) {
$scope.controls = [
{id: 1, isSel:null},
{id: 2, isSel:null},
{id: 3, isSel:null}
];
$scope.colors = [
{name:'black'},
{name:'red'},
{name:'yellow'}
];
}
Not sure I've figured out what you exactly want. As far as I understand, you need each selectbox to have different value. So, you need to bind each selectbox to a different variable.
<div ng-app="myApp">
<div ng-controller="myCtrl">
<hr/>
<div ng-repeat="n in [] | range: selectionsCount">
<select ng-model="selectedValues[$index]" ng-options="c.name for c in colors"></select>
</div>
{{ selectedValues }}
</div>
</div>
For a much clearer example, I made selectboxes count variable here.
angular.module('myApp', [])
.controller('myCtrl', function ($scope) {
$scope.colors = [
{name: 'black'},
{name: 'red'},
{name: 'yellow'}
];
$scope.selectedValues = [];
$scope.selectionsCount = 5;
})
.filter('range', function () {
return function(input, total) {
total = parseInt(total);
for (var i=0; i<total; i++)
input.push(i);
return input;
};
});
You can test it here: http://jsfiddle.net/zmU8R/7/
If I misunderstood your question, feel free to correct me.