Immutable.js: create a list with one element - redux

I have an Immutable record as follows:
export const Pane = new Record({
'id': null,
'editors': new List(),
});
where editors is a list of id strings associated with a pane.
In my Redux reducer code I have:
const pane = new Pane({
id: action.payload.id,
editors: new List(action.payload.editorId),
});
When the reducer is constructing the Pane, however, it creates the editors field as an Immutable List, as intended, but rather than having a single string, each character in the string becomes an individual item in the list.
How do I construct an Immutable List with a single object, as intended?

you just need to pass the List constructor an array containing your string(s).
assuming action.payload.editorId is a string, and not an array of strings;
e.g.
const pane = new Pane({
id: action.payload.id,
editors: new List([action.payload.editorId]),
});
For more information, check the documentation at https://immutable-js.github.io/immutable-js/docs/#/List/List

Related

using watch function w/prop in Vue3 composition api

I have a component that renders a table of Inventoried computer equipment. Here is the relevant code for initial render:
let oEquiptByType = reactive({
Laptop: [],
iPad: [],
"Document Camera": [],
"Overhead Projector": [],
Chromebook: [],
Desktop: [],
MacBook: [],
Scanner: [],
});
// ======== Props =========== //
const props = defineProps({
propFormData: {},
});
// Now let's use Stein to retrieve the SS data
// eslint-disable-next-line no-unused-vars
const fetchSheetsData = function () {
const store = new SteinStore(
"https://api.steinhq.com/v1/storages/618e81028d29ba2379044caa"
);
store
.read("HS - Classrooms")
.then((data) => {
scrapDataHSClassrooms.value = data;
emptyRowsRemoved.value.forEach((item) => {
// Let's construct an object that separates equipment by type
// Check if property exists on oEquiptByType object
const exists = Object.prototype.hasOwnProperty.call(
oEquiptByType,
item["Equipment"]
);
// If item(row) is good lets push the row onto the corresponding Object Array
// in oEquiptByType. This will construct an object where each object property corresponds
// to an equipment category. And each oEquiptByType entry is an array where each array
// element is a row from the SS. e.g., oEquiptByType["Laptop"][3] is a row from
// SS and is a laptop.
if (exists) {
oEquiptByType[item["Equipment"]].push(item);
}
});
})
.catch((e) => {
console.error(e);
failure.value = true;
});
};
// =============== Called on component mount =============================== //
onMounted(fetchSheetsData);
The initial render is fine. Now I have a watcher on the prop so when someone submits a new item for the inventory I push that data onto the corresponding object array (ie, a laptop would be pushed onto the oEquiptByType[props.propFormData.Equipment] via oEquiptByType[props.propFormData.Equipment].push(props.propFormData);
// ================================================================ //
// ======================= Watch effects ========================== //
// ================================================================ //
watch(props.propFormData, () => {
// Push the submitted form item onto the reactive
// oEquiptByType object array. This update of Vue state
// will then be injected into DOM and automagically update browser display.
oEquiptByType[props.propFormData.Equipment].push(props.propFormData);
});
This works fine for the first item I add to backend as you can see here with original and then adding first item :
and after first item added (a laptop)
Notice the oEquiptByType[props.propFormData.Equipment] has the new item added. Great.
But now when I add a second item (a MacBook) is added this is resulting state:
Notice the Macbook array has been updated but also the Laptop array's last item has been overwritten with the Mac book entry??? And this behavior continues for any additional items added from a user. I have read docs over and do not see anything that would explain this behavior. I'm hoping maybe someone with more than my limited experience with Vue can help me out. Any additional info needed please let me know. Thanks...
Update:
Put a JSON.Stringify in watch function
Update two:
here is lineage of prop.FormData-
we start in form-modal and emit the form data like:
emit("emiterUIUpdate", formAsPlainObject);
then catch the data in the parent App.vue:
<FormModal
v-show="isModalVisible"
#close="closeModal"
#emiterUIUpdate="updateUI"
>
<DisplayScrap :propFormData="formData" />
const formData = reactive({});
// Method to be called when there is an emiterUIUpdate event emiited
// from form-modal.vue #param(data) is the form data sent from the
// form submission via the event bus. We will then send this data back
// down to child display-scrap component via a prop.
const updateUI = (data) => {
Object.assign(formData, data);
};
and then as posted previous in display-scrap.vue the prop propFormData is defined and watched for in the watch function. hope that helps..
It seems like the watch is getting triggered more often than you expect.
Might be that changes to props.propFormData are atomic and every incremental change triggers changes to the props, which in turn triggers the watch.
Try console logging the value of props.propFormData with JSON.stringify to see what changes are triggering it.
What happens here:
Your form modal emits the emiterUIUpdate event on Ok or Save (button)
Parent takes the object emitted and use Object.assing to copy all properties of emitted object to a formData reactive object. Instead of creating completely new object, you are just replacing the values of all properties of that object all and over again
The formData object is passed by a prop to child component and whenever it changes, it is pushed to target array
As a result, you have a multiple references to same object (formData hold by a parent component) and all those references are to same object in memory. Every Object.assign will overwrite properties of this object and all references will reflect those changes (because all references are pointing to the same object in memory)
Note that this has nothing to do with Vue reactivity - this is simple JavaScript - value vs reference
There is no clear answer to what to do. There are multiple options:
Simplest (and not clearest)
just do not use Object.assign - create new object every time "Save" is clicked
change formData to a ref - const formData = ref({})
replace the value of that ref on emiterUIUpdate event - formData.value = { ...data }
your watch handler in the child will stop working because you are watching props in a wrong way - instead of watch(props.propFormData, () => { use watch(() => props.propFormData, () => {
Better solution
the data should be owned by parent component
when modal emits new data (Save), Parent will just add the newly generated object into a list
share the data with DisplayScraps component using a prop (this can be a simple list or a computed creating object similar to oEquiptByType)

How can I update nested List in Map that's all inserted to a List? Eg. List<Map<String, dynamic>>

I am trying to update or add to the end of the Lists that are in a Map and all inserted into a List that contains those maps. The List name is 'classes', what I have tried doing was using dot notation, so classes.index.example, but that doesn't work. Why...let's say I have two indexes in the list, If I go ahead and update index 0, the 'questions' and 'answers' will get inserted into that correct index, But for some reason, it will delete index 1 and any other that was created. It's as if It's overwriting all the data, but I don't understand why, I am not using 'setData()' Also, if I leave 'title' out, that too will get deleted??
Future updattingUserData(int index, List<dynamic> question, List<dynamic> answer, String title) async {
return await _collref.document(uid).updateData({
"classes.$index.questions": FieldValue.arrayUnion(question),
"classes.$index.answers": FieldValue.arrayUnion(answer),
//"classes.$index.title": title
});
}
Firestore doesn't have the capability of changing an array element knowing only its index within an array field. What you will have to do is read the document, modify the classes array in memory, then update the entire classes array field back to the document.

Redux: build a reducer for a subelement of a main reducer

Possibly a silly question. I have a Redux reducer built from an Immutable.js record:
export const Editors = new Record({
'editors': new List(), // this is a list of Editor items below
})
export const Editor = new Record({
'id': null,
'state': EditorState.createEmpty(),
'annotations': new List(),
});
I've structured things this way, because I expect the application that I am building to have many editors.
However, I want to write an updateState action that updates the state of an individual editor. I imagine that it would be much easier to write this if I was able to write reducer code that acts on a specific Editor rather than on the list of Editors in my Editors reducer. Is there a way to write reducer code so that I can just update state on a field in an individual editor, rather than finding the editor in the list, removing it, updating it, re-inserting it, etc.?
export const Editors = new Record({
'editors': new Map(), // this is a list of Editor items below
})
export const Editor = new Record({
'id': null,
'state': EditorState.createEmpty(),
'annotations': new List(),
});
Then updating an editor becomes:
const updateEditorState = (Editors, id, editorState) =>
Editors.setIn(['editors', id, 'state']¸editorState)
Your naming is a bit weird, by the way. I would lower case Editors and Editor.

i want binding context list but not working

I custom object:
I want to fill the object but not working.
My Code:
var BindingMainCat = context.binding.get("value");
BindingMainCat.set("AllPaymentsList[0].id", invoice_id);
BindingMainCat.set("AllPaymentsList[0].amount", inputs[i].value);
Please help me. Thanks
Accessors in coachview bound variables do not support complex navigation with dots and brackets. Once you obtain list itself you can use add(), remove() and put() operations on list items. You are allowed to use get() and set() on attributes of the list but not items themselves - see IBM documentation for "List operations" of binding. For example:
var list = this.context.binding.get("value");
var item0 = list.get(0);
item0.set("id", invoice_id);
item0.set("amount", inputs[i].value);
Also you can replace element of list with new one:
var list = this.context.binding.get("value");
var newItem0 = {id: invoice_id, amount: inputs[i].value};
list.put(0, newItem0);
Note that the final content of the list is the same but change notifications are different. In first example there will be two events about item properties changed, in second example there will be single event about list change - see bind() and bindAll() documentation.
Be also warned that on each step you can encounter empty values, if coachview does not have any binding then this.context.binding is undefined, if bound variable is null then this.context.binding.get("value") is null, if list has no items then list.get(0) is undefined etc.

How to pass a collection name as an argument into methods in Meteor?

Thanks my poor English skill, I have o express my idea by these code below..
Friendly edit:
I am trying to write a generalized confirmAndRemoveCollection method which takes in the collectionName and itemId, and I would like to perform operations on this collection. Since collectionName is a string, I wouldn't be able to perform DB operations on it. Could someone please suggest how I could use the collection name to get access to the actual collection object.
confirmAndRemoveCollection:(collectionName,itemId)->
check(itemId,String)
check(collectionName,String)
sweetAlert({
title:"confirm"
text:"blabla"
type:"info"
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "delete"
cancelButtonText: "cancel"
closeOnConfirm: false,
},(isConfirm)->
if isConfirm
collectionName.remove(itemId)
else
return
swal(
'success'
"selected item deleted"
"success"
)
The variable collectionName is a string object, so you won't be able to call MongoDB methods on it.
One way to accomplish your task is to create an object that maps the string name to the collection object.
For example:
Posts = new Mongo.Collection('posts');
Comments = new Mongo.Collection('comments');
Collections = {
'Posts': Posts,
'Comments': Comments
};
Then you could do something like this in your code
if isConfirm
Collections[collectionName].remove(itemId)
Just to add an alternative here (even though the question is really old): you can pass the collection itself as an argument and it will work.
As the collection is an Object, when you pass it as an argument it will be passed "by reference" and you will be able to call its methods.
Following the example by #FullStack (which also works, of course):
Posts = new Mongo.Collection('posts');
Comments = new Mongo.Collection('comments');
const collectionRemove = (collection, id) => {
const count = collection.remove(id);
console.log(`Removed ${count} items with id ${id} from collection ${collection._name}`)
}
And then do something like:
collectionRemove(Posts, 1);
collectionRemove(Comments, 24);

Resources