Dynamic getters in pinia - Vue 3 - javascript

There is code for Vue 2 with Vuex 3.4:
getLink: (state, getters) => rel => {
let link = state.data?.getLinkWithRel(rel);
let stac = getters[rel];
let title = STAC.getDisplayTitle([stac, link]);
if (link) {
link = Object.assign({}, link);
link.title = title;
}
else if (stac instanceof STAC) {
return {
href: stac.getAbsoluteUrl(),
title,
rel
};
}
return link;
},
This code should be ported to Vue 3 with pinia. Did the following:
getLink: (state) => (rel) => {
let link = state.data?.getLinkWithRel(rel);
let stac = state[rel];
let title = STAC.getDisplayTitle([stac, link]);
if (link) {
link = { ...link };
link.title = title;
}
else if (stac instanceof STAC) {
return {
href: stac.getAbsoluteUrl(),
title,
rel
};
}
return link;
},
The stac variable contains either null or undefined values.
When using version 1, stac contains the data returned by the called getters[rel] function.
How do I write or force pinia to give data?
Additional information:
rel - contains a string with one of the getter names (parent or collection).
getter-parent:
parent: (state) => state.getStacFromLink('parent'),
Where getStacFromLink is also a getter.
getter-collection:
collection: (state) => state.getStacFromLink('collection'),
It looks like the parent or collection getters that are called do not get the state of the Store.

Related

How do I avoid infinite loop when using Redux state in useEffect dependency array?

I'm trying to figure out why my useEffect function ends up in an infinite loop.
I have two variables that are hooked into my Redux store:
const vehicles: AllVehiclesCollection = useSelector((state: ReduxState) => state.claims?.vehicles ?? {});
const properties: AllPropertiesCollection = useSelector((state: ReduxState) => state.claims?.properties ?? {});
and I have an action that is dispatched to the store that updates these only after a user clicks a button.
I have a useEffect that will trigger based on either of these variables changing.
useEffect(() => {
let fullVehicleList: DropdownData[] = getFormattedVehicleListForDisplay();
let fullPropertyList: DropdownData[] = getFormattedPropertyListForDisplay();
let fullList = fullVehicleList.concat(fullPropertyList);
if (fullList.length > 0) {
setVehiclesAndPropertiesList(fullList);
} else {
setVehiclesAndPropertiesList(null);
}
}, [vehicles, properties]);
Nowhere in this code are the vehicles or properties variables changed or any actions dispatched that would change the Redux state.
getFormattedVehicleListForDisplay function:
const getFormattedVehicleListForDisplay = () => {
let list: DropdownData[] = [];
if (Object.keys(vehicles).length > 0) {
let thisPolicysVehicles = [];
if (vehicles !== null) {
const key = `${selectedPolicy.symbol}${selectedPolicy.number}`;
thisPolicysVehicles = vehicles[key];
}
if (thisPolicysVehicles && thisPolicysVehicles.length > 0) {
thisPolicysVehicles.forEach((vehicle: VehicleInformation) => {
if (vehicle.vehicleMake !== OTHER_VEHICLE) {
list.push({
label: formatVehicleForDisplay(vehicle),
value: { ...vehicle, type: 'V' },
});
} else {
list.push({ label: vehicle.vehicleMake, value: {} });
}
});
}
}
return list;
};
getFormattedPropertyListForDisplay function:
const getFormattedPropertyListForDisplay = () => {
let list: DropdownDataOMIG[] = [];
if (Object.keys(properties).length > 0) {
let thisPolicysProperties = [];
if (properties !== null) {
const key = `${selectedPolicy.symbol}${selectedPolicy.number}`;
thisPolicysProperties = properties[key];
}
if (thisPolicysProperties && thisPolicysProperties.length > 0) {
thisPolicysProperties.forEach((property: LocationInformation) => {
if (property.locStreet1 !== OTHER_PROP) {
list.push({
label: formatPropertyForDisplay(property),
value: { ...property, type: 'P' },
});
} else {
list.push({ label: property.locStreet1, value: {} });
}
});
}
}
return list;
};
For reference, the data in vehicles and properties is a set of key-value pairs where the key is a unique identifier of a given account number and the value is an array of vehicle/property objects for that account.
Any idea why this goes into an infinite loop when using Redux state in the dependency array? Is there a different way to use Redux state in a dependency array? Thanks!
When using
const vehicles = useSelector((state: ReduxState) => state.claims?.vehicles ?? {});
Each time this is triggered, and you don't have vehicles in your store, you return a new object {}. and {} === {} // false
So ain your useEffect dependency array, it's each time a new Object, so useEffect is triggered.
So either remove your || {} in your selector (because null === null & undefined === undefined) or consider moving to useShallowSelector as explained in react-redux documentation

How to write JavaScript code that allows for defaults to be overrided

I would like to use this text-highlighting library in my Vue project. Here's an example from their website of how it can be used:
import TextHighlighter from '#perlego/text-highlighter';
import { isDuplicate } from './utils';
import highlightsApi from './services/highlights-api';
class ArticleView {
constructor(data) {
this.data = data;
const pageElement = document.getElementById("article");
this.highlighter = new TextHighlighter(
pageElement,
{
version: "independencia",
onBeforeHighlight: this.onBeforeHighlight,
onAfterHighlight: this.onAfterHighlight,
preprocessDescriptors: this.preprocessDescriptors,
onRemoveHighlight: this.onRemoveHighlight
});
}
onBeforeHighlight = (range) => {
return !isDuplicate(range)
}
onRemoveHighlight = (highlightElement) => {
const proceed = window.confirm("Are you sure you want to remove this highlight?");
return proceed;
}
preprocessDescriptors = (range, descriptors, timestamp) => {
// Add an ID to the class list to identify each highlight
// (A highlight can be represented by a group of elements in the DOM).
const uniqueId = `hlt-${Math.random()
.toString(36)
.substring(2, 15) +
Math.random()
.toString(36)
.substring(2, 15)}`;
const descriptorsWithIds = descriptors.map(descriptor => {
const [wrapper, ...rest] = descriptor;
return [
wrapper.replace(
'class="highlighted"',
`class="highlighted ${uniqueId}"`
),
...rest
];
});
return { descriptors: descriptorsWithIds, meta: { id: uniqueId } };
}
onAfterHighlight = (range, descriptors, timestamp, meta) => {
highlightsApi.saveBatch(meta.id, descriptorsWithIds)
.then((result) => {
// Do something with the highlights that have been saved.
})
.catch((err) => console.error(err));
}
render = () => {
// Code that takes the data for the article and adds it to the DOM
// based on a html template here.
}
}
Using the above example, I would like to setup the highlighter (similar to the above code, but in a different file, for example ./utils/highlighter.js) with all the default options I want (onBeforeHighlight, onRemoveHighlight, etc.), and then be able to import it from there and override the options for which I don't want to use the defaults, so it looks something like this in the importing file:
import highlighter from "../utils/highlighter.js";
const overridingOptions = {
onAfterHighlight: (range, descriptors, timestamp, meta) => {
console.log(range, descriptors, timestamp, meta);
}
};
const target = document.getElementsByClassName("testme")[0];
highlighter(target, overridingOptions);
For some reason, I am not able to understand how to modify the ArticleView example to fit my needs, so I think I need to see this done once. How should the code in ./utils/highlighter.js look to make this possible?

unable to select all checkboxes in tree using angular2-tree on init

Objective : i have a button named "feed data" so when ever i click it the data will be loaded i mean the tree with checkboxes here my requirement is when ever i click it along with data all the check boxes have to be checked on init i tried using
this.treeComp.treeModel.doForAll((node: TreeNode) => node.setIsSelected(true));
but it is not working below is my code
click(tree: TreeModel) {
this.arrayData = [];
let result: any = {};
let rs = [];
console.log(tree.selectedLeafNodeIds);
Object.keys(tree.selectedLeafNodeIds).forEach(x => {
let node: TreeNode = tree.getNodeById(x);
// console.log(node);
if (node.isSelected) {
if (node.parent.data.name) //if the node has parent
{
rs.push(node.parent.data.name + '.' + node.data.name);
if (!result[node.parent.data.name]) //If the parent is not in the object
result[node.parent.data.name] = {} //create
result[node.parent.data.name][node.data.name] = true;
}
else {
if (!result[node.data.name]) //If the node is not in the object
result[node.data.name] = {} //create
rs.push(node.data.name);
}
}
})
this.arrayData = rs;
tree.selectedLeafNodeIds = {};
}
selectAllNodes() {
this.treeComp.treeModel.doForAll((node: TreeNode) => node.setIsSelected(true));
// firstNode.setIsSelected(true);
}
onTreeLoad(){
console.log('tree');
}
feedData() {
const results = Object.keys(this.data.info).map(k => ({
name: k,
children: this.data.info[k].properties
? Object.keys(this.data.info[k].properties).map(kk => ({ name: kk }))
: []
}));
this.nodes = results;
}
feedAnother() {
const results = Object.keys(this.dataa.info).map(k => ({
name: k,
children: this.dataa.info[k].properties
? Object.keys(this.dataa.info[k].properties).map(kk => ({ name: kk }))
: []
}));
this.nodes = results;
}
onActivate(event) {
this.selectedDataList.push(event.node.data);
console.log(this.selectedDataList)
}
onDeactivate(event) {
const index = this.selectedDataList.indexOf(event.node.data);
this.selectedDataList.splice(index, 1);
console.log(this.selectedDataList)
}
below is my stackblitz https://stackblitz.com/edit/angular-hrbppy
Use updatedata and initialized event to update the tree view to check all checkboxes.
app.component.html
<tree-root #tree *ngIf ="nodes" [nodes]="nodes" [options]="options" [focused]="true"
(initialized)="onTreeLoad()"
(updateData)="updateData()"
(select)="onActivate($event)"
(deselect)="onDeactivate($event)">
</tree-root>
It'll initiate tree-root component only if nodes variable is available,
then in the initialized and updateData event call selectAllNodes method to select all checkboxes.
app.component.ts
updateData() {
this.selectAllNodes();
}
onTreeLoad(){
this.selectAllNodes();
}
Refer to this slackblitz for working example.
just, in your function feed data call to your function this.selectAllNodes() enclosed in a setTimeout. You can see your forked stackblitz
setTimeout(()=>{
this.selectAllNodes()
})
NOTE: I see in your code you try to control in diferents ways the items selected. I simplified using a recursive function.
In this.treeComp.treeModel.selectedLeafNodeIds we have the items that are changed, so
getAllChecked()
{
const itemsChecked=this.getData(
this.treeComp.treeModel.selectedLeafNodeIds,null)
console.log(itemsChecked);
}
getData(nodesChanged,nodes) {
nodes=nodes||this.treeComp.treeModel.nodes
let data: any[] = []
nodes.forEach((node: any) => {
//in nodesChanged we has object like {1200002:true,123132321:false...}
if (nodesChanged[node.id]) //can be not changed, and then it's null because
//it's not in object or can be changed to false
data.push({id:node.id,name:node.name})
//or data.push(node.name); //if only need the "name"
if (node.children)
data=[...data,...this.getData(nodesChanged,node.children)]
}
);
return data
}
Updated I updated the function getData to include the "parent" of the node, but looking the code of #Raghul selvam, his function like me more than mine.
getData(nodesChanged,nodes,prefix) {
nodes=nodes||this.treeComp.treeModel.nodes
let data: any[] = []
nodes.forEach((node: any) => {
if (nodesChanged[node.id])
data.push(prefix?prefix+"."+node.name:node.name)
if (node.children)
data=[...data,...this.getData(nodesChanged,node.children,prefix?prefix+"."+node.name:node.name)]
}
);
return data
}
And call it as
this.getData(this.treeComp.treeModel.selectedLeafNodeIds,null,"")
You could add this in your onTreeLoad function. You could add a boolean flag(treeLoaded) for tracking if the tree has loaded or not.
onTreeLoad(tree){
this.selectAllNodes();
this.treeLoaded = true;
}

push added object to new array when calling getItems()

I am trying to display(log) the items that I added using the addItems() function when I call(log) the getItems() function..
console.log(cart.addItem("ITEMMSSS", 100, 10)) << puts out
ShoppingCart { itemName: 'ITEMMSSS', quantity: 100, price: 10 }
as expected
but the console.log(cart.getItems()) puts out -1-
when I console.log(this.addedItems) it logs out -undefined-(twice)
I don't understand why I don't have access to the returned value from the
addItem() function.
class ShoppingCart {
constructor(itemName, quantity, price) {
this.itemName = itemName
this.quantity = quantity
this.price = price
}
addItem(...items) {
const addedItems = new ShoppingCart(...items)
return addedItems
}
getItems(addedItems) {
const el = []
const selected = this.addedItems
const newArr = el.push(selected)
return newArr
}
clear(...item) {
// return items.slice(0, ...items).concat(items.slice(...items + 1))
}
clone(...items) {
// console.log(this)
// copiedCart.map((item) => {
// return item
// })
}
}
FIXed the issue,
class ShoppingCart {
constructor(items) {
this.items = []
}
addItem(name, quantity, pricePerUnit) {
const shopCart = this.items.push({
name: name,
quantity: quantity,
pricePerUnit: pricePerUnit
})
return shopCart
}
getItems(...items) {
const displayItems = this.items
return displayItems
}
clear(...items) {
const emptyCart = this.items.length = []
return emptyCart
}
clone(...items) {
const copyCart = new ShoppingCart()
copyCart.items = JSON.parse(JSON.stringify(this.items))
return copyCart
}
}
//
// const cart1 = new ShoppingCart('banana', 12, 23)
// const cart2 = cart1.clone()
// //
// console.log(cart2)
// //
module.exports = ShoppingCart;
But can't seem to get an immutable copy of the shoppingCart <--
fixed issue after reading about deep copying
clone(...items) {
const copyCart = new ShoppingCart()
copyCart.items = JSON.parse(JSON.stringify(this.items))
return copyCart
}
Couple ideas for author to think about:
1) addItem should be a function of an existing instance of ShopppingCart. Creating a new object inside addItems should not be necessary. I only know of returning a value from a setter to be usually only done for "fluent" setters practices so that you can chain them together. But that would be returning the current object.
2) getItems should usually not perform any logic. Usually getters return the current state of a variable / object member.
To address authors direct question:
You are returning the addItems object from the function but not storing it.
Try:
cart = cart.addItem("ITEMMSSS", 100, 10)

Change the values of all nested objects Immutable.js

reducer looks like this
import {ADD_TASK, EDIT_TASK, CHECK_TASK, CHECK_ALL_TASK} from '../constants';
import {OrderedMap, Record} from 'immutable';
const TaskRecord = Record({
id: null,
text: null,
isChecked: false
});
const ReducerState = Record({
entities: new OrderedMap({})
});
const defaultState = new ReducerState();
export default (tasksState = defaultState, action) => {
const {type, payload, randomId} = action;
switch (type) {
case ADD_TASK:
return tasksState.setIn(['entities', randomId], new TaskRecord({...payload.task, id: randomId}));
case CHECK_ALL_TASK:
return tasksState.map(entities => {
return entities.map((task) => {
task.set('isChecked', true);
});
});
};
return tasksState;
};
How change all isChecked in TaskRecord that in entities? I wrote CHECK_ALL_TASK, but it gives an error (tasksState undefined)
When you want to set a new value for a property based on it's current one, .update is what you want to use.
When you want to make a change to each item in a collection, .map is what you want to use.
In your case, you want to set a new value for the property entities by modifying each entity in the collection.
This means you want to use update and map:
function checkAllBoxes(taskState) {
return taskState.update('entities', entities =>
entities.map(entity =>
entity.set('isChecked', true)));
}
const TaskRecord = Immutable.Record({
id: null,
text: null,
isChecked: false
});
const ReducerState = Immutable.Record({
entities: new Immutable.OrderedMap({})
});
const taskState = new ReducerState({
entities: Immutable.OrderedMap({
a: new TaskRecord(),
b: new TaskRecord(),
c: new TaskRecord()
})
});
function checkAllBoxes(taskState) {
return taskState.update('entities', entities =>
entities.map(entity =>
entity.set('isChecked', true)));
}
console.log(checkAllBoxes(taskState))
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/4.0.0-rc.9/immutable.js"></script>
wrote this function, but can it be made as something simpler?
function checkAllCheckboxes(state) {
let newState = state;
for (let id of state.entities) {
newState = newState.setIn(['entities', id[0], 'isChecked'], true);
}
return newState;
}

Categories