Angularjs function inside foreach loop not working - javascript

I have two functions deleteNode, which accepts a node id and deleteEdge ,which takes a edge object as input.deleteEdge is triggered when a button is pressed and it deletes a particular node and also deletes all its connected edges. However my code only seems to delete the first edge that was added in the array.Not sure what is causing this problem.
Here $scope.nodes is an array of objects with each object looks like this:
var node = {
id:$scope.node_id,
Yedges_id:[],
Nedges_id:[],
}
And $scope.edges is an array of objects where each edge looks like :
var edge = {
id: $scope.edge_id,
start_id: start,
end_id: end,
}
$scope.deleteEdge = function(edge){
var index = $scope.edges.indexOf(edge)
var start_idx = $scope.nodes.findIndex(node=>node!==undefined && node.id==edge.start_id);
var end_idx = $scope.nodes.findIndex(node=>node!==undefined && node.id==edge.end_id);
var Ystart_edge_idx = $scope.nodes[start_idx].Yedges_id.indexOf(edge.id);
var Nstart_edge_idx = $scope.nodes[start_idx].Nedges_id.indexOf(edge.id);
var end_edge_idx = $scope.nodes[end_idx].Yedges_id.indexOf(edge.id);
if(Ystart_edge_idx!==-1){
$scope.nodes[start_idx].Yedges_id.splice(Ystart_edge_idx,1);
}
if(Nstart_edge_idx!==-1){
$scope.nodes[start_idx].Nedges_id.splice(Nstart_edge_idx,1);
}
$scope.nodes[end_idx].Yedges_id.splice(end_edge_idx,1);
$scope.edges.splice(index, 1);
}
$scope.deleteNode = function(id){
var index = $scope.nodes.findIndex(x=>x!==undefined && x.id===id);
$scope.nodes[index].Yedges_id.forEach(edge_id => {
var edge = $scope.edges.filter(edge=>edge.id==edge_id)
$scope.deleteEdge(edge[0]);
});
$scope.nodes[index].Nedges_id.forEach(edge_id => {
var edge = $scope.edges.filter(edge=>edge.id==edge_id)
$scope.deleteEdge(edge[0]);
});
delete $scope.nodes[index]
}

Related

JavaScript - Issues recovering a map in an object after being saved in localStorage

I've been dealing with this for some time. I've a list of sections in which the user checks some checkboxes and that is sent to the server via AJAX. However, since the user can return to previous sections, I'm using some objects of mine to store some things the user has done (if he/she already finished working in that section, which checkboxes checked, etc). I'm doing this to not overload the database and only send new requests to store information if the user effectively changes a previous checkbox, not if he just starts clicking "Save" randomly. I'm using objects to see the sections of the page, and storing the previous state of the checkboxes in a Map. Here's my "supervisor":
function Supervisor(id) {
this.id = id;
this.verif = null;
this.selections = new Map();
var children = $("#ContentPlaceHolder1_checkboxes_div_" + id).children().length;
for (var i = 0; i < children; i++) {
if (i % 2 == 0) {
var checkbox = $("#ContentPlaceHolder1_checkboxes_div_" + id).children()[i];
var idCheck = checkbox.id.split("_")[2];
this.selections.set(idCheck, false);
}
}
console.log("Length " + this.selections.size);
this.change = false;
}
The console.log gives me the expected output, so I assume my Map is created and initialized correctly. Since the session of the user can expire before he finishes his work, or he can close his browser by accident, I'm storing this object using local storage, so I can change the page accordingly to what he has done should anything happen. Here are my functions:
function setObj(id, supervisor) {
localStorage.setItem(id, JSON.stringify(supervisor));
}
function getObj(key) {
var supervisor = JSON.parse(localStorage.getItem(key));
return supervisor;
}
So, I'm trying to add to the record whenever an user clicks in a checkbox. And this is where the problem happens. Here's the function:
function checkboxClicked(idCbx) {
var idSection = $("#ContentPlaceHolder1_hdnActualField").val();
var supervisor = getObj(idSection);
console.log(typeof (supervisor)); //Returns object, everythings fine
console.log(typeof (supervisor.change)); //Returns boolean
supervisor.change = true;
var idCheck = idCbx.split("_")[2]; //I just want a part of the name
console.log(typeof(supervisor.selections)); //Prints object
console.log("Length " + supervisor.selections.size); //Undefined!
supervisor.selections.set(idCheck, true); //Error! Note: The true is just for testing purposes
setObj(idSection, supervisor);
}
What am I doing wrong? Thanks!
Please look at this example, I removed the jquery id discovery for clarity. You'll need to adapt this to meet your needs but it should get you mostly there.
const mapToJSON = (map) => [...map];
const mapFromJSON = (json) => new Map(json);
function Supervisor(id) {
this.id = id;
this.verif = null;
this.selections = new Map();
this.change = false;
this.selections.set('blah', 'hello');
}
Supervisor.from = function (data) {
const id = data.id;
const supervisor = new Supervisor(id);
supervisor.verif = data.verif;
supervisor.selections = new Map(data.selections);
return supervisor;
};
Supervisor.prototype.toJSON = function() {
return {
id: this.id,
verif: this.verif,
selections: mapToJSON(this.selections)
}
}
const expected = new Supervisor(1);
console.log(expected);
const json = JSON.stringify(expected);
const actual = Supervisor.from(JSON.parse(json));
console.log(actual);
If you cant use the spread operation in 'mapToJSON' you could loop and push.
const mapToJSON = (map) => {
const result = [];
for (let entry of map.entries()) {
result.push(entry);
}
return result;
}
Really the only thing id change is have the constructor do less, just accept values, assign with minimal fiddling, and have a factory query the dom and populate the constructor with values. Maybe something like fromDOM() or something. This will make Supervisor more flexible and easier to test.
function Supervisor(options) {
this.id = options.id;
this.verif = null;
this.selections = options.selections || new Map();
this.change = false;
}
Supervisor.fromDOM = function(id) {
const selections = new Map();
const children = $("#ContentPlaceHolder1_checkboxes_div_" + id).children();
for (var i = 0; i < children.length; i++) {
if (i % 2 == 0) {
var checkbox = children[i];
var idCheck = checkbox.id.split("_")[2];
selections.set(idCheck, false);
}
}
return new Supervisor({ id: id, selections: selections });
};
console.log(Supervisor.fromDOM(2));
You can keep going and have another method that tries to parse a Supervisor from localStorageand default to the dom based factory if the localStorage one returns null.

Best way to use Local Storage in this example?

I've built a to do list in vanilla JS, but now I want to convert it to local storage.
I know how to use set, get and remove in local storage, but I'm not sure how to use it on my app.
because each to do item has text, a delete button, edit button, checkbox etc I'm not sure how I save that.
Or can I save just the text and somehow render it with the other elements?
Just wanting to get an idea of the theory/best way to think about taking this task on.
I'm wondering if there's a simple way to do it or it's more a case of completely restructuring the way the app works.
Cheers
JS
// To do list
// Cache DOM
var addToDo = document.getElementById('add-to-do');
var taskHolder = document.getElementById('task-holder');
var uncompleteTasks = document.getElementById('uncompleted-tasks');
var completedTasks = document.getElementById('completed-tasks');
// Bind events
var bindEvents = function(listItem, checkboxEventHandler) {
// Delete
var deleteToDo = listItem.querySelector('.delete-to-do');
deleteToDo.addEventListener('click', deleteTask);
// Edit
listItem.querySelector('.edit-to-do').addEventListener('click', editTask);
listItem.querySelector('.edit-holder').addEventListener('keyup', editTaskEnter);
// Checkbox
var checkbox = listItem.querySelector('input.edit-to-do');
checkbox.onchange = checkboxEventHandler;
}
// Create list item
var createListItem = function() {
var listItem = document.createElement('li');
var deleteToDo = document.createElement('button');
deleteToDo.innerHTML = 'delete';
deleteToDo.classList.add('delete-to-do');
var editToDo = document.createElement('button');
editToDo.innerHTML = 'edit';
editToDo.classList.add('edit-to-do');
var toDoStatus = document.createElement('input');
toDoStatus.type = 'checkbox';
toDoStatus.classList.add('edit-to-do');
var editHolder = document.createElement('input');
editHolder.type = 'text';
editHolder.classList.add('edit-holder');
listItem.appendChild(deleteToDo);
listItem.appendChild(editToDo);
listItem.appendChild(toDoStatus);
listItem.appendChild(editHolder);
return listItem;
}
// Add task
var addTask = function(e) {
var taskHolderValue = taskHolder.value;
if(taskHolderValue) {
var taskHolderElement = document.createElement('label');
taskHolderElement.classList.add('to-do-item');
taskHolderElement.innerHTML = taskHolderValue;
var listItem = createListItem();
listItem.insertBefore(taskHolderElement, listItem.childNodes[0]);
uncompleteTasks.appendChild(listItem);
bindEvents(listItem, taskCompleted);
taskHolder.value = '';
} else {
alert("You didn't add a to a to do!");
}
}
var addTaskEnter = function(e) {
var key = 'which' in e ? e.which : e.keyCode;
if(key === 13) {
addTask();
}
}
// Delete task
var deleteTask = function() {
var listItem = this.parentNode;
console.log(listItem);
var parentItem = listItem.parentNode;
parentItem.removeChild(listItem);
}
// Edit task
var editTask = function() {
var defaultValue = this.parentNode.querySelector('label').innerHTML;
var listItem = this.parentNode;
var listParent = this.parentNode;
var editedValue = listParent.querySelector('input.edit-holder').value;
if(listItem.classList.contains('editing') && editedValue) {
listParent.querySelector('label').innerHTML = editedValue;
}
listItem.classList.toggle('editing');
}
// Edit task enter
var editTaskEnter = function(e) {
var key = 'which' in e ? e.which : e.keyCode;
if(key === 13) {
editTask.call(this);
}
}
// Task completed
var taskCompleted = function() {
var listItem = this.parentNode;
completedTasks.appendChild(listItem);
this.parentNode.classList.add('completed');
bindEvents(listItem, taskUncompleted);
}
// Task uncompleted
var taskUncompleted = function() {
var listItem = this.parentNode;
uncompleteTasks.appendChild(listItem);
this.parentNode.classList.remove('completed');
bindEvents(listItem, taskCompleted);
}
// Add task
addToDo.addEventListener("click", addTask);
taskHolder.addEventListener("keyup", addTaskEnter);
// Loop over uncomplete tasks
for(i=0; i<completedTasks.length; i++) {
var listItem = completedTasks[i];
uncompleteTasks.appendChild(listItem);
bindEvents(listItem, completedTasks);
}
You don't really need to save any of the html, but you can save things associated with it. It's all in the way you store your todo list. Consider creating some kind of variable you can use to store your todo list items such as an array or object.
A common method is to use an array of objects. This is beneficial because you can also put extra details in it. Something like this:
var todos = [ { id: 1, text : "Clean", completed : true}, { id : 2, text : "Eat", completed : false } ... ]
In this example I've put an id so that I could potentially use it later to reference a specific item, but this isn't strictly necessary.
Using JSON#parse and JSON#stringify you can convert this array to and from a string so that it can be stored in localStorage.
Whenever the page loads you should populate that todo list and whenever the todo list changes you should save the changes back to localStorage.
Lastly you should modify your code to iterate through the array each time the user adds or removes a todo list item, possibly using Array#splice to remove the item and Array#push to append a new one to the end, then recreate your entire todo list section.

Alternative to Map.set(key, list) always points to the same list

The following code is supposed to build a map that contains (id, domainList), as the current JS version doesn't allow Map.set() (it doesn't even recognize Map()).
When using testMap[key] = domainList, it always points to the same domainList rather than other lists like Map.set() does. It always points to a single domainList.
What would be the cause of this?
testMap = {};
for (var key in idSet) {
iterator = data.iterator();
var domainList = [];
while (iterator.hasNext()) {
var tuple = iterator.next();
var domain = tuple["Domain"];
var current_Id = tuple["Id"];
if (current_Id == key) {
domainList.push(domain);
}
}
testMap[key] = domainList;
domainList.lengh() = 0;

Deleting a record from desktop Chrome IndexedDB vs from android Chrome IndexedDB

I am trying to delete a record from IndexedDB using the fallowing code:
DB.indexedDB.IDBTransaction.READ_WRITE="readwrite";
window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
if ('webkitIndexedDB' in window) {
window.IDBTransaction = window.webkitIDBTransaction;
window.IDBKeyRange = window.webkitIDBKeyRange;
DB.indexedDB.IDBTransaction.READ_WRITE=IDBTransaction.READ_WRITE;
}
DB.indexedDB.idelete = function( storeName, indexValue, index, keyPathfield ){
var db = DB.indexedDB.db;
var transaction = db.transaction([storeName], DB.indexedDB.IDBTransaction.READ_WRITE);
var store = transaction.objectStore(storeName);
var sindex = store.index(index);
sindex.get(indexValue).onsuccess = function (event){
store.delete(event.target.result[keyPathfield]).onsuccess = function(event) {
document.getElementById("result").innerHTML+="deleted<br>";
};
}
}
it calls onsuccess but when I add a new record with the same indexValue and call idelete again and search for the record using the fallowing code:
DB.indexedDB.readAll=function(storeName, index){
var results=document.getElementById("result");
var db = DB.indexedDB.db;
var transaction = db.transaction([storeName]);
var store = transaction.objectStore(storeName);
var key = IDBKeyRange.lowerBound(0);
var cursor = store.openCursor(key);
var x=0;
cursor.onsuccess = function(event){
var result = event.target.result;
if(result)
{
x++;
var charx=x.toString();
results.innerHTML+=charx+result.value[index]+"<br>";
result.continue();
}
else
return;
}
if I am using Windows Chrome, the record is deleted correctly. But if I am using Android Chrome version M18.1, readAll can still find the record but idelete can't delete it because it was actually deleted.
How about just open key cursor instead of actually retrieving it.
sindex.openKeyCursor(indexValue).onsuccess = function (event){
var cursor = event.target.result;
if (cursor) {
// cursor.delete();
var key = cursor.key;
var primaryKey = cursor.primaryKey;
store.delete(primaryKey).onsuccess = function(event) {
document.getElementById("result").innerHTML+= key + ' (' + primaryKey + ") deleted<br>";
};
}
} else {
document.getElementById("result").innerHTML+= indexValue + ' not found.<br>";
}
}
The problem here is that there are multiple transactions in flight at once - I can tell this becuause idelete() doesn't return the transaction it creates, so there's no way to guarantee that readAll() executes after idelete().
instead you're going to need to make idelete() return the transaction, and then handle that:
foo = idelete(....).oncomplete = function() { readAll()...}
I think the fact that it happens to work in android is just a fluke of the implementation of chrome (which is single-process on android, so events tend to run more serially there)

return from JS function

basic JS question, please go easy on me I'm a newb :)
I pass 2 variables to the findRelatedRecords function which queries other related tables and assembles an Array of Objects, called data. Since findRelatedRecords has so many inner functions, I'm having a hard time getting the data Array out of the function.
As it currently is, I call showWin inside findRelatedRecords, but I'd like to change it so that I can get data Array directly out of findRelatedRecords, and not jump to showWin
function findRelatedRecords(features,evtObj){
//first relationship query to find related branches
var selFeat = features
var featObjId = selFeat[0].attributes.OBJECTID_1
var relatedBranch = new esri.tasks.RelationshipQuery();
relatedBranch.outFields = ["*"];
relatedBranch.relationshipId = 1; //fac -to- Branch
relatedBranch.objectIds = [featObjId];
facSel.queryRelatedFeatures(relatedBranch, function(relatedBranches) {
var branchFound = false;
if(relatedBranches.hasOwnProperty(featObjId) == true){
branchFound = true;
var branchSet = relatedBranches[featObjId]
var cmdBranch = dojo.map(branchSet.features, function(feature){
return feature.attributes;
})
}
//regardless of whether a branch is found or not, we have to run the cmdMain relationship query
//the parent is still fac, no advantage of the parent being branch since cmcMain query has to be run regardless
//fac - branch - cmdMain - cmdSub <--sometimes
//fac - cmdMain - cmdSub <-- sometimes
//second relationship query to find related cmdMains
var relatedQuery = new esri.tasks.RelationshipQuery();
relatedQuery.outFields = ["*"];
relatedQuery.relationshipId = 0; //fac -to- cmdMain
relatedQuery.objectIds = [featObjId];
//rather then listen for "OnSelectionComplete" we are using the queryRelatedFeatures callback function
facSel.queryRelatedFeatures(relatedQuery, function(relatedRecords) {
var data = []
//if any cmdMain records were found, relatedRecords object will have a property = to the OBJECTID of the clicked feature
//i.e. if cmdMain records are found, true will be returned; and continue with finding cmdSub records
if(relatedRecords.hasOwnProperty(featObjId) == true){
var fset = relatedRecords[featObjId]
var cmdMain = dojo.map(fset.features, function(feature) {
return feature.attributes;
})
//we need to fill an array with the objectids of the returned cmdMain records
//the length of this list == total number of mainCmd records returned for the clicked facility
objs = []
for (var k in cmdMain){
var o = cmdMain[k];
objs.push(o.OBJECTID)
}
//third relationship query to find records related to cmdMain (cmdSub)
var subQuery = new esri.tasks.RelationshipQuery();
subQuery.outFields = ["*"];
subQuery.relationshipId = 2;
subQuery.objectIds = [objs]
subTbl.queryRelatedFeatures(subQuery, function (subRecords){
//subRecords is an object where each property is the objectid of a cmdMain record
//if a cmdRecord objectid is present in subRecords property, cmdMain has sub records
//we no longer need these objectids, so we'll remove them and put the array into cmdsub
var cmdSub = []
for (id in subRecords){
dojo.forEach(subRecords[id].features, function(rec){
cmdSub.push(rec.attributes)
})
}
var j = cmdSub.length;
var p;
var sub_key;
var obj;
if (branchFound == true){
var p1 = "branch";
obj1 = {};
obj1[p1] = [cmdBranch[0].Branches]
data.push(obj1)
}
for (var i=0, iLen = cmdMain.length; i<iLen; i++) {
p = cmdMain[i].ASGMT_Name
obj = {};
obj[p] = [];
sub_key = cmdMain[i].sub_key;
for (var j=0, jLen=cmdSub.length; j<jLen; j++) {
if (cmdSub[j].sub_key == sub_key) {
obj[p].push(cmdSub[j].Long_Name);
}
}
data.push(obj);
}
showWin(data,evtObj) <---this would go away
})
}
//no returned cmdRecords; cmdData not available
else{
p = "No Data Available"
obj = {}
obj[p] = []
data.push(obj)
}
showWin(data,evtObj) <--this would go away
})
})
}
I'd like to have access to data array simply by calling
function findRelatedRecords(feature,evt){
//code pasted above
}
function newfunct(){
var newData = findRelatedRecords(feature,evt)
console.log(newData)
}
is this possible?
thanks!
Edit
Little more explanation.....
I'm connecting an Object event Listener to a Function like so:
function b (input){
dojo.connect(obj, "onQueryRelatedFeaturesComplete", getData);
obj.queryRelatedFeatures(input);
console.log(arr) //<----this doesn't work
}
function getData(relatedFeatData){
var arr = [];
//populate arr
return arr;
}
So when obj.QueryRelatedFeatures() is complete, getData fires; this part works fine, but how to I access arr from function b ?
Post Edit Update:
Due to the way that this event is being hooked up you can't simple return data from it. Returning will just let Dojo call to the next method that is hooked up to onSelectionComplete.
When init runs it is long before findRelatedRecords will ever be executed/fired by the onSelectionComplete event of the well, which is why you were seeing undefined/null values. The only way to work with this sort of system is to either 1) call off to a method like you're already doing or 2) fire off a custom event/message (technically it's still just calling off to a method).
If you want to make this method easier to work with you should refactor/extract snippets of it to make it a smaller function but contained in many functions. Also, changing it to have only one exit point at the end of the findRelatedRecords method will help. The function defined inside of subTbl.queryRelatedFeatures() would be a great place to start.
Sorry, you're kind of limited by what Dojo gives you in this case.
Pre Edit Answer:
Just return your data out of it. Everywhere where there is a showWin call just use this return.
return {
data: data,
evtObj: evtObj
}
Then your newfunct would look like this.
function newfunct(){
var newData = findRelatedRecords(feature,evt);
console.log(newData);
console.log(newData.data);
console.log(newData.evtObj);
}
If you only need that "data" object, then change your return to just return data;.
Also, start using semicolons to terminate statements.

Categories

Resources