Access an unknown Firebase path - javascript

First of all will be easier if you check the Firebase realtime database image:
So with my code I create some "practicas" with an id (152648... in this case) and then, inside that object I create a list of "grupos" (groups). The problem comes here, to do this I use .push(), so Firebase creates a list inside that firebase main node, but the thing is that the 'key' it uses is random, so then, I want to access to the last step called "alumnos", but as I don't know the previous key I can't access there. I tried to use an ID to push the object but it adds the ID and then the key.
My code:
//don't take care about what is values[], grupoList[] and so on
//I just take values from a checkbox on the HTML and I send them to the 'grupo' value of the object 'practica'
addGroup(){
let y=0;
for(let i=0; i<this.values.length; i++){
if(this.values[i] == true){
this.grupoList[y] = this.profiles[i];
y++;
}
}
this.grupo.alumnos = this.grupoList;
this.practica.grupo = this.grupo;
this.practicaService.anyadirGrupos(this.practica);
this.navCtrl.setRoot(VerGruposPage, {'data': this.practica});
}
PracticaService:
//Here is where I work with firebase adding the 'grupo'
public anyadirGrupos(practica){
this.afDB.database.ref('practicas/' + practica.id + '/grupos/').push(practica.grupo);
}
//to access the node 'alumnos' (it doesn't work)
public getAlumnos(practica){
return this.afDB.list('practicas/' + practica.id +'/grupos/' + '../alumnos/')
}
Any idea to access to the last step without knowing the previous one?

You can have two different possible approaches:
1/ Write the "sub-grupos" without an extra key
Which means having a database structure like this:
- practicas
-idPracticas
-grupos
-alumnos
-0 ....
-1 ......
-anotherGroupName
-0 ....
-1 ......
To do that you should use set() instead of push()
2/ Keep your structure and loop over the different child nodes
db.ref('practicas/' + practica.id + '/grupos/').orderByKey().once('value').then(function(snapshot) {
console.log(snapshot.val());
snapshot.forEach(function(childSnapshot) {
console.log(childSnapshot.val());
console.log(childSnapshot.val().alumnos[0]);
console.log(childSnapshot.val().alumnos[1]);
});
});

Related

push only unique values into array

not able to check the unique values log showing all values getting
added to the array
.
var moveToReady = [];
var topLinesRecords = new GlideRecord('x_snc_ms_dynamics_d365_queue');
topLinesRecords.addEncodedQuery('root_element_sys_id=03133e1a1bfe6410f8ca0e16624bcba7');
topLinesRecords.orderByDesc('sys_created_on');
topLinesRecords.query();
while(topLinesRecords.next()){
gs.info(' first record : ' + topLinesRecords.number);
if(moveToReady.indexOf(topLinesRecords.getValue('object_sys_id')) == -1){
moveToReady.push(topLinesRecords.getValue('object_sys_id'));
}
gs.info('array. : ' + moveToReady);
updateRecordtoFail(topLinesRecords);
}
You can use the Set structure from ES6 to make your code faster and more readable:
// Create Set
this.items = new Set();
add(item) {
this.items.add(item);
// Set to array
console.log([...this.items]);
}
you may use array.includes
if (!moveToReady.includes(topLinesRecords.getValue('object_sys_id'))){
moveToReady.push(topLinesRecords.getValue('object_sys_id'));
}
So, some tips to get unique values on ServiceNow:
-GlideRecord has a "getUniqueValue" method
(URL: https://docs.servicenow.com/bundle/paris-platform-administration/page/administer/table-administration/concept/c_UniqueRecordIdentifier.html)
-You can search on your Script Includes a OOB method/function to get only unique values. Search for "utils". Every instance has this, maybe "ArrayUtils".
Hope this information helped!

Data not updating in Vuejs

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);

Using Firebase Push key as Key in Second Push

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);
})

storage.removeItem - how would i use it to remove a multiple items from the value and not the key

I want to be able to delete the value but the not the key from the local storage, any clue?
$(".delete").on("click", function() {
localStorage.removeItem('Property');
});
Try to use setItem() and set the value as null or blank like,
$(".delete").on("click", function() {
localStorage.setItem('Property',"");
});
Updated as per #qutz comment, If you have multiple items then first you need to store values in JSON format then you will be able to parse and splice to remove a single item like,
$(".delete").on("click", function() {
var ar = JSON.parse(localStorage.getItem("Property")),
item='color';// let we wants to remove color property from json array
var index = ar.indexOf(item);
if (index > -1) { // if color property found
ar.splice(index, 1);// then remove it
}
// again set the property without having color
localStorage.setItem("Property", JSON.stringify(ar));
});
Hope this will help you, may be some changes are required as per your logic and method which you've used for it.

Angular2 bind ngModel from ngFor

So I'm rendering my textarea dynamically using ngFor however I'm not sure how I can pass the ngModel to bind it in my function.
<div *ngFor="let inputSearch of searchBoxCount; let i = index" [ngClass]="{'col-sm-3': swaggerParamLength=='3', 'col-sm-9': swaggerParamLength=='1'}">
<textarea name="{{inputSearch.name}}" id="{{inputSearch.name}}" rows="3" class="search-area-txt" attr.placeholder="Search Product {{inputSearch.name}}"
[(ngModel)]="inputSearch.name"></textarea>
</div>
textarea example:
textarea is render based on the length of the response I get from api call in my case searchBoxCount is basically searchBoxCount.length, so if it length is = 1 then it will only render 1 textarea if its 3 then it will show 3 textareas. The objs have different names (example: id/email/whatever), so ngModel is based on the obj name from the json object.
How do I bind inputSearch.name to my function getQueryString()
getQueryString() {
this.isLoading = true;
let idInputValue = inputSearch.name; //bind it here
return "?id=" + idInputValue
.split("\n") // Search values are separated by newline and put it in array collection.
.filter(function(str) {
return str !== ""
})
.join("&id=");
}
Search func where getQueryString() is called
searchProduct() {
let queryString1 = this.getQueryString();
this._searchService.getProduct(queryString1)
.subscribe(data => {
console.log(data);
});
}
I know how to do it if the ngModel is not coming from the ngFor, is there another way to get the value from the textarea without ngModel? maybe that's the only way or if I can still use ngModel.
Summary of current state
First, let me summarize where your data is. You have a list of one or more objects named searchBoxCount. Each of the elements in the list is an object which has a name property, so you could, for example, call let name = this.searchBoxCount[0].name; to get the name of the first object in the list.
In the HTML template you use ngFor to loop through all of the objects in the searchBoxCount list, and in each iteration you assign the object to a local (to the ngFor) variable named inputSearch. You then bind the input from the textarea created in each loop iteration to the name property for that iteration's inputSearch object.
How to get your data
The key here is that the inputSearch is the same Object as is stored in searchBoxCount at some particular index (index 0 for the first object, etc...). So when the ngModel is tied to inputSearch.name it is also bout to searchBoxCount[n].name. External to the ngFor, you would loop through the searchBoxCount list to get each name you need.
As a consequence
Based on the comments on the original post, it sounds like you can have one or
more names that you need to include in the query string output. That means for your getQueryString() to work, you have to loop through the list (or as in this case, let the list loop for us):
getQueryString() {
this.isLoading = true;
let result : string = "?id=";
this.searchBoxCount.forEach(
(inputSearch:any) => { //Not the same variable, but same objects as in the ngFor
result = result + inputSearch.name + "&id=";
});
result = result.slice(0, result.length - 4); //trim off the last &id=
return result;
}
Edit: Multiple different fields with different names
From the comments on this post, it now is clear each inputSearch has its own key to be used in the query string, that is stored in the name property. You need to preserve that name, which means you can't bind the ngModel to it. Otherwise the user will destroy the name by typing in their own text and there will be no way to get the correct key back. To that end, you need to store bind the ngModel to some other property of the inputSearch object. I am going to assume the object has a value property, so it looks like this:
{
name: "id",
value: "33\n44"
}
That is, each inputSearch has a name, and the value will have one or more values, separated by new line. You would then have to change the HTML template to this:
<div *ngFor="let inputSearch of searchBoxCount; let i = index"
[ngClass]="{'col-sm-3': swaggerParamLength=='3', 'col-sm-9':
swaggerParamLength=='1'}">
<textarea name="{{inputSearch.name}}"
id="{{inputSearch.name}}" rows="3" class="search-area-txt"
attr.placeholder="Search Product {{inputSearch.name}}"
[(ngModel)]="inputSearch.value"></textarea>
</div>
Notice that I changed the ngModel from inputSearch.name to inputSearch?.value (the ? allows for null if there is no value to begin with) inputSearch.value. The getQueryString() method then looks something like this:
getQueryString() {
let result:string = "?";
//for each of the input search terms...
this.searchBoxCount.forEach( (inputSearch:any) => {
// first reparse the input values to individual key value pairs
let inputValues:string = inputSearch.value.split("\n")
.filter(function(str) { return str !== "" })
.join("&" + inputSearch.name + "=");
// then add it to the overall query string for all searches
result = result +
inputSearch.name +
"=" +
inputValues +
"&"
});
// remove trailing '&'
result = result.slice(0, result.length - 1);
return result;
}
Note, using RxJs this is probably easier but I am testing vanilla javascript.
Using this, if the user entered two IDs (33 and 44), a single sku, and two emails, the result would be ?id=33&id=24&sku=abc123&email=name#compa.ny&email=an.other#compa.ny

Categories