how to clear checkbox checked status? with vue - checkbox

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.

Related

How to make data reactive behind an array?

I noticed than Vuejs doesn't observe data change after the first level of an array.
How to change this behaviour?
new Vue({
el: '#container',
data: {
value: [],
},
beforeMount() {
this.value[0] = 'first level'
this.value[1] = []
this.value[1][0] = 'second level'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="container">
value[0] :
<input type="text" id="container" placeholder="enter text" v-model="value[0]">
<span>{{ value[0] }}</span>
<hr>
value[1][0] :
<input type="text" id="container" placeholder="enter text" v-model="value[1][0]">
<span>{{ value[1][0] }}</span>
</div>
Your problem is due to the main caveat of Vue reactivity system :
See working snippet below :
new Vue({
el: '#container',
data: {
value: [],
},
methods:{
updateValue(event){
this.$set(this.value, 1, [event.target.value])
}
},
beforeMount() {
this.value[0] = 'first level'
this.value[1] = []
this.value[1][0] = 'second level'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="container">
value[0] :
<input type="text" id="container" placeholder="enter text" v-model="value[0]">
<span>{{ value[0] }}</span>
<hr>
value[1][0] :
<input type="text" id="container" placeholder="enter text" #input="updateValue" :value="value[1][0]">
<span>{{ value[1][0] }}</span>
</div>
Note that if you try to do
updateValue(event){
this.value[1][0] = event.target.value
console.log(this.value[1][0]) //the value has changed but the UI is not refreshed
}
you will have the same problem : since what you're doing is basically modifying one item inside your array (same goes for one property within the same object), meaning that your array is the same, Vue has no way to know your UI needs to be re-rendered.
Vue.$set (or this.$set) explicitly tells Vue that a data has been modified, forcing it to re-render corresponding DOM element.
$set take three params : first is the object/array you want to update (this.value), second is the index/property that needs to be modified (1), third is the new value
It would also work if, instead of modifying one array item, you reassign the entire array :
updateValue(event){
this.value = [...this.value[0], [event.target.value]]
}

Check if state of dropdown has changed

I am using ng-options to display drop-down with options. Suppose I have three options in it for example option1, option2, option3.By default option1 is selected, now if a user selects option2, then $pristine becomes False and again if he selects option1 then from angularjs's prospective $pristine should be false but according to user he has not changed the option.So I was looking for a way to detect this change
Here is the Js fiddle demo
HTML
<div ng-app="myApp">
<div ng-controller="ctrl">
<form name="myForm">
{{myForm.$pristine}}
<select ng-model='list' ng-options='item.name for item in data'>
</select>
</form>
</div>
</div>
JS code
var app = angular.module('myApp', []);
app.controller('ctrl', function($scope) {
$scope.data = [{
id: 1,
name: 'test'
}, {
id: 2,
name: 'test1'
}];
$scope.list = $scope.data[0];
$scope.$watch('list', function(o, n) {
if ($scope.list == $scope.data[0])
$scope.myForm.$pristine = true;
})
});
You have add watch on your list model then this can be achieved
That is exactly what ng-change is for.
Usage would be like this (added $index to show you another option):
HTML
<div ng-app="myApp">
<div ng-controller="listController">
<form name="myForm">
<select ng-model="currOption"
ng-options='item.name for item in data'
ng-change="optionChanged(currOption, $index)">
</select>
</form>
</div>
</div>
Controller
angular.module('myApp')
.controller('listController', function($scope) {
$scope.data = [{
id: 1,
name: 'option1'
}, {
id: 2,
name: 'option2'
}, {
id: 3,
name: 'option3'
}];
$scope.optionChanged(option, index) {
// option = the selection the user just made.
// index = the index of the selection in your data. I.E. $scope.data[index]
};
});

Angular - Data Bind Issue

So the main functionality I want is here, which is selecting an option from the drop-down menu and having it populate my input field.
JSFiddle:
<div ng-app="app" ng-controller="MainCtrl">
Two things I want to fix:
When typing into the input ("Email Subject") field I don't wan't it to change the drop-down menu option.
I want the input field to initialize with it's placeholder value of ("Email Subject") instead of initializing with "Select a Canned Response"
I'm assuming this means making the input field have a one-way data bind, but I'm not sure how to do this, any help is appreciated.
<div ng-app="app" ng-controller="MainCtrl">
<input ng-model="CannedResponse" placeholder="Email Subject"><!--change this-->
<div class="item item-input item-select" >
<div class="input-label"> </div>
<select ng-model="newSelectedITem" ng-options="CannedResponse as CannedResponse.label for CannedResponse in CannedResponses"
ng-change="yourFunction()"> <!--change this-->
<option value="{{item.value}}"> </option>
</select>
</div>
</div>
js code
angular.module('app', [])
.controller('MainCtrl', function($scope) {
$scope.CannedResponses = [{
label: 'Selet a Canned Response',
value: 0
}, {
label: 'Hi there whats up',
value: 1
}, {
label: 'Here is another response',
value: 2
}, {
label: 'Why not select this one?',
value: 3
}];
$scope.newSelectedITem = $scope.CannedResponses[0] //ADD THIS (X1)
$scope.CannedResponse='';//add this
$scope.yourFunction = function() {//add this function
$scope.CannedResponse = $scope.newSelectedITem.label;
};
});
see where I wrote change this. There where you have to change your code.

ng-model using two dynamic value

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?

How can I use Angular to output dynamic form fields?

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.

Resources