Add class depending on JSON data - javascript

With dynamically generated HTML from JSON data I'm trying to add a class to each .status-card, in this case depending on the value of c.callStatus. This is the closest I got, but this just adds active class to all status-card. I'm guessing it's something to do with how I'm using $(this) or I'm missing something else?
$(function() {
var agents = [];
$.getJSON('js/agents.json', function(a) {
$.each(a.agents, function(b, c) {
var content =
'<div class="status-card">' +
'<div class="agent-details">' +
'<span class="agent-name">' + c.name + '</span>' +
'<span class="handling-state">' + c.callStatus + '</span>' +
'<span class="handling-time">' + c.handlingTime + '</span>' +
'</div>' +
'<div class="status-indicator"></div>' +
'</div>'
$(content).appendTo('#left');
//Add class depending on callStatus
$('.status-card').each(function() {
if (c.callStatus == 'On Call') {
$(this).removeClass('idle away').addClass('active');
} else if (c.callStatus == 'Idle') {
$(this).removeClass('active away').addClass('idle');
} else {
$(this).removeClass('active idle').addClass('away');
}
console.log(c.callStatus);
});
});
});
});
Thanks!

This is the closest I got, but this just adds active class to all status-card.
This is happening because after adding each status-card you are adding other classes to all the status cards added till now:
$.each(a.agents, function(b, c) {
....
// here you are updating the class for all the cards added till now.
$('.status-card').each(function() {
....
});
....
});
So, the active classes is added to all the other cards because that might be the callStatus in the last outer loop.
You can compute the className based on the callStatus before creating the HTML and then use that className in HTML like this:
function getClassNameByStatus (callStatus) {
switch(callStatus){
case "On Call":
return "active";
case "Idle":
return "idle";
default:
return "away";
}
}
$.each(a.agents, function(b, c) {
var className = getClassNameByStatus(c.callStatus);
var content =
'<div class="status-card' + className + '">' +
....; // rest of the HTML
$(content).appendTo('#left');
});

You're calling $('.status-card').each() for each agent in your a.agents list. so in the final iterate all .status-card elements will have the last agent.callStatus evaluated class.
I'd write something like this.
$(function() {
function createStatusCard(name,callStatus,handlingTime) {
var status_class_map = {
"On Call" : "active",
"Idle" : "idle"
};
var $content = $("<div/>").addClass("status-card").addClass(function(){
return status_class_map[callStatus] || "away";
});
$content.html('<div class="agent-details">' +
'<span class="agent-name">' + name + '</span>' +
'<span class="handling-state">' + callStatus + '</span>' +
'<span class="handling-time">' + handlingTime + '</span>' +
'</div>' +
'<div class="status-indicator"></div>');
return $content;
}
$.getJSON('agents.json', function(a) {
$.each(a.agents, function(b, c) {
$("#left").append(createStatusCard(c.name,c.callStatus,c.handlingTime));
});
});
});
it's more readable and easier to debug.

This is because you have your status-card each loop nested inside your JSON loop. Therefore every time you loop through an object from the JSON data you are setting all of the status-card element's class which have already been added to the DOM.
You could restructure to set the class as you build the elements before appending.
$.each(a.agents, function(b, c) {
var content =
'<div class="agent-details">' +
'<span class="agent-name">' + c.name + '</span>' +
'<span class="handling-state">' + c.callStatus + '</span>' +
'<span class="handling-time">' + c.handlingTime + '</span>' +
'</div>' +
'<div class="status-indicator"></div>';
var $statusCard = $("<div/>").addClass("status-card");
.append(content);
$statusCard.addClass(function(){
switch(c.callStatus){
case "On Call":
return "active";
case "Idle":
return "idle";
default:
return "away";
}
}());
$statusCard.appendTo('#left');
});

Related

Multiple of the similar "Select Option" ID , Dynamic Javascript Code

1) I need to find a way to have multiple of id="select-repo" because every single "add another item" is the same. "Add Another Item" is suppose to add another row of html with that id="select-repo" for now it's just a empty textbox.
Generate Functions on the go? Dynamic Functions? I can easily loop the Select box but not the "$('#select-repo').selectize({" function i believe.
2) After number 1 is solved, I need to find a way to know which row of data to update after an option has been selected.
3) Is this easier to get done with VUE.JS, since i'm using laravel , integration should be easier with Vue.JS
What is your advice, I was told to use stuff like ReactJS / styled components? is there anyway to not switch framework to just get this done?
Please Advice.
HTML Code
<td><select id="select-repo" class="repositories"></select></td>
JS Code
<script>
//<select id="select-repo"></select>
$('#select-repo').selectize({
valueField: 'url',
labelField: 'name',
searchField: 'name',
options: [],
create: false,
render: {
option: function(item, escape) {
return '<div>' +
'<span class="title">' +
'<span class="name"><i class="icon ' + (item.fork ? 'fork' : 'source') + '"></i>' + escape(item.name) + '</span>' +
'<span class="by">' + escape(item.username) + '</span>' +
'</span>' +
'<span class="description">' + escape(item.description) + '</span>' +
'<ul class="meta">' +
(item.language ? '<li class="language">' + escape(item.language) + '</li>' : '') +
'<li class="watchers"><span>' + escape(item.watchers) + '</span> watchers</li>' +
'<li class="forks"><span>' + escape(item.forks) + '</span> forks</li>' +
'</ul>' +
'</div>';
}
},
score: function(search) {
var score = this.getScoreFunction(search);
return function(item) {
return score(item) * (1 + Math.min(item.watchers / 100, 1));
};
},
load: function(query, callback) {
if (!query.length) return callback();
$.ajax({
url: 'https://api.github.com/legacy/repos/search/' + encodeURIComponent(query),
type: 'GET',
error: function() {
callback();
},
success: function(res) {
callback(res.repositories.slice(0, 10));
}
});
},
onChange: function(value) {
alert(value);
}
});
</script>
Since id can not be the same, you can define a global variable like index to memo the count as part of id;
Like to dynamically add select with id "select-repo"+index; e.g. select-repo1, select-repo999
Here's an example:
var index = 1;
function addSelect(){
$('#somewhere').append('<select id="select-repo'+index+'">');
$('select-rep'+index).selectize(){
....
};
index++;
}
And you can easily get the select index by parse Id string.

Function mouseup in forEach at each loop

I have foreach loop:
objects.forEach(function(object) {
var button = '<tr><td>' + object.object.code + '</td><td>' +
formatDistance(1456000) + '</td></tr>';
$(button).mouseup(function(event) {
if (event.which == 1) {
runObject(object);
}
});
result += button;
});
$(mydiv).html(result);
but this can't work. I have one object listed only in each forEach cycle.
How can I correctly write onclick event for each point of loop.
You can append each item into your div inside the forEach loop:
objects.forEach(function(object) {
var button = $('<tr><td>' + object.object.code + '</td><td>' + formatDistance(1456000) + '</td></tr>');
button.mouseup(function(e) { if(e.which === 1) { runObject(object); } });
$(mydiv).append(button);
});
Try this
objects.forEach(function(object) {
var button = '<tr class="test_' + object.object.code + "'><td>' + object.object.code + '</td><td>' +
formatDistance(1456000) + '</td></tr>';
$('.test_' + object.object.code).mouseup(function(event) {
if (event.which == 1) {
runObject(object);
}
});
result += button;
});
$(mydiv).html(result);
you have to resolve syntax error
Here is one of the ways:
//Create an empty container
var $result = $();
[1,2,3,4].forEach(function(object) {
//Create TRs as jQuery objects (as opposed to strings), append whatever
var $button = $('<tr/>').append('<td>[' + object + ']: </td><td>' + ("this is " + object) + '</td>');
//Bing events, mouseup, click or whatever
$button.on("mouseup", function(e) {
//Add whatever conditions you like
if (e.which === 1) {
alert (object);
}
});
//Keep adding the TR to the container
$result = $result.add($button)
});
//Append all at once, outside the loop
$("#mydiv").empty().append($result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="mydiv"></div>

Add div to javascript code

Trying to add a div around some javascript code.
Here's the code I'm trying to modify:
slider.controlNavScaffold = $('<ol class="'+ namespace + 'control-nav ' + namespace + type + '"></ol>');
if (slider.pagingCount > 1) {
for (var i = 0; i < slider.pagingCount; i++) {
slide = slider.slides.eq(i);
item = (slider.vars.controlNav === "thumbnails") ? '<img src="' + slide.attr( 'data-thumb' ) + '"/>' : '<a>' + j + '</a>';
if ( 'thumbnails' === slider.vars.controlNav && true === slider.vars.thumbCaptions ) {
var captn = slide.attr( 'data-thumbcaption' );
if ( '' != captn && undefined != captn ) item += '<span class="' + namespace + 'caption">' + captn + '</span>';
}
slider.controlNavScaffold.append('<li>' + item + '</li>');
j++;
}
}
Here's the resulted outcome when you add <div class="container"> before the <ol> and closing </div> tag after </ol> in the code above...as you can see the list closes before list items make it inside:
<div class="container"><ol class="flex-control-nav flex-control-paging"></ol><li><a>1</a></li><li><a>2</a></li><li><a>3</a></li><li><a>4</a></li></div>
Here's what I'm trying to output.
<div class="container"><ol class="flex-control-nav flex-control-paging"><li><a class="">1</a></li><li><a class="flex-active">2</a></li><li><a>3</a></li><li><a>4</a></li></ol></div>
Code that isn't working:
slider.controlNavScaffold = $('<div class="container"><ol class="'+ namespace + 'control-nav ' + namespace + type + '"></ol></div>');
if (slider.pagingCount > 1) {
for (var i = 0; i < slider.pagingCount; i++) {
slide = slider.slides.eq(i);
item = (slider.vars.controlNav === "thumbnails") ? '<img src="' + slide.attr( 'data-thumb' ) + '"/>' : '<a>' + j + '</a>';
if ( 'thumbnails' === slider.vars.controlNav && true === slider.vars.thumbCaptions ) {
var captn = slide.attr( 'data-thumbcaption' );
if ( '' != captn && undefined != captn ) item += '<span class="' + namespace + 'caption">' + captn + '</span>';
}
slider.controlNavScaffold.append('<li>' + item + '</li>');
j++;
}
}
You didn't post it, but I suspect that you changed that first line to
slider.controlNavScaffold = $('<div><ol class="'+ namespace + 'control-nav ' + namespace + type + '"></ol></div>');
That will cause the code to do exactly what you describe, because the .append() calls will append to the outer element (the <div>).
Instead, leave that first line alone, and then at the end — after the <li> elements have been added — add this:
slider.controlNavScaffold.wrap( $('<div/>', { "class": "container" }) );
After that, in order to have things work properly when you actually add the stuff to the DOM, you'll want to find the parent of the <ol> and make sure that that's what you add:
slider.controlNavWrapper = slider.controlNavScaffold.parent();
(or however you want to keep track of it).

When to combine common functionality? - A public static object w/ initialization

This is a common occurence when I code...I see some code that looks kind of alike..and I know that it is obviously not good to have redundant functionality in my code.
However , is this absolute? 0 Redundancy? I have two functions below, which look kind of alike. ViewH.bookmark and ViewH.tweet.
I'm trying to decide if I should pull out the common functionality into a function called ViewH.mark().
EDIT
var ViewH = {
MARK:
{
FIELD: '|',
ROW: '||',
PASS: '<xx_p>',
FAIL: '<xx_f>'
},
return_string: '',
mark: function(passed_function, embeddedAml)
{
var return_string,
first_split,
element_count,
second_split;
return_string = '';
first_split = embeddedAml.split( ViewH.MARK.ROW );
for( element_count=0; element_count < first_split.length; element_count++)
{
second_split = first_split[element_count].split( ViewH.MARK.FIELD );
passed_function(second_split);
}
return ViewH.return_string;
},
bookmark: function ( embeddedAml )
{
ViewH.return_string='';
return ViewH.mark(ViewH.bookmark_inner, embeddedAml);
},
tweet: function ( embeddedAml )
{
ViewH.return_string='';
return ViewH.mark(ViewH.tweet_inner, embeddedAml);
},
portfolio: function ( embeddedAml )
{
ViewH.return_string='';
return ViewH.mark(ViewH.portfolio_inner, embeddedAml);
},
bookmark_inner: function ( second_split )
{
ViewH.return_string = ViewH.return_string
+ '<img name="bo_im" class="c" src="'
+ 'http://www.google.com/s2/favicons?domain='
+ second_split[0]
+ '" onerror="Arc.BookmarkError(this)"><a target="_blank" name="bookmark_link" class="b" href = "'
+ second_split[1]
+ '">'
+ second_split[2]
+ '</a>';
},
tweet_inner: function ( second_split )
{
ViewH.return_string = ViewH.return_string
+ '<div class="Bb2b"><img class="a" src="'
+ Constant.PICTURES + second_split[ 0 ]
+ '.jpg" alt=""/><a class="a" href="javascript:void(0)\">'
+ second_split[ 1 ]
+ ' posted '
+ ViewH.pretty( second_split[ 2 ],second_split[ 3 ] )
+ '</a><br/><p class="c">'
+ second_split[ 4 ]
+ '</p></div>';
},
portfolio_inner: function ( second_split )
{
if( ( second_split[ 1 ] === 'docx' ) || ( second_split[ 1 ] === 'xlsx' ) )
{
ViewH.return_string = ViewH.return_string
+ '<img name="bo_im" class="c" src="'
+ Constant.IMAGES + second_split[1]
+ '.ico"><a target="_blank" name="bookmark_link" class="b" href = "/'
+ Constant.ROOT
+ second_split[1]
+ '/'
+ second_split[0]
+ '.'
+ second_split[1]
+ '">'
+ second_split[0]
+ '.'
+ second_split[1]
+ '</a>';
}
else
{
ViewH.return_string=ViewH.return_string
+ '<simg name="bo_im" class="c" src="'
+ Constant.IMAGES
+ 'generic'
+ '.ico"><a target="_blank" name="bookmark_link" class="b" href = "'
+ Constant.TEXT
+ second_split[0]
+ '.txt">'
+ second_split[0]
+ '.'
+ second_split[1]
+ '</a>';
}
},
This is a great question, but there is no answer that will apply to all cases. It really is going to depend on what your code looks like. Redundancy is generally to be avoided but it is sometimes worse to over-engineer your code and try to make it fit into a box that it does not really fit into.
In your case you could definitely benefit from taking common code and pulling it into a common method. It looks like the only difference between your methods is the rendering part and it would be simple to pass a rendering function into your "mark" method.
Your "mark" method would look a bit like this:
mark: function(embeddedAml, renderer) {
var return_string,
first_split,
element_count,
second_split;
return_string = '';
first_split = embeddedAml.split( ViewH.MARK.ROW );
for( element_count=0; element_count < first_split.length; element_count++)
{
second_split = first_split[element_count].split( ViewH.MARK.FIELD );
return_string = return_string + renderer(second_split);
}
return return_string;
}
You would keep your bookmark and tweet methods but they would change as well:
bookmark: function (embeddedAml) {
return this.mark(embeddedAml, function(data) {
return '<img name="bo_im" class="c" src="' +
'http://www.google.com/s2/favicons?domain=' +
data[0] +
'" onerror="Arc.BookmarkError(this)"><a target="_blank" name="bookmark_link" class="b" href = "' +
data[1] + '">' +
data[2] + '</a>'
});
}
Now your rendering code (the only code that was different) is controlled independently, but the code that overlapped is in a common place and if it changes you only have to update it in one place.
Generally, yes.
One deciding factor is whether the code is similar coincidentally, or because it performs a similar task. If the latter is true, should you change the functionality of one in the future (particularly that part of the functionality which is shared), will you also want to change the functionality in the other? If so, that makes your decision easy - merge the code where you can.
Even if the code is similar coincidentally, it may still make sense to create a generic library function that cleans up your code.
I would definitely attempt to combine them. You'll notice that the body of the for loop is the only thing that's different between the two. Here's one approach (most of ViewH elided):
var ViewH = {
bookmark: function(embeddedAml) {
return ViewH.combinedFunc(embeddedAml, function(parts) {
return '<img name="bo_im" class="c" src="' +
'http://www.google.com/s2/favicons?domain=' +
parts[0] +
'" onerror="Arc.BookmarkError(this)"><a target="_blank" name="bookmark_link" class="b" href = "' +
parts[1] + '">' +
parts[2] + '</a>';
});
},
combinedFunc: function (embeddedAml, handler) {
var return_string,
first_split,
element_count,
second_split;
return_string = '';
first_split = embeddedAml.split(ViewH.MARK.ROW);
for(element_count=0; element_count < first_split.length; element_count++) {
second_split = first_split[element_count].split(ViewH.MARK.FIELD);
return_string = return_string + handler(second_split);
}
return return_string;
},
}
You could easily do the same thing for tweet. Clearly, you'll want to name the function something better than combinedFunc, but you'll need to choose that name based on context.

streamlining a series of javascript functions

Is there a better, more concise way to do this:
function getTweets(){
$.getJSON("http://search.twitter.com/search.json?callback=?&q=superfad",
function(data){
tweetsLoaded = true;
$.each(data.results, function(i,item){
var textPlain = item.text;
var textLinked = linkify(textPlain);
var textHashed = hashify(textLinked);
var textListed = listify(textHashed);
function linkify(tweet){
return tweet.replace(/(http:\/\/[^\s]*)/g, "<a class='twtr-link' target=\"_blank\" href=\"$1\">$1</a>");
}
function hashify(tweet){
return tweet.replace(/(^|\s+)#(\w+)/gi, function(m, before, hash) {
return before + '<a target="_blank" class="twtr-hashtag" href="http://twitter.com/search?q=%23' + hash + '">#' + hash + '</a>';
});
}
function listify(tweet) {
return tweet.replace(/\B[#@]([a-zA-Z0-9_]{1,20})/g, function(m, username) {
return '<a target="_blank" class="twtr-atreply" href="http://twitter.com/intent/user?screen_name=' + username + '">#' + username + '</a>';
});
}
$("#twitter_results").append('<li class="twitter"><img class="twitter_img" src="' + item.profile_image_url + '"/>'+ textListed + '</li>');
});
});
} //end getTweets
// define this globally
function stuffify(match, group1, group2) {
switch (group1 || group2) {
case 'http':
return '<a class="twtr-link" target="_blank" href="' + match + '">' + match + '</a>';
case '#':
return '<a class="twtr-hashtag" target="_blank" href="http://twitter.com/search?q=' + encodeURIComponent(match) + '">#' + match + '</a>'
case '#':
return '<a class="twtr-atreply" target="_blank" href="http://twitter.com/intent/user?screen_name=' + encodeURIComponent(match) + '">#' + match + '</a>';
default:
return match;
}
}
function(data){
tweetsLoaded = true;
var interestingParts = /(http):\/\/\S+|(#|#)[^\s.,!?;^()\[\]<>{}]+/g;
$.each(data.results, function(i,item) {
var newText = item.text.replace(interestingParts, stuffify);
$("#twitter_results").append('<li class="twitter"><img class="twitter_img" src="' + item.profile_image_url + '"/>'+ newText + '</li>');
});
}
I would realign the code such that the text manipulation functions (linkify, hashify, listify) are not inside of the each function, nor are they even inside of getTweets itself. If it's in getTweets, then every time you call that function they have to be redefined. Worse, inside of the each where you had them, those functions are redefined for every item in the returned tweet-set.
Also, there is no need to store the return of each of those functions in it's own var since you only use them once afterward and aren't performing any checks before using them. Just nest the function calls.
Finally, since you're calling append over and over on the same element (inside the each) I pre-queried that element instead of querying for it every time the each function is executed
Those changes, along with some things I do for my own performance preferences are exhibited in the code sample pasted below.
There are other things I would do, however I won't show them here--you can see it in my posted answer at Trouble Converting jQuery Script to Plugin -- a question about making a jQuery plugin which does the same ting as your code). You should store the formatted tweets in an array rather than append each as you get it. Having built that array, you should then combine it into one string and call append once with that string. Making this a jQuery plugin would also be nice for you as it would not require you modify the code to change the targeted DOM element. Looking into use of String.prototype.link would be good as well.
(demo: http://jsfiddle.net/JAAulde/fQ3Lp/2/ )
var getTweets = ( function()
{
/* Privatized text manipulation functions */
var linkify = function( tweet )
{
return tweet.replace( /(http:\/\/[^\s]*)/g, "<a class='twtr-link' target=\"_blank\" href=\"$1\">$1</a>" );
};
var hashify = function( tweet )
{
return tweet.replace( /(^|\s+)#(\w+)/gi, function(m, before, hash)
{
return before + '<a target="_blank" class="twtr-hashtag" href="http://twitter.com/search?q=%23' + hash + '">#' + hash + '</a>';
} );
};
var listify = function( tweet )
{
return tweet.replace(/\B[#@]([a-zA-Z0-9_]{1,20})/g, function(m, username)
{
return '<a target="_blank" class="twtr-atreply" href="http://twitter.com/intent/user?screen_name=' + username + '">#' + username + '</a>';
} );
};
var $twitterResultTarget = $( "#twitter_results" );
/* The actual function which is stored in `getTweets` */
return function()
{
$.getJSON( "http://search.twitter.com/search.json?callback=?&q=superfad", function( data )
{
tweetsLoaded = true;
$.each( data.results, function( i, item )
{
$twitterResultTarget
.append( [
'<li class="twitter"><img class="twitter_img" src="',
item.profile_image_url,
'"/>',
listify( hashify( linkify( item.text ) ) ),
'</li>'
].join( '' ) );
} );
} );
}
}() );
Try to avoid using anonymous functions, its always less confusing if they all have names and are declared outside of any other functions.
I'd probably write in in the jQuery plugin style, and use split as opposed to replace with HTML in callbacks:
(function($){
function getTweets(q) {
var
$set = this,
prefixes = {
'h': 'h',
'#': 'http://twitter.com/search?q=%23',
'#': 'http://twitter.com/intent/user?screen_name='
},
classes = {
'h': 'twtr-link',
'#': 'twtr-hashtag',
'#': 'twtr-atreply'
};
$.getJSON(
"http://search.twitter.com/search.json?callback=?&q=" + encodeURIComponent(q),
function(data){
$.each(data.results, function(i, item){
var
$li = $('<li class="twitter"></li>')
.append('<img class="twitter_img" src="' + item.profile_image_url + '"/> ');
$.each(
item.text.split(
/(\s+)|(https?:\/\/[^\s]*)|(#\w+)|(#[a-zA-Z0-9_]{1,20})/g
),
function(i, chunk) {
if (/^(https?:\/\/|#|#)/.test(chunk)) {
$('<a target="_blank"></a>')
.addClass(classes[chunk[0]])
.attr('href', prefixes[chunk[0]] + chunk.substr(1))
.text(chunk)
.appendTo($li);
} else if (chunk) {
$li.append(document.createTextNode(chunk));
}
}
);
$li.appendTo($set);
});
}
);
}
$.fn.getTweets = getTweets;
})(jQuery);
Then call it like this:
$('#twitter_results').getTweets('superfad');
My $0.02

Categories

Resources