Here's a snippet from a knockout based answer editor
<!-- ko foreach: Answers -->
<div class="qa-box" data-bindx="event: { mousedown: mouseDown, dragend: dragEnd, dragstart: dragStart }">
<div class="qa-body">
<div class="radio">
<label>
<input type="radio" data-bind="attr: { name: 'Q' + $parentContext.$index(), value: $index }, checked: $parent.CorrectAnswer" /><span></span>
Tick the correct answer
<span data-bind="text:$parent.CorrectAnswer"></span>
</label>
<a href="#" data-bind="click: $parent.remove.bind($parent)">
<i class="fa fa-times"></i>
Remove this answer
</a>
<div class="form-control" contenteditable="true" data-bind="ckeditor: $data, attr: { id: 'Q' + $parentContext.$index() + 'A' + $index() }"></div>
</div>
</div>
</div>
<!-- /ko -->
<div>CorrectAnswer: <span data-bind="text: CorrectAnswer"></span></div>
You'll notice I put a bound span on the end of the radio button label so I can see what happens to the CorrectAnswer observable when I interact with the UI. This is how I know it's correctly bound to the view model. Clicking a radio button or its label changes the value of CorrectAnswer exactly as intended.
This also allows me to know that CorrectAnswer contains the value I expected.
Let's take a closer look at the binding in case it isn't obvious.
attr: { name: 'Q'+$parentContext.$index(), value: $index }, checked: $parent.CorrectAnswer
All the answers for a given question get the same name Qx and a value provided by the item's list position. When an item is clicked its list position is written to CorrectAnswer. This does happen, as evidence by the new value showing up in the telltale div.
So, what could be preventing the UI from initialising as checked when everything else is fine?
It isn't an initialisation problem, it's a type compatibility problem. The value of a radio input is of type string. The value provided by my view model is of type number. Knockout does a strong comparison and does not recognise a match.
See also Radio buttons Knockoutjs
Related
I am creating a form in VueJS that allows a user to create a quiz using various question types. One of those question types is Multiple Choice. Currently, this is my data structure for the new quiz...
newQuiz: {
title: '',
questions: [
],
}
And this is the structure for Multiple Choice questions...
this.newQuiz.questions.push({
id: count+'mc',
type: 'mc',
question: 'Example',
correct: 0,
answers: [
'First answer',
'Second answer',
'Third answer',
'Fourth answer'
]
});
Currently, I have a v-for running on a div that creates each question, giving the radio buttons their proper labels, name, etc. Each radio button and it's respective label has an #click that runs a function as such...
this.newQuiz.questions[question].correct = answer;
I was curious if I could bind the radio buttons to the correct value, so that no updating function is necessary, or so that the default shows selected properly?
For context, here's the v-for as well.
<div v-for="(question, qIndex) in this.newQuiz.questions" :key="question.id">
<div v-if="question.type='mc'" class="mb-8">
<label class="block">{{ question.question }}</label>
<div v-for="(answer, aIndex) in question.answers" :key="answer">
<input type="radio" :id="question.id+'-'+aIndex" :name="question.id" #click="updateMCAnswer(qIndex, aIndex)">
<label :for="question.id+'-'+aIndex" #click="updateMCAnswer(qIndex, aIndex)">{{ answer }}</label>
</div>
</div>
</div>
If you want to bind the answer directly to the correct field of the corresponding question, try the following code
<div v-for="(question, qIndex) in this.newQuiz.questions" :key="question.id">
<div v-if="question.type='mc'" class="mb-8">
<label class="block">{{ question.question }}</label>
<div v-for="(answer, aIndex) in question.answers" :key="answer">
<input type="radio" :id="question.id+'-'+aIndex" :name="question.id" v-model="question.correct"
:value="answer">
<label :for="question.id+'-'+aIndex" #click="updateMCAnswer(qIndex, aIndex)">{{ answer }}</label>
</div>
</div>
</div>
You can remove the click event and directly bind the value by adding v-model="question.correct" and :value="answer" to the input field.
I have created a tabular page in which i need to filter the table with filters using a left side facets boxes of different category but with multi-selection options and i need to use Angularjs for this requirement.
I need to check/uncheck using the clear filter selection .
Any helping library can help to achieve the same or we need to do some logic around the checkboxes to achieve this.
My checkbox code looks like this:
<div class="col-sm-2" style="padding-top: 10px; padding-bottom: 20px;">
<div class="facetBx sltBx" ng-show="tabFilters.length > 0">
<p class="facetBxTitle"><i class="fa fa-filter"></i> Filter Selection
<a class="clrSlt" ng-click="clearAllFilters();">Clear</a>
</p>
<div class="facetBxChld" id="uRslctn">
<ul>
<li ng-repeat="item in tabFilters">
<div class="crop">
<strong title="{{item}}">{{item}}</strong>
</div>
<i class="fa fa-remove rmvThs" style="font-size: 14px;color:#000;float: right;" ng-click="checkItem(item, item,false);"></i>
</li>
</ul>
</div>
</div>
<div class="facetBx" ng-repeat="item in filters">
<p class="facetBxTitle bomtype">{{item.label}}</p>
<div class="facetBxChld" id="bomFacet">
<ul class="multiselect" style="max-height: 140px;overflow-y: auto;">
<li ng-repeat="(k,v) in item.values">
<input type="checkbox" ng-model='isSelected' ng-click='checkItem(item.name, k, isSelected)'>
<span> {{k}} ({{v}})</span>
</li>
<li ng-show="value.length == 0">
No Data Available.
</li>
</ul>
</div>
</div>
</div>
Below website are the reference of the code which i am trying to build:
www.jabong.com
The UI(HTML) is done but i am facing the trouble in the maintaining the checking and un-checking of the checkboxes which are not clearing off.
I believe i need to code something in the ng-model of checkbox to achieve it but i am not able to be successfull so need help on the same.
Sample Plunkur for the same:
enter link description here
Thanks in advance.
Basically you need to keep track of ng-model of checkboxes with some property in your $scope. I did this by modifying your $scope.filters and adding selected property inside it, like below.
var filters = [{
label: 'Brand',
name: 'brand',
values: {
Blackberrys: 503,
Arrow: 175,
ParkAvenue: 358
}
}, {
label: 'Color',
name: 'color',
values: {
Black: 100,
Green: 200,
Red: 300
}
}]
function loadFilters() {
$scope.filters = filters.map(function(filter) {
var filter = angular.copy(filter);
for (var key in filter.values) {
filter.values[key] = {
selected: false,
count: filter.values[key]
}
}
return filter;
})
}
loadFilters();
Then you can call loadFilters() any time you want to clear all filters. Please see POC attached below.
https://plnkr.co/edit/SADPoUpftnJkg1rMuSB9?p=preview
You should try 'ng-change' on checkboxes to trigger a method which changes the values according to checked and unchecked checkboxes.
For Ex.
<input type="checkbox" ng-model='isSelected' ng-click='checkItem(key, k, isSelected)' ng-change="yourMethodHere()">
in JS:
$scope.yourMethodHere = function() {
if (isSelected) {
// filter the values here
} else {
// non filtered values
}
}
By doing this you do not need to even maintain the values of exiting checked/unchecked checkbox.
I added an option to the dropdown that allows user to add item if it doesn't exist.
For that matter, I added an input field to the dropdown but when the user enters something, the dropdown tries to match the entered text with items that are already in the list.
I find it quite annoying in that specific case. I have noticed in the docs that input elements are bound to the search function. Nevertheless, I couldn't find how to disable this behaviour.
Here's the HTML:
<div class="ui fluid selection dropdown playlist">
<input name="playlist" type="hidden">
<i class="dropdown icon"></i>
<div class="default text">playlist</div>
<div class="menu">
<div class="item create" data-value="0">
<span class="create-placeholder">+ new playlist</span>
<div class="ui action input add-playlist">
<input placeholder="new playlist">
<button class="ui button">Add</button>
</div>
</div>
<div class="item" data-value="1">foo</div>
<div class="item" data-value="2">bar</div>
<div class="item" data-value="3">baz</div>
</div>
</div>
The .add-playlist div and its content are not shown but I'm willing to spare you with the CSS here.
And the js:
$dropdown = $('.ui.dropdown');
$dropdown.dropdown({
action: (text, val) => {
if (val == 0) { // eslint-disable-line
$('.create-placeholder').hide(100);
$('.add-playlist').css('display', 'inline-flex');
$('.add-playlist input, .add-playlist button').show(200);
}
else $dropdown.dropdown('set selected', val).dropdown('hide');
},
onHide: () => {
// do that after dropdown has been hidden
setTimeout(() => {
$('.add-playlist, .add-playlist input, .add-playlist button').hide();
$('.create-placeholder').show();
}, 400);
}
});
I've set up a fiddle to have a clear exemple. Just type "foo" and you'll see what I mean in case it's not crystal clear.
To allow user to add new items just add allowAdditions: True To dropdown options, for more informtions see semantic-ui dropdown settings
Example:
$('dropdownSelector').dropdown({
allowAdditions: True
}).dropdown();
HTML code
<div class="check-box-panel">
<!--
ngRepeat: employee in employees
-->
<div class="ng-scope" ng-repeat="employee in employees">
<div class="action-checkbox ng-binding">
<input id="John" type="checkbox" ng-click="toggleSelection(employee.name)"
ng-checked="selection.indexOf(employee.name) > -1" value="John"></input>
<label for="John">
::before
</label>
<!--
John
end ngRepeat: employee in employees
-->
<div class="ng-scope" ng-repeat="employee in employees">
<div class="action-checkbox ng-binding">
<input id="Jessie" type="checkbox" ng-click="toggleSelection(employee.name)"
ng-checked="selection.indexOf(employee.name) > -1" value="Jessie"></input>
<label for="Jessie"></label>
I tried using jQuery
element(by.repeater('employee in employees')).element(by.id('Jessie')).click();
also,I tried using css
element(by.repeater('employee in employees')).$('[value="Jessie"]').click();
But it didn't do the job. Any other way I can click on the particular Checkbox?
Alright I had a very similar issue where I couldn't click the checkbox. It turned out I had to click the checkbox label. However if you want to do any checks on if the checkbox is selected then you have to check the actual checkbox. I ended up making two variables, one for the label and one for the actual checkbox.
JessieChkbxLabel = element(by.css("label[for='Jessie']"));
JohnChkbxLabel = element(by.css("label[for='John']"));
//Click the Jessie checkbox
JessieChkbxLabel.click();
//Click the John checkbox
JohnChkbxLabel.click();
You should just need to use element(by.id('Jessie')) to grab your object. The issue you might be having is that click() returns a promise, so you need to handle that with a .then. So something along the lines of:
element(by.id('Jessie')).click().then(function(value) {
// fulfillment
}, function(reason) {
// rejection
});
Aside from checking the id directly, you can also filter the desired element checking the employee name:
var employee = element.all(by.repeater('employee in employees')).filter(function (elm) {
return elm.evaluate("employee.name").then(function (name) {
return name === "Jessie";
});
}).first();
employee.element(by.css("input[type=checkbox]")).click();
I am using the teleriks treeview control (asp.net mvc extensions), where I may have up to three children nodes, like so (drumroll...... awesome diagram below):
it has its own formatting, looking a bit like this:
<%=
Html.Telerik().TreeView()
.Name("TreeView")
.BindTo(Model, mappings =>
{
mappings.For<Node1>(binding => binding
.ItemDataBound((item, Node1) =>
{
item.Text = Node1.Property1;
item.Value = Node1.ID.ToString();
})
.Children(Node1 => Node1.AssocProperty));
mappings.For<Node2>(binding => binding
.ItemDataBound((item, Node2) =>
{
item.Text = Node2.Property1;
item.Value = Node2.ID.ToString();
})
.Children(Node2 => Node2.AssocProperty));
mappings.For<Node3>(binding => binding
.ItemDataBound((item, Node3) =>
{
item.Text = Node3.Property1;
item.Value = Node3.ID.ToString();
}));
})
%>
which causes it to render like this. I find it unsual that when I set the value it is rendered in a hidden input ? But anyway:...
<li class="t-item">
<div class="t-mid">
<span class="t-icon t-plus"></span>
<span class="t-in">Node 1</span>
<input class="t-input" name="itemValue" type="hidden" value="6" /></div>
<ul class="t-group" style="display:none">
<li class="t-item t-last">
<div class="t-top t-bot">
<span class="t-icon t-plus"></span>
<span class="t-in">Node 1.1</span>
<input class="t-input" name="itemValue" type="hidden" value="207" />
</div>
<ul class="t-group" style="display:none">
<li class="t-item">
<div class="t-top">
<span class="t-in">Node 1.1.1</span>
<input class="t-input" name="itemValue" type="hidden" value="1452" />
</div>
</li>
<li class="t-item t-last">
<div class="t-bot">
<span class="t-in">Node 1.1.2</span>
<input class="t-input" name="itemValue" type="hidden" value="1453" />
</div>
</li>
</ul>
</li>
</ul>
What I am doing is updating a div after the user clicks on a certain node. But when the user clicks on a node, I want to send the ID not the Node text property. Which means I have to get it out of the value in these type lines <input class="t-input" name="itemValue" type="hidden" value="1453" />, but it can be nested differently each time, so the existing code I ahve doesn't ALWAYS work:
<script type="text/javascript">
function TreeView_onSelect(e) {
//`this` is the DOM element of the treeview
var treeview = $(this).data('tTreeView');
var nodeElement = e.item;
var id = e.item.children[0].children[2].value;
...
</script>
So based on that, what is a better way to get the appropriate id each time with javascript/jquery?
edit:
Sorry to clarify a few things
1) Yes, I am handling clicks to the lis of the tree & want to find the value of the nested hidden input field. As you can see, from the telerik code, setting item.Value = Node2.ID.ToString(); caused it to render in a hidden input field.
I am responding to clicks anywhere in the tree, therefore I cannot use my existing code, which relied on a set relationship (it would work for first nodes (Node 1) not for anything nested below)
What I want is, whenever there is something like this, representing a node, which is then clicked:
<li class="t-item t-last">
<div class="t-bot">
<span class="t-in">Node 1.1.2</span>
<input class="t-input" name="itemValue" type="hidden" value="1453" />
</div>
</li>
I want the ID value out of the input, in this case 1453.
Hope this now makes a lot more sense.
if possible would love to extend this to also store in a variable how nested the element that is clicked is, i.e. if Node 1.1.2 is clicked return 2, Node 1.1 return 1 and node 1 returns 0
It's a little unclear what you're asking, but based on your snippet of JavaScript, I'm guessing that you're handling clicks to the lis of the tree & want to find the value of the nested hidden field? If so, you want something like this:
function TreeView_onSelect(e) {
var id = $(e.item).find(".t-input:first").val();
}
Edit: In answer to your follow-up question, you should be able to get the tree depth with the following:
var depth = $(e.item).parents(".t-item").length;
In jQuery you can return any form element value using .val();
$(this).val(); // would return value of the 'this' element.
I'm not sure why you are using the same hidden input field name "itemValue", but if you can give a little more clarity about what you are asking I'm sure it's not too difficult.
$('.t-input').live('change',function(){
var ID_in_question=$(this).val();
});