KnockoutJS calling computed for each element in foreach - javascript

I have a problem i cannot figure out..
My example on JSbin: http://jsbin.com/wiwuwepe/1/edit
Basicly, in
<script id="QuestionTemplate" type="text/html">
<div class="well">
<div class="form-group">
<div class="col-md-5">
<p class="form-control-static" data-bind="text: QuestionText"></p>
</div>
</div>
<div class="form-group">
<div class="col-md-5">
<!-- THIS IS WHERE I WANT COMPUTED FOR EACH SURVEYQUESTION -->
<p class="form-control-static" data-bind="text: QuestionTypeTemplate"></p>
</div>
</div>
</div>
QuestionTypeTemplate shows undefinded, although model for this is
function SurveyQuestion(data) {
var self = this;
self.QuestionText = ko.observable(data.QuestionText);
self.QuestionType = ko.observable(data.QuestionType);
self.QuestionAnswers = ko.observableArray(data.QuestionAnswers);
self.QuestionTypeTemplate = ko.computed(function () {
/*
if( self.QuestionType() == 0) {
return "radio";
} else if (self.QuestionType() == 1) {
return "checkbox";
}
*/
return "This is what i want";
}, self);
}
Please, check full code on jsbin. Just uncomment that 1 line in QuestionTemplate script/html.
When I compare my example with http://knockoutjs.com/examples/cartEditor.html , I really cant find big difference, why that works, and why mine does not.

on your Survey function
instead of this
self.SurveyQuestions = ko.observableArray(data.SurveyQuestions);
you need to use this
var questions = [];
for(i = 0 ; i<data.SurveyQuestions.length ; i++) {
questions.push(new SurveyQuestion(data.SurveyQuestions[i]));
}
self.SurveyQuestions = ko.observableArray(questions);

Related

Getting values on particular index in javascript

I have a button and onclick of that button, i am running a AJAX call and getting values from another jsp page. My rows are indexed.
<%
List<DiskSheet> ds1 = new ArrayList<DiskSheet>();
if (request.getParameter("numLLP") != null && !request.getParameter("numLLP").isEmpty()) {
int numLLP = Integer.valueOf(request.getParameter("numLLP"));
for (int i=0;i<numLLP;i++) {
DiskSheet d = new DiskSheet();
d.setCH5Limiter(request.getParameter("limiter_"+i));
d.setMfrPrice(request.getParameter("diskvalues_"+i));
d.setDiskCyc(request.getParameter("diskcyc"));
ds1.add(d);
}
request.getSession().setAttribute("engine" + request.getParameter("diskid"), ds1);
}
<%
List<DiskSheet> ds = (List<DiskSheet>) request.getSession().getAttribute("engine" + request.getParameter("diskid"));
if (ds == null) {
ds = new ArrayList<DiskSheet>();
}
String disksheet = request.getParameter("disksheet");
if (disksheet != "") {
String engine = request.getParameter("Engines");
if (ds.size() == 0) {
ds = DiskSheet.getLLPEngine(engine);
}
%>
<div><input type="text" style="text-align:right;" name="limiter_<%=i%>" id="limiter" class="limiter" value="<%=airs.getCH5Limiter()%>" size="10" onblur="getDiskSheetCyc()"></div>
<div><input type="hidden" class="diskvalues" id="diskvalues" name="diskvalues_<%=i%>" size="10" value="<%=airs.getMfrPrice()%>" onblur="getDiskSheetCyc()"></div>
<div><input type="text" class="diskcyc" id="diskcyc" name="diskcyc" size="10" value="<%=airs.getDiskCyc()%>" onblur="getDiskSheetCyc()"></div>
I am trying to perform a simple calculation and print the values in the third row however, it only displays the value in one of the cells. Here's what i tried.
function showPopup(diskid){
document.getElementById("popup_win").style.display = 'block';
}
function getDiskSheet(diskid) {
var form = document.getElementById("airplaneForm");
var id = diskid;
var myAjax = new Ajax.Updater("ch5limiteroutput",
"/jsp/Ch5Limiter.jsp", {
asynchronous : true,
postBody : Form.serialize(form),
data: id,
method : 'post',
onComplete : function() {
displayLimiter();
getDiskSheetCyc();
document.getElementById("id").innerHTML = id;
}
});
}
function displayLimiter() {
var form = document.getElementById("airplaneForm");
var limiteroutput = document.getElementById("ch5limiteroutput").innerHTML;
document.getElementById("limiter").innerHTML = limiteroutput;
}
function getDiskSheetCyc(){
var diskvalues = document.getElementsByClassName("diskvalues");
var limiter = document.getElementsByClassName("limiter");
for (var i = 0; i < diskvalues.length; i++) {
var diskval = parseInt(diskvalues[i].value);
var limiter = parseInt(limiter[i].value);
diskcyc = diskval/limiter;
}
document.getElementById('diskcyc').value = diskcyc;
}
<td class="trigger_popup" ><input type="button" id="1" value="Disk Sheet" name="disksheet" class="disksheet" onclick="showPopup(this.id);getDiskSheet(this.id);">
</td>
<div class="popup_win" id="popup_win">
<span class="helper"></span>
<div>
<br>
<div id="TableBox" class="TableBox" style="width: 110%;">
<div>
<div><span class="id" id="id"></span></div>
<div><span class="limiter" id="limiter"></span></div>
</div>
</div>
</div>
</div>
<div id="ch5limiteroutput" style="display: none"></div>
Also tried doing it through jQuery but it doesn't seem to go inside the loop. I am not sure what i am doing wrong here. Any help is greatly appreciated. Thank you.
function getDiskSheetCyc(){
const jQuerytable = jQuery('#TableBox');
const jQueryrow = jQuerytable.find('> div');
jQuery(".jQueryrow").each(function() {
const jQuerythis = jQuery(this);
const diskvalues = parseInt(jQuerythis.find('.diskvalues').val());
const limiter = parseInt(jQuerythis.find('.limiter').val());
const diskcyc = diskvalues/limiter;
if (!isNaN(diskcyc)) jQuerythis.find('.diskcyc').val(diskcyc);
});
}
Your code at present does not make a lot of sense. For a jQuery solution, consider the following code example.
$(function() {
function showPopup() {
$("#popup_win").show();
}
function displayLimiter() {
var limiteroutput = $("#ch5limiteroutput").html();
$("#limiter").html(limiteroutput);
}
function getDiskSheetCyc() {
var diskvalues = $(".diskvalues");
var limiter = $(".limiter");
var diskcyc = 0;
diskvalues.each(function(i, el) {
var diskval = $(el).val() == "" ? parseInt($(el).val()) : 0;
var limiter = $(el).val() == "" ? parseInt($(el).val()) : 0;
if (diskval > limiter) {
diskcyc = diskval / limiter;
}
});
$('#diskcyc').val(diskcyc);
}
function getDiskSheet(diskid) {
var form = $("#airplaneForm");
var id = diskid;
$.post("/jsp/Ch5Limiter.jsp", {
postBody: form.serialize(),
data: id
}, function(results) {
displayLimiter();
getDiskSheetCyc();
$("#id").val(id);
});
}
$("[type='button']").click(function() {
showPopup();
getDiskSheet($(this).attr("id"));
});
});
.popup {
border: 1px solid #ccc;
border-radius: 6px;
width: 340px;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tr>
<td class="trigger_popup">
<input type="button" id="1" value="Disk Sheet" name="disksheet" class="disksheet">
</td>
</tr>
</table>
<div class="popup window" id="popup_win" style="display: none;">
<span class="helper"></span>
<div>
<br>
<div id="TableBox" class="TableBox" style="width: 110%;">
<div>
<div><span class="id" id="id"></span></div>
<div><span class="limiter" id="limiter"></span></div>
</div>
</div>
</div>
</div>
<div id="ch5limiteroutput" style="display: none"></div>
So this encounters issues all over due to a number of missing elements that were not provided. The basic idea is you want to show a popup, post some data, and then update some content. I don't see where you do anything with data returned from the AJAX call... I am guessing it's just updating the DB with no feedback.
It would be helpful to provide an example of the data that is retuened if you need more help there.
In regards to the calculation, you're on the right path. You just need to make sure the proper fields get populated with the right details. I am assuming this is where the data from the POST gets to be used.
Examine your Network tab in development tools. You should see the POST action, the Headers, and the Response. This should help you identify if you're getting an error in your AJAX or maybe getting different data back than expected.

Detect observable change inside observableArray

There's a paragraph in knockout docs that said you can create an observableArray with properties as observables but there isn't an example of that:
http://knockoutjs.com/documentation/observableArrays.html
So what I'm trying to achieve is to add an element to an observableArray that has an observable property to detect state changes when it's clicked. So here is my code what I have so far
export class Team {
Name: KnockoutObservable<string>;
Score: KnockoutObservable<number>;
ListTeamsArray: KnockoutObservableArray<any>;
selectedTeam: KnockoutObservable<boolean>;
constructor(json) {
var self = this;
if (json !== null) {
self.Name = ko.observable(json.Name);
self.Score = ko.observable(0);
self.ListTeamsArray = ko.observableArray();
self.selectedTeam = ko.observable(false);
}
}
addTeam = () => {
var self = this;
//Working correctly and I'm declaring "selectedTeam" as an observable with initial value of "false"
var newTeam = { Name: self.Name(), Score: 0, selectedTeam: ko.observable(false)};
self.ListTeamsArray.push(newTeam);
}
//Here I create a function that is passing a "team" parameter (the one in the array and it's working fine
teamSelectedFn = (team: Team, event) => {
var self = this;
$(".teamPanel").css("background", "none");
//Loop thru array in knockout to assign selected value, probably there's a better way
ko.utils.arrayForEach(self.ListTeamsArray(), (item) => {
if (item.Name === team.Name) {
$(event.currentTarget).css("background", "#a4e4ce");
item.selectedTeam = ko.observable(true);
} else {
item.selectedTeam = ko.observable(false);
}
});
//just for testing
ko.utils.arrayForEach(self.ListTeamsArray(), (item) => {
console.log(item);
console.log(item.selectedTeam());
});
}
}
And this is the HTML
<div class="row" id="teamCrud">
<div class="col-sm-3" >
<div class="form-group">
<input class="form-control" data-bind="value: Name" />
#*<span data-bind="text: Score"></span>*#
<button data-bind="click: addTeam" class="btn btn-success">Add</button>
</div>
</div>
<div class="col-sm-9">
Equipos
<div data-bind="foreach: ListTeamsArray" class="row">
<div class="col-sm-3">
<div class="panel panel-default teamPanel" data-bind="click: $parent.teamSelectedFn, style: { border: selectedTeam() ? '2px solid red' : 'none'}#*, style: { background: $data.selectedTeam() ? 'red' : 'none'}*#">
<div class="panel-heading" data-bind="text: Name"></div>
<div class="panel-body">
Score:
<p data-bind="text: Score"></p>
Seleccino
<p data-bind="text: selectedTeam()"></p>
</div>
</div>
</div>
</div>
</div>
</div>
Everything it's working, I know I can change the background color of the HTML element with knockout but I need to detect the dependency change. It's not detecting the changes from the observable inside the array. Is there something else I need to do or I'm handling this the wrong way?
In your click function you are overwriting the bound observable with a new observable. You probably just need to change your function to update the existing observable instead of replacing it.
teamSelectedFn = (team: Team, event) => {
var self = this;
$(".teamPanel").css("background", "none");
//Loop thru array in knockout to assign selected value, probably there's a better way
ko.utils.arrayForEach(self.ListTeamsArray(), (item) => {
if (item.Name === team.Name) {
$(event.currentTarget).css("background", "#a4e4ce");
item.selectedTeam(true);
} else {
item.selectedTeam(false);
}
});

knockout if binding working in laptops, but not working in mobile and tablet

I am trying some thing with if binding from knockout. If value is true I want to show some text and if it is false, I want to show some different text, as given in the code.
When I am opening the page with this html, I am getting the expected results.
But when I am trying to get the result in phones and in kindle tab (working fine in wondows tab), it is not giving the results for the if binding I have used in html.
I tried removing '()' from failStatus and status in html, but it is not working. Is it any issue of binding or I am doing any thing wrong?
Thanks for any help.
function temp()
{
this.inviteeEmailList = ko.observableArray([]);
var emailList = {};
emailList['email'] = {'a#x.y , b#c.n'};
emailList['status'] = ko.observable();
emailList['failStatus'] = ko.observable();
this.showList = function()
{
for(var k in inviteeEmailList)
{
if(some_condition)
{
this.inviteeEmailList()[k]['status'](true);
this.inviteeEmailList()[k]['failStatus']("");
}
else
{
this.inviteeEmailList()[k]['status'](false);
this.inviteeEmailList()[k]['failStatus']("not exist");
}
}
}
}
<div id="foundEmail" data-bind="foreach : inviteeEmailList">
<span data-bind="if: $data.status()">
<span>Success</span>
</span>
<span data-bind="if:(!$data.status() && $data.failStatus()) ">
<span>hello world</span>
</span>
<div data-bind="text:$data.email"></div>
<div data-bind="if:!$data.status()">
<div data-bind="text:$data.failStatus()"></div>
</div><br/>
</div>
Instead of using if binding, I tried using visible binding, which worked properly for me.
Giving code below
function temp()
{
this.inviteeEmailList = ko.observableArray([]);
var emailList = {};
emailList['email'] = {'a#x.y , b#c.n'};
emailList['status'] = ko.observable();
emailList['failStatus'] = ko.observable();
this.showList = function()
{
for(var k in inviteeEmailList)
{
if(some_condition)
{
this.inviteeEmailList()[k]['status'](true);
this.inviteeEmailList()[k]['failStatus']("");
}
else
{
this.inviteeEmailList()[k]['status'](false);
this.inviteeEmailList()[k]['failStatus']("not exist");
}
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="foundEmail" data-bind="foreach : inviteeEmailList">
<span data-bind="visible: $data.status()">
<span>Success</span>
</span>
<span data-bind="visible:(!$data.status() && $data.failStatus()) ">
<span>hello world</span>
</span>
<div data-bind="text:$data.email"></div>
<div data-bind="visible:!$data.status()">
<div data-bind="text:$data.failStatus()"></div>
</div><br/>
</div>

Knockout js: can't write value from form inside array

I have a script that produces an array of forms, with each form affecting the available options for the next form. The awesome martin booth solved the problem of getting the displayed values to update as new forms are added.
however, I have an observable array (defaultSampleRates) that sits outside the forms array, and for the life of me I can't get the form to push items into that array. I've tried declaring it in a dozen different places in a dozen different ways, but it just won't stick.
basically I need the 'Default sample rate' drop-down to show the sample rates that have been selected in the form above (the user must only be able to choose a default sample rate from a displayed one, rather than from the full list).
Any tips much helpo brain pain. fiddle here: http://jsfiddle.net/3lliot/9vsa4hh7/
html:
<body>
<div style="float:left; width:60%">
<div data-bind="foreach: forms">
<div style="float:left; margin-right:20px"> <span>
<!-- This is a *view* - HTML markup that defines the appearance of your UI -->
<p><span style="color:#AB0002">Sample rate element <span data-bind="text: formNum"></span></span>
</p>
<p>Sample rate (Hz):
<select data-bind="options: sampleRates, value: selectedSampleRate"></select>
</p>
</span>
</div>
</div>
<div style="float:left; clear:both; margin-bottom:20px">
<hr/>
<button data-bind="click: addForm">Add <srate> element</button>
<button data-bind="click: removeForm">Remove</button>
<p>Default sample rate:
<select data-bind="options: defaultSampleRates, value: selectedDefaultSampleRate"></select>
</p>
</div>
</div>
<div style="float:right; width:38%; overflow:scroll; border-left:thin; border-left-style:solid; border-left-color:#dfdfdf;padding-left: 1%"> <span class="code"><audio></span>
<ul data-bind="foreach: forms">
<li>
<!-- render the json --> <span class="code"> <srate id="<span data-bind="text: formNum"></span>">
<br/> <sample_rate><span data-bind="text: selectedSampleRate"></span></sample_rate>
<br/> </srate></span>
</li>
</ul> <span class="code"> <default_srate><span data-bind="text: selectedDefaultSampleRate"></span></default_srate></span>
<br/><span class="code"></audio></span>
</div>
</body>
js:
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
//window.onload = startKnockout;
window.onload = startKnockout;
var formNum;
var i = -1;
var selectedSampleRates = [];
function Form(allSampleRates, forms) {
var self = this;
// Declare observables
self.selectedSampleRate = ko.observable();
self.formNum = ko.observable();
self.sampleRates = ko.computed(function () {
var formsValue = forms(),
availableSampleRates = ko.utils.arrayFilter(allSampleRates, function (sampleRate) {
return !ko.utils.arrayFirst(formsValue, function (form) {
if (form != self) {
if (form.selectedSampleRate() === sampleRate) {
if (selectedSampleRates.indexOf(sampleRate) === -1) {
selectedSampleRates.push(sampleRate);
}
}
return form.selectedSampleRate() === sampleRate;
} else {
return form != self;
}
});
});
return availableSampleRates;
});
// count how many srate elements there are
i++;
self.formNum = i;
}
var Vm = function () {
var self = this;
var item = 0,
allSampleRates = ['192000', '176400', '96000', '88200', '48000', '44100'];
// declare observables for options outside the srate elements
self.selectedDefaultSampleRate = ko.observable();
// add remove forms stuff
self.forms = ko.observableArray([]);
self.forms.push(new Form(allSampleRates, self.forms));
item++;
self.addForm = function () {
if (i < 5) {
self.forms.push(new Form(allSampleRates, self.forms));
item++;
} else {
alert("Can't have more than 6 <srate> elements!")
}
};
self.removeForm = function () {
if (item > 1) {
self.forms.splice(item - 1, 1);
item--;
i--;
} else {
alert("Must have at least one <srate> element!")
}
};
// define arrays for options outside srate elements
self.defaultSampleRates = ko.observableArray([]);
return self;
}
// Activates knockout.js
function startKnockout() {
ko.applyBindings(new Vm());
};
You can make use of selectedOptions binding to add defaultSample rate.
I changed select sampleRates code to this
<select data-bind="options: sampleRates, value: selectedSampleRate, selectedOptions: $root.defaultSampleRates"></select>
Notice selectedOptions binding there..
Should work as per your need.
Updated Fiddle Demo here : http://jsfiddle.net/rahulrulez/9vsa4hh7/3/
I hope that's what you wanted.

auto select first ng-repeat element in angularjs?

any idea on how to do this?
i basically have 1 controller set up and if you click on an element, an animation appears above, however, the first element should be active/clicked as soon as the page loads...
some code:
Animation Appears here:
<div class="animation">
<div ng-repeat="theme in themes" ng-show="isActive($index);" ng-if="isActive($index);">
<img id="id_{{ theme.animation_id }}" ng-src="{{ theme.animation_gif_url }}" class="active" />
</div>
</div>
Animation selection here:
<div class="theme-select animated fadeInUp">
<div class="theme-container" ng-repeat="theme in themes" ng-click="showAnimation($index);">
<div id="theme_{{ theme.animation_id }}" class="theme">
<img ng-src="{{ theme.icon_url }}" />
<div class="name">
{{ theme.name }}
</div>
</div>
</div>
</div>
Here's my controller:
themeFunction(function(themeData) {
name = [];
themeID = [];
animationURL = [];
var themes = $scope.themes = themeData.themes;
for (var t = 0; t < themes.length; t++) {
animationURL = themes[t].animation_url;
themeID = themes[t].animation_id;
iconURL = themes[t].icon_url;
name = themes[t].name;
animationGifUrl = themes[t].animation_gif_url;
$scope.themeID = themeID;
if ($scope.themes[t].animation_gif_url == 'NULL' || $scope.themes[t].animation_gif_url == null || $scope.themes[t].animation_gif_url == '.gif') {
// console.log(name + " are null or weird");
$scope.themes[t].animation_gif_url = iconURL;
} else {
$scope.themes[t].animation_gif_url = animationGifUrl;
}
}
$scope._Index = 0;
$scope.isActive = function(index) {
return $scope._Index === index;
};
$scope.showAnimation = function(index) {
selectedTheme = $(this).attr('theme');
console.log('Selected ' + selectedTheme.animation_id);
$scope._Index = index;
};
});
In your controller, after your list loads up, call the showAnimation on the first index
...list loads up
$scope.themes = data;
if (data.length) $scope.showAnimation(0);
Because:
[] == false
So, you can do if simply like this:
if (themes) $scope.showAnimation(0);
If themes is a empty array, not thing will happened.
If themes is a array has at least one theme, will trigger showAnimation function

Categories

Resources