Let me start by explaining my use case, I have a fee_map attr on data which is a list of objects which contain data about the fee student has to pay, balance, payment mode etc, and a computed property, lets say 'updateOptions' which returns a list of objects with id and text suitable to populate select2(payment mode), now whenever user does something, updateOptions will be called. and on some other user actions Program will choose the selected option and set it on fee_map, structure for fee_map is as below.
data: {
fee_map: {
1: {
details:{
1: {
option_selected: "1",
}
}
// other_attr
},
2: {
details:{
1: {
option_selected: "2",
}
}
// other_attr
},
}
I have a method UpdateSelected, which will update the selections, where I loop through the fee_map by fetching keys and looping with forEach. and then set the selected option as below
var fee_map = this.fee_map;
t_keys = Object.keys(fee_map);
t_keys.forEach(function(t){
f_keys = Object.keys(fee_map[t].details);
f_keys.forEach(function(f){
fee_map[t].details[f].option_selected = "2";
});
});
Now, when I update the option_selected from here, my fee_map is not updated with new value. What am I doing wrong here?
Your object of objects is one level deeper than you were iterating over. So when you have fee_map[t].details[f], f is the key "details"and details["details"] is undefined. You need to do a foreach for the details object as well as can be seen here.
https://jsfiddle.net/50wL7mdz/78427/
var fee_map = this.fee_map;
t_keys = Object.keys(fee_map);
t_keys.forEach(function(t){
f_keys = Object.keys(fee_map[t]);
f_keys.forEach(function(f){
d_keys = Object.keys(fee_map[t][f]);
d_keys.forEach(function(d){
fee_map[t].details[d].option_selected = "5";
});
});
});
After lot of struggle I found code is updating data too quickly in a loop, but somehow vue is detecting changes only on the next tick, so I had to call a self invoking function as a wrapper to Vue.nextTick to preserve the context and update the selected_option inside the Vue.nextTick callback and that is working perfect.
working code below
(function(fee, ref){
console.log("Registering vue tick");
Vue.nextTick(function(){
console.log("Vue ticked, updating selected option");
fee.option_selected = ref;
});
})(fee, ref);
Related
So I am trying to delete the field Notes[1], selectedNote has a value of the selected array I need to delete.
window.addEventListener("load", function load(event) {
document.getElementById("deleteNotes").onclick = function() {
console.log("you did click atleast");
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
let user = firebase.auth().currentUser;
let userInfo = db.collection("Users").doc(user.uid);
userInfo.get().then(function(doc) {
if (doc.exists) {
let selectedNote = document.getElementById("noteSelect")
.selectedIndex;
console.log(selectedNote);
var cityRef = db.collection("Users").doc(user.uid);
cityRef.update({
Notes: FieldValue.delete().arrayRemove(selectedNote)
});
}
});
}
});
};
});
So I am trying to use this
cityRef.update({
Notes: FieldValue.delete().arrayRemove(selectedNote)
});
to delete the selectedNote which is the array 1 for example inside of Notes. I don't want the entire Notes field deleted but just the selected array inside the Notes field. For some reason I am struggling to get it working. Any help would be appreciated <3
Firebase arrays doesn't use a indexes for their arrays, instead they require the value of the array entry. this does mean you can't get away with duplicated data.
you will have to fetch the array "Notes" first and return its value from its index
Although, I have heard that the arrays can come back out of order because they don't rely on an index.
change this line;
Notes: FieldValue.delete().arrayRemove(selectedNote)
to this;
Notes: FieldValue.delete().arrayRemove(doc.data().Notes[selectedNote])
I would recommend you pull the value of the array from a stored variable locally to ensure the values match pre and post edit.
I built a custom component that filters an array of objects. The filter uses buttons, sets from active to non-active and allows more than one option on/off at the same time.
StackBlitz of my attempt - https://stackblitz.com/edit/timeline-angular-7-ut6fxu
In my demo you will see 3 buttons/options of north, south and east. By clicking on one you make it active and the result should include or exclude a matching "location" either north, south and east.
I have created my methods and structure to do the filtering, I'm struggling with the final piece of logic.
So far I have created a method to create an array of filtered locations depending on what the user clicks from the 3 buttons.
Next this passes to my "filter array" that gets the logic that should compare this filtered array against the original to bring back the array of results that are still remaining.
Its not quite working and not sure why - I originally got this piece of functionality working by using a pipe, but fore reasons do not want to go in that direction.
//the action
toggle(location) {
let indexLocation = this.filteredLocations.indexOf(location);
if (indexLocation >= 0) {
this.filteredLocations = this.filteredLocations.filter(
i => i !== location
);
} else {
this.filteredLocations.push({ location });
}
this.filterTimeLine();
}
// the filter
filterTimeLine() {
this.filteredTimeline = this.timeLine.filter(x =>
this.contactMethodFilter(x)
);
}
//the logic
private contactMethodFilter(entry) {
const myArrayFiltered = this.timeLine.filter(el => {
return this.filteredLocations.some(f => {
return f.location === el.location;
});
});
}
https://stackblitz.com/edit/timeline-angular-7-ut6fxu
Sorry for my expression but u have a disaster in your code. jajaja!. maybe u lost that what u need but the logic in your functions in so wrong. comparing string with objects. filter a array that filter the same array inside... soo u need make a few changes.
One:
this.filteredLocations.push({location});
Your are pushing object. u need push only the string.
this.filteredLocations.push(location);
Two:
filterTimeLine() {
this.filteredTimeline = this.timeLine.filter(x =>
this.contactMethodFilter(x)
);
}
in this function you filter the timeLine array. and inside of contactMethodFilter you call filter method to timeLine again....
See a functional solution:
https://stackblitz.com/edit/timeline-angular-7-rg7k3j
private contactMethodFilter(entry) {
const myArrayFiltered = this.timeLine.filter(el => {
return this.filteredLocations.some(f => {
return f.location === el.location;
});
});
}
This function is not returning any value and is passed to the .filter
Consider returning a boolean based on your logic. Currently the filter gets undefined(falsy) and everything would be filtered out
I have an array of objects in the scope ($scope.contracts). When I run my function, I pass in a contract. I Want it to iterate through $scope.contracts and find the one with the same .CONT_ORDNO. Then I want to set this contract to the contract passed in.
$scope.mergeContract = function (contractThatsReplaced) {//clicked 2nd new
angular.forEach($scope.contracts, function (con) {
if (con.CONT_ORDNO == contractThatsReplaced.CONT_ORDNO) {
con = $scope.contractToMerge;
}
});
};
Here is me trying to do it, it does everything I want except that only the con variable is changed, the actual contract that's an array location in $scope.contracts is not being updated. How do I make sure it is?
Note I don't want to remove and replace the contract, I want to update it.
Edit: I updated the if statement to have two (==). This doesn't fix it. It still is only setting the con variable correctly, not the $scope.contracts array location.
Assigning to con only changes the local variable. Try this instead:
$scope.mergeContract = function (contractThatsReplaced) {//clicked 2nd new
angular.forEach($scope.contracts, function (con, i) {
if (con.CONT_ORDNO == contractThatsReplaced.CONT_ORDNO) {
$scope.contracts[i] = $scope.contractToMerge;
}
});
};
Also, you need to change = to == in the if statement.
need == in the if condition
if (con.CONT_ORDNO == contractThatsReplaced.CONT_ORDNO) {
I'm trying to add two related items to my Firebase database. I want to push one item, then get that item's newly created key and use it as the key for the second item in a different tree. I've tried querying the database to get the last key created and using it as the key for the second push, but it's still just generating a new key for it. Here's the code that I'm using:
save: function() {
if (this.$.document.isNew && (this.editableCard.title || this.editableCard.body)) {
return this.$.document.save(this.cardsPath).then(function() {
this.$.document.reset();
var sceneRef = firebase.database().ref().child(this.cardsPath);
var scene = sceneRef.orderByKey().limitToLast(1);
var sceneKey = scene.key;
this.$.document.save('/documents/', sceneKey);
}.bind(this));
}
return Promise.resolve();
}
(I'm using Polymer, and my starting point is the note-app demo for Polymerfire).
Any ideas on how I can retrieve the new key of the first push and use it for the second push? Thanks!
EDIT
I found the answer in Firebase's documentation for Reading and Writing to the database for Web. Link
push() returns a DatabaseReference immediately. You can ask that reference what its key is, using getKey(), then use that string to update another location in your database.
You can access the key property on the original database reference and use that as the key for the second one, like so:
let firstObjRef = firebase.database().ref('/first/path/).push(firstObj, (error) => {
videoObj["roomUploadedTo"] = this.roomName;
var updateObj = {};
updateObj[videoObjRef.key] = videoObj;
firebase.database().ref('/second/path/').update(updateObj).then( (e) => {
console.log('update went through. booyah! ' + e);
})
My aim is to replace the teacher-id(f_teacher) of one outputted array with the teacher name of another array. I wrote a custom filter, that should do the job:
angular.module('core')
.filter('replaceId', function () { //filter, which replaces Id's of one array, with corresponding content of another array
return function (t_D, s_D, t_prop, s_prop) { //data of target, data of source, target property, source property
var replacment = {};
var output = [];
angular.forEach(s_D, function (item) {
replacment[item.id] = item[s_prop]; //replacment - object is filled with 'id' as key and corresponding value
});
angular.forEach(t_D, function (item) {
item[t_prop] = replacment[item[t_prop]]; //ids of target data are replaced with matching value
output.push(item);
});
return output;
}
});
I use a 'ng-repeat' like this:
<tr ng-repeat="class in $ctrl.classes | filter:$ctrl.search | replaceId:$ctrl.teachers:'f_teacher':'prename' | orderBy:sortType:sortReverse">
<td>{{class.level}}</td>
<td>{{class.classNR}}</td>
<td>{{class.f_teacher}}</td>
</tr>
But it only outputs an empty column. Now the strange thing: If I follow the steps with the debugger, it works for the first time the filter is performed. But when it is performed a second time it outputs an empty column.
I noticed that the returned object of the filter overwrites the $ctrl.classes - array, but normally this shouldn't be the case?
Here is a plnkr:
https://plnkr.co/edit/EiW59gbcLI5XmHCS6dIs?p=preview
Why is this happening?
Thank you for your time :)
The first time through your filter the code takes the f_teacher id and replaces it with the teacher name. The second time through it tries to do the same thing except now instead of getting a teachers ID in f_teacher it finds the teacher's name so it doesn't work. You could fix it by making a copy of the classes instead of modifying them directly. e.g.
angular.forEach(t_D, function (item) {
var itemCopy = angular.copy(item);
itemCopy[t_prop] = replacment[itemCopy[t_prop]];
output.push(itemCopy);
});
https://plnkr.co/edit/RDvBGITSAis3da6sWnyi?p=preview
EDIT
Original solution will trigger an infinite digest because the filter returns new instances of objects every time it runs which will cause angular to think something has changed and retrigger a digest. Could you just have a getter function that gets a teachers name instead of using a filter?
$scope.getTeacherName = function(id) {
var matchingTeachers = $scope.teachers.filter(function(teacher) {
return teacher.id == id;
})
//Should always be exactly 1 match.
return matchingTeachers[0].prename;
};
And then in the HTML you could use it like
<tr ng-repeat="class in classes">
<td>{{class.level}}</td>
<td>{{class.classNR}}</td>
<td>{{getTeacherName(class.f_teacher)}}</td>
</tr>
https://plnkr.co/edit/gtu03gQHlRIMsh9vxr1c?p=preview