How to apply an ng-class when data is added dynamically (?) - javascript

I have an application running with Ionic/Angular, all I need is to apply a class which is from the animate.css library, I have here this function which is the one working once you call $scope.addLineToBetSlip() and I want that class that I mentioned working once you call that function:
$scope.addLineToBetSlip = function(line, row, type) {
$scope.picksCount = !$scope.picksCount;
var spreadSelected = (row.spreadSelected && type === 'spread'),
totalSelected = (row.totalSelected && type === 'total'),
moneyLineSelected = (row.moneyLineSelected && type === 'moneyline');
if (spreadSelected || totalSelected || moneyLineSelected) {
BetSlipFactory.remove(line, row, type);
}else {
BetSlipFactory.add(line, row, type);
}
return $scope.picksCount;
};
here is my wrong HTML:
UPDATE
just change my code, is working now but only the first time that {{betSlipCount}} chenges
<span class="badge badge-assertive animate infinite"
ng-class="{bounceIn: picksCount}">{{betSlipCount}}</span>
<i class="icon ion-code-download"></i>
the other way I see, is that {{betSlipCount}} is constantly changing, actually {{betSlipCount}} changes every time you call $scope.addLineToBetSlip(), so the other way is activating that class every single time that {{betSlipCount}} changes.

It's been a while since I've used ng-class but I'm pretty sure the syntax is:
ng-class="{'className': booleanVariable}"
(meaning you've got the class name and variable backwards)
(also you may want to try enclosing the class name in single quotes, though I'm not sure if that's necessary)
The boolean variable can be a function that returns a boolean variable ie:
ng-class="{'fadeIn': addLineToBetSlip()}"
But it doesn't appear that your function returns a boolean variable. You could have it toggle a boolean variable in $scope, and use that variable name instead of the function, or you could have the function return true.
But I'm also not sure why you wouldn't just always want the 'fadeIn' class active.
Maybe you could tell us more about what your code is supposed to do and what it is currently doing.
UPDATE
Controller Code:
//Intialize the boolean variable
$scope.picksCount = false;
$scope.addLineToBetSlip = function(line, row, type) {
var spreadSelected = (row.spreadSelected && type === 'spread');
var totalSelected = (row.totalSelected && type === 'total');
var moneyLineSelected = (row.moneyLineSelected && type === 'moneyline');
if (spreadSelected || totalSelected || moneyLineSelected)
{
BetSlipFactory.remove(line, row, type);
}
else
{
BetSlipFactory.add(line, row, type);
}
$scope.picksCount = !$scope.picksCount;
};
HTML Code:
<span class="badge badge-assertive animate infinite" ng-class="{'bounceIn': picksCount}">{{betSlipCount}}</span>
<i class="icon ion-code-download"></i>

Related

Why is my break statement only working on the first "if"?

I'm trying to make a prompt based todo list
At the moment I'm trying to add the part that will prompt the user to add to the array, which works, but then when I try to quit it just keeps asking.
Quit works on the first question - "What would you like to do?" but once I've said "new" then typed an item it won't let me quit.
// TO DO
let toDo = [];
let options = prompt("What would you like to do?")
while (options !== "quit") {
if (options === "quit") {
break;
}
if (options === "new") {
let options = prompt("add to do")
toDo.push(options);
}
}
console.log( toDo )
The break here is completely pointless. if (options === "quit") { will never pass because while (options !== "quit") { will have failed by that time.
Your problem is caused because you have two variables named options.
The first one (on line 4) is used to test if the loop should exit or not.
The second one (inside if (options === "new") {) is used to push something onto the toDo array and shadows the first one.
Don't redeclare options. Remove let from the second one and just assign a new value to the existing variable.
Please use this code, what you do wrong,
you declare options again
if (options === "new") if bad, it make an infinite loop when the value is not 'new' (or 'quit')
the while test is sufficient
// TO DO
let toDo = []
let options = prompt("What would you like to do?")
while (options !== "quit")
{
toDo.push(options)
options = prompt("add to do")
}
console.log( toDo )
I think you can simplify your logic, using just one prompt; when users click on OK the input will be added to the to-do list and when they click on Cancel you will stop prompting the user for input.
Furthermore, as you have to prompt the user at least once, it would make sense to use a do { ... } while () block.
const todoList = [];
let todoItem;
do {
todoItem = window.prompt('What do you want to add to your list?\n(click on "OK" to add an item or on "Cancel" to quit)');
if (todoItem !== null && todoItem !== '') todoList.push(todoItem)
} while (todoItem !== null)
// test
console.log(todoList);

JavaScript API Response - Check if variable exists

In an API response, I want to check if a variable exists. If it doesn't, I want to assign it a blank value:
if(!data3.fields[i+2].values.value[0]) {
data3.fields[i+2].values.value[0] = "";
} else {
break;
}
Error in the console is:
Uncaught TypeError: Cannot read property 'value' of undefined
This confuses me because I thought that's exactly what my if the statement was checking. Any ideas what's going on here?
The if check won't protect you from trying to use an undefined variable. In your instance the values property is undefined. If you wanted to test for that you would need to first check that specific property
if(data3.fields[i+2].values !== undefined && data3.fields[i+2].values.value[0]){
//do something with data3.fields[i+2].values.value[0]
}
additionally, if you are in a scenario where you don't even know if data3 exists (for example you are checking for the existence of a third party script, or something else in your environment) you would need to use the typeof operator to be safe. E.G.
if(typeof(ga) !== 'undefined'){ //typeof returns a string. This would be testing for google analytics on a page.
It doesnt work like PHP does (which checks the whole 'chain'). In your example, you actually check if .value[0] of values exists, but dont check if values exists. The full version should be:
if( data3 && && data3.fields[i+2] && data3.fields[i+2].values && !data3.fields[i+2].values.value[0]) {}
In your code ata3.fields[i+2].values is undefined, and you're trying to access value[0] of 'undefined'
Or slightly more simplefied, if you wand to test if d has a value, you have to make sure that a, b and c aldo have a value:
if( a && a.b && a.b.c && !a.b.c.d){ /* ... */ }
You can remove checks on the left side of the checks if you are sure those exist. E.g.: If you know that a.b always exist, you can simplefy:
if( a.b.c && !a.b.c.d){ /* ... */ }
If you really want to make sure the complete property chain is not undefined you have to check every single step and the later ones won't be executed if at least && condition is false.
if (data3 && data3.fields && data3.fields[i+2] && data3.fields[i+2].values && data3.fields[i+2].values.value && data3.fields[i + 2].values.value[0]) {
data3.fields[i + 2].values.value[0] = "";
} else {
break;
}
Another way would be to just do it and catch the exception:
try {
data3.fields[i + 2].values.value[0] = "";
} catch (e) {
break;
}
The error is telling you that data3.fields[i+2].values is undefined. You can't check for a property .value on undefined.
You'd need to verify each property/index belongs along the way if you always want that nested path to default to an empty string.
if (data3.fields[i+2] === undefined) {
data.fields[i+2] = {};
}
if (data3.fields[i+2].values === undefined) {
data3.fields[i+2].values = {};
}
if (data3.fields[i+2].values.value === undefined) {
data3.fields[i+2].values.value = [];
}
// and finally your empty string assignment
if (data3.fields[i+2].values.value[0] === undefined) {
data3.fields[i+2].values.value[0] = '';
}
Depending on your requirements, you might be able to get away with assigning a stub as soon as you know data3.fields[i+2] is undefined.
if (data3.fields[i+2] === undefined) {
data3.fields[i+2] = {
values: {
value: ['']
}
};
}

Knockout JS textInput after event

I am having trouble using the textInput binding with a custom autocomplete/dropdown function. I am using the textInput binding so I do see the view model get updated, but the update takes place after the keypress event which is what my autocomplete is bound too. Here is my autocomplete function:
$("#table-body").on("keypress", ".combo", function (e) {
var item = ko.dataFor(e.target),
drop = $(".dropdown-menu", this);
if (item.Name() !== undefined && item.Name().length === 0) {
$(".input-group-btn", this).removeClass("open");
} else if (item.Name() !== undefined && item.Name() !== null && item.Name() !== "") {
drop.children().not(":containsNoCase(" + item.Name() + ")").hide();
drop.children().filter(":containsNoCase(" + item.Name() + ")").show();
$(".input-group-btn", this).addClass("open");
}
});
In the event above, item.Name() is null or whitespace when the first character is entered, and then is always 1 character behind what is typed. Any ideas on how I can change the event capture to be after the textInput has updated the view model?
It's interesting that the update of the model value takes so long and gets set so late in the game. I looked at the textInput binding source and found that knockout defers the setting of the value by 4ms when the input is changed. Might explain why the value is not updated at the time the handler is run?
var deferUpdateModel = function (event) {
if (!timeoutHandle) {
// The elementValueBeforeEvent variable is set *only* during the brief gap between an
// event firing and the updateModel function running. This allows us to ignore model
// updates that are from the previous state of the element, usually due to techniques
// such as rateLimit. Such updates, if not ignored, can cause keystrokes to be lost.
elementValueBeforeEvent = element.value;
var handler = DEBUG ? updateModel.bind(element, {type: event.type}) : updateModel;
timeoutHandle = setTimeout(handler, 4);
}
};
Instead of manually binding to the keypress event, I would consider using a knockout subscription on your Name observable. By doing do, you can be assured that you have the latest value and won't have to worry about the 1-behind problem. Whipped up a quick fiddle to demonstrate.
vm.name.subscribe(function(val){
var name = val,
drop = $(".dropdown-menu");
if (name !== undefined && name.length === 0) {
$(".input-group-btn").removeClass("open");
} else if (name !== undefined && name !== null && name !== "") {
drop.children().not(":contains(" + name + ")").hide();
drop.children().filter(":contains(" + name + ")").show();
$(".input-group-btn").addClass("open");
}
});
http://jsfiddle.net/ttz0p33n/

Knockout: Change css class based on value of observable

I use foreach on an observable array:
<div id="mainRight" data-bind="foreach: notifications">
<div class="statusRow">
<div class="leftStatusCell">
<div class="leftStatusCellColor" data-bind="css: availabilityCssClass($data.availability)"></div>
</div>
<div class="topRightStatusCell" data-bind="text: sip"></div>
<div class="bottomtRightStatusCell ellipsisSingleline" data-bind="text: note"></div>
</div>
</div> <!== end mainRight ==>
As you can see, I pass the current value of availability to the function availabilityCssClass, which compares the value to some predefined strings. Depending on the matching string, it returns a class name.
self.availabilityCssClass = ko.computed(function (value) {
var availability = value;
if (availability === "Busy" || "DoNotDisturb" || "BeRightBack")
return "leftStatusCellColorOrange";
else if (availability === "Away" || "Offline")
return "leftStatusCellColorRed";
else
return "leftStatusCellColorGreen";
});
This is my model. The data comes from an external data source.
function Notification(root, sip, availability, note) {
var self = this;
self.sip = ko.observable(sip);
self.availability = ko.observable(availability);
self.note = ko.observable(note);
};
self.notifications = ko.observableArray();
However, it doesnt work as is. When the computed function is not commented out, the foreach does not iterate over the data and the div is empty. But I can see that the viewModel is not empty.
You cannot pass value into computed in such way. It is better to add this computed to Notification view model and use self.availability property:
function Notification(root, sip, availability, note) {
var self = this;
self.sip = ko.observable(sip);
self.availability = ko.observable(availability);
self.note = ko.observable(note);
self.availabilityCssClass = ko.computed(function() {
var availability = self.availability();
if (["Busy", "DoNotDisturb", "BeRightBack"].indexOf(availability) != -1) return "leftStatusCellColorOrange";
else if (["Away", "Offline"].indexOf(availability) != -1) return "leftStatusCellColorRed";
else return "leftStatusCellColorGreen";
});
};
Your if statement wasn't correct, so I fixed the logic. Here is working fiddle: http://jsfiddle.net/vyshniakov/Jk7Fd/
You just need to make availabilityCssClass a function. As you've written it, it's not a computed observable since it has no observable dependencies.
self.availabilityCssClass = function (value) {
var availability = value;
if (availability === "Busy" || "DoNotDisturb" || "BeRightBack")
return "leftStatusCellColorOrange";
else if (availability === "Away" || "Offline")
return "leftStatusCellColorRed";
else
return "leftStatusCellColorGreen";
};
The CSS binding wants a object literal with the name of the CSS class as member name and the value true or false depeding on you want to remove or add the class
data-bind="css: { 'css-class-name': true }"
edit: Hmm, they have changed the css binding in 2.2 ;)

Javascript - Passing function as string, then execute if it exists

I have a javascript library which does returns a list of results to a div triggered on keyup events. I want to use Jquery to apply a standard keyup event to all fields on all pages which have a certain class.
That part I can do and it works OK.
My issue is that the parameters which I use are dynamic and the last 2 of these are optional functions.
<input type="text"
class="font8_input" name="wsFromPart"
value="<%=wsFromPart%>" id="wsFromPart"
size="20" maxlength="20"
onfocus="javascript:fncAjaxClear()"
onkeyup="javascript:fncAjaxSearch('wsDatabase','..\\AjaxBrowses\\PartBrowse.asp','wsFromPart','wsFromPartList','fncPrePartAjax',null);"/>
At present, to control this I pass null if I don't have a function.
I'm trying to get the to a position where I can define all of the fields like this.
<input type="text" class="PartClass" name="wsFromPart" value="<%=wsFromPart%>" id="wsFromPart" />
Everything else will be added by setting classes/events by Jquery.
I'm trying to work out how I can test if a function exists on my page, and only execute if it does. I've tried passing the function name as a string but can't seem to make that work. My last attempt is to have a generic function, and pass the name of the function which may exist to this function to evaluate and, if a function, execute it.
<input type="text" class="font8_input" name="wsFrPt" id="wsFrPt" size="20" maxlength="20" value="<%=wsFrPt%>" onfocus="javascript:fncAjaxClear()" onkeyup="javascript:fncAjaxSearch('wsDatabase','..\\AjaxBrowses\\PartBrowse.asp','wsFrPt','wsFrPtList',fncCheckFunction('fncPreAjaxPart1'),'fncPostAjaxPart');"/>
function fncAjaxSearch(wsDb,wsAsp,wsId,wsTarget,wsPreFunction,wsReturnFunction) {
var myDate = new Date();
var myDate1 = myDate.getTime();
if (objXHR.readyState == 4 || objXHR.readyState == 0) {
var wsDatabase = escape(document.getElementById(wsDb).value);
var wsStr = escape(document.getElementById(wsId).value);
var wsParam = "";
if (wsPreFunction !== null) {
wsParam = wsPreFunction();
}
//Only do ajax call if the 'source' field is not empty, otherwise empty target and clear border.
if (document.getElementById(wsId).value > '') {
objXHR.open("GET", wsAsp + '?qryDatabase=' + wsDatabase + '&qryDummy=' + myDate1 + '&qrySearch=' + wsStr + wsParam, true);
objXHR.onreadystatechange = function(){if(objXHR.readyState==4){fncAjaxSearchReturn(objXHR,wsId,wsTarget,wsReturnFunction)}};
objXHR.send(null);
}
else {
document.getElementById(wsTarget).innerHTML = '';
document.getElementById(wsTarget).style.borderWidth = '0px';
}
}
}
If it is a global, you can do this
var fnc = window["yourFunctionName"]; //Use bracket notation to get a reference
if( fnc && typeof fnc === "function" ) { //make sure it exists and it is a function
fnc(); //execute it
}
If it is namespaced, you can do the same type of thing, just involves some looping.
var myFnc = "foo.bar.fun";
var nameParts = myFnc.split("."); //split up the string into the different levels
var fnc = window; //set it to window
for(var i=0;i<nameParts.length;i++){ //loop through each level of the namespace
fnc = fnc[nameParts[i]];
if(!fnc){ //make sure it exists, if not exit loop
fnc = null;
break;
}
}
if( fnc && typeof fnc === "function" ) { //make sure it exists and it is a function
fnc(); //execute it
}
You can check if function exists by:
if(typeof(yourFunctionName) == "function")
//...do your code
else
//...function not exists
if you passing the function name as a string, then change typeof(yourFunctionName) to typeof(eval(yourFunctionName))

Categories

Resources