Easier way to Populate Jstree with 6 levels - javascript

I have been working since last week with jstree for a project, and right now I am kind of stuck. I have been able to make it work with static data, but now I need to do it dynamically, and it needs to have up to 6 levels nested. Another condition is that the user should be able to just choose the deepest level, so I need to disable and make invisible the checkbox whenever a node has a children.
I am getting the data from SQL, but I am using a software called Thingworx to generate the user interface of the project (the tree is going to be a widget of for this software), so using JSON I think it's not an option.
My problem is that the only solution that I have found for now is something like this:
out:
var rowsActualData = core.data.rows.length
for (var i = 0, i < rowsActualData, i++){
if (Level1Value === core.data[i].text){
var rowsActualData2 = core.data.children.rows.length
for (var j = 0, j < rowsActualData2, j++){
if (Level2Value === core.data[i].children[j].text){
if (Level3Value !== null){
var rowsActualData3 = core.data.children.children.rows.length
...
All the levels of the code. Need less indent to be able to show it here
To check each case I need to check if the node exist already, go to the next level and check if exist, several times. And then, depending on each condition, I need to write it down as a new one or push it to one that exists already (for each level).
Part of the code for the first row, when the tree doesn't have data yet
Here an example of how it should look, but populating it with static data https://jsfiddle.net/josuenanin/LeyeLsrc/3/
Maybe I haven't explained myself too well, but not sure how to explain it without a lot of code.
Thank you in advance.

Related

How can I filter to an array and choose a random Id from it?

Good night folks.
I'm new to programming, less than 2 months, so I don't quite understand how to make this work. Here's exactly what I want to do:
I have this game I'm trying to make which needs to do a raffle between existing units that fit the condition "is alive" and have "is able to use the prize". To do that, I was told I needed to include an array with the unit Id for the units that can receive it, then filter them by "is alive" to choose from by a random generator. Thing is, I have no idea how to make this work. I've tried this code, but it does not work. Anyone has any idea why or how I should do it instead?
var rafflearray = []; // the array containing the units
if (root.getExternalData().isUnitRegistered() = true) {var character = root.getCurrentSession().getPlayerList().getData()}; // establish the character as a variable
if (var character.getAliveStatus = true ) {rafflearray.push(character)}; // checks his alive status and send him to the array
var chosen = rafflearray [Math.random()*chosenarray.lenght]; // to choose amongst them
chosen.addItem()
Thanks in advance for the attention!
I strongly suggest going through Javascript Fundamentals (here section 1 & 2) before this project. However I hope this answer helps you
First of all you cannot use var inside if statements like
if(var character.getAliveStatus = true)
And you need to use == or === instead of = , like:
if(isAlive === true){
// Code to be executed
}
// the difference between '==' and '===' can be found in the docs mentioned below
see Javascript Operators from here on section 3
var is for declaring a variable which is not limited to its parent blocks see javascript scoping
In your case I'm not sure if you have an array of players already or if you're trying to make one.
In the first situation, I suggest you use Array.filter() function which is a cool way. For example :
// Assuming 'players' is the array of players in the game
// filtering out raffle worthy players
let raffle_worthy = players.filter(player => player.getAliveStatus === true);
// Randomly choosing 1 person between the raffle worthy players
let chosen = raffle_worthy[Math.floor(Math.random * raffle_worthy.length)]
You may want to take a look at Array.filter() docs and also Js Math has been used in the above example
On the other hand if you dont have an array of players and getting them one by one, I'm not really sure if I can understand the way you get users and what gathers them

Learning Basics --- Extracting Extensions in Javascript

Working on an Edabit challenge and running into a situation that frequently confuses me. Still learning basics so I apologize if my terminology is off or unclear.
The challenge was to separate an array of file names with extensions from their extensions. I used toString and split methods across two lines to separate all the tests into arrays that contain the file names and extensions separately. With the first file name being position 0 and the first extension being 1.
So from here I thought using a for loop starting at position 1 and incrementing every other value (i+=2) would work. Just unsure how to return the values at this point. I can console.log the index at this point and it prints what I want to get but, if i say return index it just returns the first item, and if I create a new array and use push method it just returns a set of numbers. Unsure what to do, or if this was the right thing to do.
Code for ref this is after splitting the original tests into arrays that contained only the file names and extension names as separate items.
for(let i = 1; i < stringPeriod.length; i+=2){
console.log(stringPeriod[i])
}
this will print the extension names but not separated as a single array which I need to pass the challenge.
Is this what you're looking for? You need an array with just the extensions?
You can accomplish it with a new array (I have a feeling it wasn't working for you because you were probably just pushing "i" into the new array)
var newArray = [];
for(let i = 1; i < stringPeriod.length; i+=2){
newArray.push(stringPeriod[i]);
}
console.log(newArray);

D3 V4 Tree Search and Highlight

So, I really love this example from Jake Zieve shown here: https://bl.ocks.org/jjzieve/a743242f46321491a950
Basically, on search for a term, the path to that node is highlighted. I would like to accomplish something similar but with the following caveats:
I would like to stay in D3 v4.
I'm concerned about cases where the path doesn't clear out on next node pick OR what happens when there are two nodes of the same
name (I would ideally like to highlight all paths)
I would like to AVOID using JQuery
Given a set search term (assume you're already getting the string from somewhere) I know I need to make use of the following lines specifically (you can see my stream of consciousness in the comments) but I'm just not quite sure where to start.
// Returns array of link objects between nodes.
var links1 = root.descendants().slice(1); //slice to get rid of company.
console.log(links1); //okay, this one is nice because it gives a depth number, this describes the actual link info, including the value, which I am setting link width on.
var links2 = root.links(); // to get objects with source and target properties. From here, I can pull in the parent name from a selected target, then iterate again back up until I get to source. Problem: what if I have TWO of the same named nodes???
console.log(links2);
Thoughts on this? I'll keep trying on my own, but I keep hitting roadblocks. My code can be found here: https://jsfiddle.net/KateJean/7o3suadx/
[UPDATE]
I was able to add a filter to the links2 to call back a specific entry. See
For example:
var searchTerm = "UX Designer"
var match = links2.filter(el => el.target.data.name === searchTerm); //full entry
console.log(match);
This single entry gives me all associated info, including the full list of all points back up to "COMPANY"
So, I can GET the data. I think the best way to accomplish what I want is to somehow add a class to each of these elements and then style on that "active" class.
Thank you!

Backbone pass different max values to template?

This theoretically seems kind of easy but I am not 100% on the appropriate code to write.
Inside a view if I am defining a max variable that returns an attribute points in a collection, I can pass that variable as an object into my template like maxPoints.toJSON() and get that object's attributes.
var maxPoints = this.collection.max(function(player){
return player.get('team') == home || player.get('team') == away ? player.get('points') : 0;
});
//Pass to template
this.$el.find('.top-preformer').append(this.template(maxPoints.toJSON()));
//Inside the template using underscore.js I can get the `name` of the object returned
<%= name %>
//Easy, but now I am passing two objects, how do I access `name` for top points
//or `name` for top assists?
This is fine, but what if I want to retrieve multiple max values and interact with them inside the template?
var maxAssists = this.collection.max(function(player){
return player.get('team') == home || player.get('team') == away ? player.get('assists') : 0;
});
Now I have an object maxAssists that I could pass into my template, but I am not sure how this would work. How could I interact with each specific object?
I'm sorry if this answer isn't appropriate now that you've deleted your previous question and asked another one. This is the answer I was about to send when you deleted your question:
I hope I've correctly understood your scenario. Let me sum it up just in case:
There's a Model, let's call it Player, which represents a player with a name, a team, points and assists (at least). There's a Collection of Players which is held by the View you're referring to. This View shows stuff about players and also highglights the player with most points and the one with most assists.
I'm assuming too much about your needs, but let's say you want to show a list of players with their info and then the info of those highlighted. Check this JSFiddle I prepared to show my approach.
As a rule of thumb, you shouldn't set data in the DOM unless it's extrictly necessary, that's why you're using JavaScript after all. Since you have a View which holds a Collection, that View should be the one to hold references to both most points player and most assists player. Using your max functions, you may do it this way:
getMaxPointsPlayer: function () {
return this.getMax("points");
},
getMaxAssistsPlayer: function () {
return this.getMax("assists");
},
getMax : function (attribute) {
return this.collection.max(function (player) {
return player.get("team") === this.homeTeam || player.get("team") === this.awayTeam ? player.get(attribute) : 0;
}, this);
},
Note two things:
You should always use === instead of == where possible. Search this site for more info on it.
Since I don't know where those home and away variables are in your code and what are them, I placed them as member variables in the view (and they're just strings).
So, when you want to reference the player with most points/assists, you'd call one of those functions which are part of the View. Then you may store it in a variable (so you can get it without further filtering) or use it straight away from the call to the function.
If you study the code in the JSFiddle, you'll see more stuff than you asked for. It's there to show you a nice way to keep your application clean and tidy (obviously, it's just a JSFiddle). Try to keep Views on their own business (may it be present and deal with a Model, have a list of sub-views and manage them because they're part of a Collection or just be a manager of other sub-views). That way, those views have the logic they need without dealing with data in the DOM.

jQuery "Autocomplete" plugin is messing up the order of my data

I'm using Jorn Zaefferer's Autocomplete plugin on a couple of different pages. In both instances, the order of displayed strings is a little bit messed up.
Example 1: array of strings: basically they are in alphabetical order except for General Knowledge which has been pushed to the top:
General Knowledge,Art and Design,Business Studies,Citizenship,Design and Technology,English,Geography,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Displayed strings:
General Knowledge,Geography,Art and Design,Business Studies,Citizenship,Design and Technology,English,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Note that Geography has been pushed to be the second item, after General Knowledge. The rest are all fine.
Example 2: array of strings: as above but with Cross-curricular instead of General Knowledge.
Cross-curricular,Art and Design,Business Studies,Citizenship,Design and Technology,English,Geography,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Displayed strings:
Cross-curricular,Citizenship,Art and Design,Business Studies,Design and Technology,English,Geography,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Here, Citizenship has been pushed to the number 2 position.
I've experimented a little, and it seems like there's a bug saying "put things that start with the same letter as the first item after the first item and leave the rest alone". Kind of mystifying. I've tried a bit of debugging by triggering alerts inside the autocomplete plugin code but everywhere i can see, it's using the correct order. it seems to be just when its rendered out that it goes wrong.
Any ideas anyone?
max
EDIT - reply to Clint
Thanks for pointing me at the relevant bit of code btw. To make diagnosis simpler i changed the array of values to ["carrot", "apple", "cherry"], which autocomplete re-orders to ["carrot", "cherry", "apple"].
Here's the array that it generates for stMatchSets:
stMatchSets = ({'':[#1={value:"carrot", data:["carrot"], result:"carrot"}, #3={value:"apple", data:["apple"], result:"apple"}, #2={value:"cherry", data:["cherry"], result:"cherry"}], c:[#1#, #2#], a:[#3#]})
So, it's collecting the first letters together into a map, which makes sense as a first-pass matching strategy. What i'd like it to do though, is to use the given array of values, rather than the map, when it comes to populating the displayed list. I can't quite get my head around what's going on with the cache inside the guts of the code (i'm not very experienced with javascript).
SOLVED - i fixed this by hacking the javascript in the plugin.
On line 549 (or 565) we return a variable csub which is an object holding the matching data. Before it's returned, I reorder this so that the order matches the original array of value we were given, ie that we used to build the index in the first place, which i had put into another variable:
csub = csub.sort(function(a,b){ return originalData.indexOf(a.value) > originalData.indexOf(b.value); })
hacky but it works. Personally i think that this behaviour (possibly coded more cleanly) should be the default behaviour of the plugin: ie, the order of results should match the original passed array of possible values. That way the user can sort their array alphabetically if they want (which is trivial) to get the results in alphabetical order, or they can preserve their own 'custom' order.
What I did instead of your solution was to add
if (!q && data[q]){return data[q];}
just above
var csub = [];
found in line ~535.
What this does, if I understood correctly, is to fetch the cached data for when the input is empty, specified in line ~472: stMatchSets[""] = []. Assuming that the cached data for when the input is empty are the first data you provided to begin with, then its all good.
I'm not sure about this autocomplete plugin in particular, but are you sure it's not just trying to give you the best match possible? My autocomplete plugin does some heuristics and does reordering of that nature.
Which brings me to my other answer: there are a million jQuery autocomplete plugins out there. If this one doesn't satisfy you, I'm sure there is another that will.
edit:
In fact, I'm completely certain that's what it's doing. Take a look around line 474:
// loop through the array and create a lookup structure
for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
/* some code */
var firstChar = value.charAt(0).toLowerCase();
// if no lookup array for this character exists, look it up now
if( !stMatchSets[firstChar] )
and so on. So, it's a feature.

Categories