I'm new to JavaScript and probably trying to emulate Ruby with this. I use StimulusJS, but I think the question is applicable to JS in general.
My goal is to run a method (on button click) which would fetch and display all subcategories of the category from the button. The method/function would first fetch the data and then manipulate the DOM. I wanted to split these into two methods, but when I call the other method (displaySubcategories) from within the first one (getSubcategories) the value of event changes. Same thing with this in the fetch block - I need to assign it first to self to be be later able to related to object itself.
Is there a better way to do this? Like variables accessible to all methods (instance variables in Ruby)? Or I shouldn't be splitting these into two functions at all (not JS way)?
import {Controller} from "stimulus"
export default class extends Controller {
static targets = ["categoryId", "categoryLabel", "subcategoryList", "subcategoryHeader"]
getSubcategories() {
let category_id = event.target.firstElementChild.value
let url = "/api/categories/" + category_id + "/subcategories?levels=1"
let self = this
let e = event
fetch(url)
.then(response => response.json())
.then(json_response => {
self.displaySubcategories(json_response, e)
})
}
displaySubcategories(json_response, event) {
subcategories.forEach(function (subcategory) {
let category_label_copy = this.cloneLabel(current_label, subcategory)
current_label.classList.add("chosen")
subcategory_list.appendChild(category_label_copy)
}, this)
}
}
expenses#getSubcategories">
Related
I have a Store which will be provided to the component. In this Store file, there are several getter function. But I find only this getter function will be executed three times since this.rawMonthlyImpacts will be only changed once when the api get response from backend. I am so confused because other getter function in this file will be only executed once. During every execution, this.rawMonthlyImpacts is always same. Because this function is time-consuming, so I want to figure out why this happens. Hope you can give me some advice. Thanks!
get Impacts(){
const monthlyImpacts = new Map<string, Map<string, number>>();
if (this.rawMonthlyImpacts) {
this.rawMonthlyImpacts.forEach((impact) => {
if (impact.Impact > 0) {
const month = TimeConversion.fromTimestampToMonthString(impact.Month);
const tenantId = impact.TenantId;
const tenantImpact = impact.Impact;
if (!monthlyImpacts.has(month)) {
const tenantList = new Map<string, number>();
monthlyImpacts.set(month, tenantList.set(tenantId, tenantImpact));
} else {
const tenantWithImpactMap = monthlyImpacts.get(month);
if (!tenantWithImpactMap.has(tenantId)) {
tenantWithImpactMap.set(tenantId, tenantImpact);
} else {
tenantWithImpactMap.set(tenantId, tenantWithImpactMap.get(tenantId) + tenantImpact);
}
monthlyImpacts.set(month, tenantWithImpactMap);
}
}
});
}
return monthlyImpacts;
},
Update: I have find that there are other two functions use this.Impacts. If I remove these two functions, the getter function will only be executed only once. I think the getter function uses the cache to store data, so once the data is calculated for the first time, subsequent calls to the getter function should not be re-executed, only the value in the cache needs to be retrieved. So I am very confused about why this getter function will be executed 3 times.
getImpactedTenants(month: string): string[] {
return Array.from(this.Impacts.get(month).keys());
},
get overallMonthlyImpactedTenants(): Map<string, number> {
return new Map<string, number>(
Array.from(this.Impacts)?.map((monthEntries) => {
const month = monthEntries[0];
const impactedTenants = monthEntries[1].size;
return [month, impactedTenants];
})
);
}
Hard to tell what exactly is happening without more context, but remember that with a get function, every single time you reference that property (.Impacts in this case) the get function will be called.
Assuming that each impact stored in this.rawMonthlyImpacts which you loop through is an instance of the class with this getter, then as far as I'm aware, you are calling the get function each time you reference impact.Impacts, such as in the conditional:
if (impact.Impact > 0) {
I might be way off though; I'm unfamiliar with React and so my answer is based only on my experience with vanilla JS.
Let's assume that there is a service will be used for http request calls.And two different components(could be more than two) which will send same request by using same observables via this service.After getting result that should be assigned to global variable(Components have not relationship like parent-child or child-parent).Below I wrote same code block for all components.Is there any better way to write this function once and call by returning same value?
Service
getStudents() {
const requestUrl = this.apiUrl + 'students/';
return this.httpClient.get(requestUrl);
}
Component1
studentList:Student[]=[];
getStudents.subscribe((students:Student[])=>{
this.studentList=students;
//Some operations
})
Component2
studentList:Student[]=[];
getStudents.subscribe((students:Student[])=>{
//Some operations
this.studentList=students;
})
I'm not a fan of global state, but if you want to maintain the same list of students across components using global state, then that state may as well live in the service (Rather than existing in each component separately)
So, for example:
Service
studentList:Student[] = [];
setStudents(students:Student[]) {
this.studentList = students;
// Operations involved with setting students
}
updateStudents() {
const requestUrl = this.apiUrl + 'students/';
return this.httpClient.get(requestUrl).pipe(
tap(this.setStudents)
);
}
Component
ngOnInit(){
this.service.updateStudents().subscribe();
}
You can have an Observable inside your service,
studentsReceived:Subject = new Subject();
on success of getStundent() you can emit next value of studentsReceived.
Now you can subscribe to the studentsReceived inside your components, after the successful API call you will be notified of each of the subscribed components.
studentRecived.subscribe(data=>{ // do some code })
You must call this getStudent() on some higher component like AppComponent.
2 Important things here:
1) If you dont want to repeat the same block of code, then create a method in the service file,
and call it in the component. Something like this:
SERVICE:
makeSubcription(componentVariableName) {
this.yourObservable.subcribe(subscribedValue => {
componentVariableName = subscribedValue;
})
}
In your Component, you can do this:
yourComponentMethod() {
this.service.makeSubscription(this.studentLists);
}
************
2) If you dont want to make a service call too many times, what you can do is,
use Behavior Subject and try to store the values, so that you are subscribing to the observable and not the actual API call. Something like this:
private initialValuesForObservable: YourObjectModel = {}; // or null;
private observableSource: BehaviorSubject<YourObjectModel> =
new BehaviorSubject<YourObjectModel>(this.initialValuesForObservable);
public subscribableObservable: Observable<YourObjectModel> =
this.observableSource.asObservable();
setObservableValue(data: YourObjectModel) {
this.observableSource.next(data);
}
getObservableData() {
return this.subscribableObservable;
}
In your COMPONENT;
this.service.getObservableData().subscribe(latestObservableData => {
this.schoolList = latestObservableData;
});
I have been told my function:
for (const key of Object.keys(temp)) {
this.sessionData.push(temp[key]);
}
Must now use a .map instead,
I have tried this below:
Object.keys(temp).map(function(key) {
this.sessionData[key];
})
But 1 I don't know if it's actually accurate, and also, it cant access the data outside of the scope of the function it is in, here is the whole function below:
public sessionData;
sessionDates(sessionsData) {
const temp = {};
this.sessionData = [];
sessionsData.forEach(session => {
const date = moment(session.startDatetime).format('DDMMYYYY');
if (temp[date]) {
temp[date].push(session);
} else {
temp[date] = [session];
}
});
Object.keys(temp).map(function(key) {
this.sessionData[key];
})
TRYING TO USE THIS BELOW... session data is undefined, it can't access out of the scope?
Object.keys(temp).map(function(key) {
this.sessionData[key];
})
But this works..
for (const key of Object.keys(temp)) {
this.sessionData.push(temp[key]);
}
So this new .map method can't access anything out of its scope.. sigh!
If anybody can help that would be amazing! Thanks!
In Javascript all functions can access variables from outside (called "higher scope") - it's one of the strengths of the language and is called "capture".
The reason your code is failing is because it's using this.sessionData inside a function declaration, which cases problems because this in javascript is... somewhat complex. But you don't need it!
You also need to make sure you return the value you want to output. Here's how I would write it:
this.sessionData = Object.keys(temp).map(key => temp[key]);
In Javascript I have an array of clients. Each client is an object that has inside an array of vehicles which are also objects. Now I need to get another array of objects named trabajos that should be nested inside the vehicles. All should be stored in the localStorage, but I am not sure how I should start getting the trabajos inside the vehicles. Here is how I gotten the first two parts.
function getClientes(){
let listaClientes = JSON.parse(localStorage.getItem('listaClientesLS'));
let clientes = [];
if (listaClientes == null) {
clientes = [];
} else{
listaClientes.forEach(obj =>{
let objCliente = new Cliente(obj.nombre, obj.apellido1, obj.apellido2, obj.cedula, obj.telefono, obj.email);
obj.listaVehiculos.forEach(objVehiculoTemp => {
let objVehiculo = new Vehiculo(objVehiculoTemp.matricula,objVehiculoTemp.marca,objVehiculoTemp.modelo,objVehiculoTemp.anno,objVehiculoTemp.capacidad,objVehiculoTemp.kilometraje);
objCliente.agregarVehiculo(objVehiculo);
});
clientes.push(objCliente);
})
}
return clientes;
}
The github repository (the question is not minimal) suggests jobs ("trabajos") for a vehicle should be stored in the listaTrabajos array property of a vehicle object, placed there by the add job method (agregarTrabajo) of a the vehicle class, called from the job registration function regTrabajo.
But the vehicle class does not define the agregarTrabajo method. I expect regTrabajo throws an error when called. (Note errors on the console should be solved before anything else! You can ask a question about an error if you can't solve it.)
Add the agregarTrabajo method to the Vehiculo class, e.g.
class Vehiculo {
constructor(....
//... constructor function
}
agregarTrabajo (pObjTrabajo) {
this.listaTrabajos.push(pObjTrabajo);
}
}
Now the list of jobs can be accessed using the listaTrabajos property of the objVehiculoTemp argument of the function processing each vehicle in the post. I don't know what processing is required:
obj.listaVehiculos.forEach(objVehiculoTemp => {
let objVehiculo = new Vehiculo(objVehiculoTemp.matricula,objVehiculoTemp.marca,objVehiculoTemp.modelo,objVehiculoTemp.anno,objVehiculoTemp.capacidad,objVehiculoTemp.kilometraje);
// process objVehiculoTemp.listaTrabajos
objVehiculo.listaTrabajos = objVehiculoTemp.listaTrabajos; // FOR EXAMPLE
objCliente.agregarVehiculo(objVehiculo);
});
I am trying to structurise JS code using Revealing Prototype Pattern.
My basic usecase is: Create different types of entities like Person, Technology. Each entity has its own tags. In order to get these tags i make an ajax call which returns an object of tags. I want to access this object in my implementation. But i am not sure how to do it in right way.
My attempt is as follows:
var Entity= function (url) {
this.url = url; /*variable that can be shared by all instances*/
var entityTags;
};
Entity.prototype = function () {
var create = function (type, values) {
//code for creating
}
var update = function (type, values) {}
var tags = function () {
AjaxCall(' ', this.url, {data:data}, 'callbackAfterGetTags', '');
callbackAfterGetTags=function(responseFromAjax)
{
entityTags=responseFromAjax.tagsReturned; //how to access this entityTags in my implementation
}
};
return {
createEntity: create,
getTagsEntity: tags
};
My Implementation
var myEntity = new Entity(url);
myEntity.getTagsEntity();
Ajax call returns object successfully but i am not sure how to access the object inside tags functions in a right way. Any suggestions? This is my first trial to use OO style in JS. Let me also know if i am right track or not.