I have been trying to solve this problem, but there is probably something of Immutable.js that I don't catch. I hope somebody can help me to understand.
I have a test like this:
import {List, Map} from 'immutable';
import {expect} from 'chai';
import {setInitial,
addAtListOfMembers,
removeAtListOfMembers
} from '../src/core';
describe('removeAtListOfMembers', () => {
it('remove a member to the list of members', () => {
const state = Map({
removing: 3,
infos : Map(),
members: Map({
1:Map({
userName:'René',
date:'12/02/2016'
}),
2:Map({
userName:'Jean',
date:'10/03/2016'
}),
3:Map({
userName:'Elene',
date:'05/01/2016'
})
})
});
const nextState = removeAtListOfMembers(state);
expect(nextState).to.equal(Map({
infos : Map(),
members: Map({
1:Map({
userName:'René',
date:'12/02/2016'
}),
2:Map({
userName:'Jean',
date:'10/03/2016'
})
})
}));
});
});
});
...witch tests this funtion:
export function removeAtListOfMembers(state) {
const members = state.get('members');
const removing = state.get('removing');
return state
.deleteIn(['members'], removing)
.remove('removing');
}
but it doesn't work. I have tryed everything.... changing the line to make it work, but I don't get the item number 3 deleted.
What's wrong? Somebody to help me?
This should work:
export function removeAtListOfMembers(state) {
const members = state.get('members');
const removing = state.get('removing');
return state
.deleteIn(['members', String(removing) ])
.remove('removing');
}
Your code has two issues:
deleteIn takes a single keyPath argument, which in your case is [ 'members' ]. The second argument (removing) is ignored, so the result is that the entire members map is deleted; instead, removing should become part of the key path.
removing is a Number, but because you're creating a Map from a JS object, its keys will be String's (this is mentioned in the documentation as well):
Keep in mind, when using JS objects to construct Immutable Maps, that JavaScript Object properties are always strings
So you need to convert removing to a String when passing it to deleteIn.
Related
I have a basic database that essentially stores an array of product id's underneath a user. The user can select products to add to the array so it makes sense to use 'arrayUnion' so I avoid reading and re-writing the array constantly, however, I keep getting the error *"Property 'firestore' does not exist on type 'FirebaseNamespace'."
I've followed the documentation found at: https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array but I fear I'm still using it incorrectly.
My code for updating the array is:
addToPad(notepadName: string){
const updateRef = this.db.collection('users').doc(this.activeUserID).collection('notepads').doc(notepadName);
updateRef.update({
products: firebase.firestore.FieldValue.arrayUnion(this.productId)
});
}
First you need to import firestore:
import { firestore } from 'firebase/app';
Then you will be able to use arrayUnion:
addToPad(notepadName: string){
const updateRef = this.db.collection('users').doc(this.activeUserID).collection('notepads').doc(notepadName);
updateRef.update({
products: firestore.FieldValue.arrayUnion(this.productId)
});
}
import { arrayUnion } from '#angular/fire/firestore'
const path = `ai/${videoId}/panel-operation/${id}`
const myDoc: AngularFirestoreDocument<any> = this.afs.doc<any>(path)
const promise: Promise<void> = myDoc.update({ auxPanelSelections: arrayUnion({auxPanel: 'clip', operation: 'replace'}) }).catch((err: any) => {
console.error(`oopsie - ${err.message}`)
return null
})
auxPanelSelections is an array within the myDoc document
Note that the above code also works perfectly fine with arrayRemove
I cannot find the #angular/fire docs for arrayUnion but the generic docs are here
I have multiple utility methods like
export const makeTextUtils = ({ U }) =>
Object.freeze({
hello: () => "Hello World!",
lorem: () => "Lorem ipsum..."
)};
these child utils can reference each other
export const makeOutputUtils = ({ U }) =>
Object.freeze({
logHello: () => console.log(U.txt.hello())
)};
Now I want to expose the Utils in a utils.js and inject the parent Method into all children
import { makeTextUtils } from './text';
import { makeOutputUtils } from './output';
// dependency injections
const textUtils = makeTextUtils({ U });
const outputUtils = makeTextUtils({ U });
// building parent util method
export const U = Object.freeze({
txt: textUtils,
out: outputUtils
});
I've tried various ways of importing the U at the top of the main file and switching up the order within the file, but nothing seems to do the trick.
Any help would be much appreciated.
First declare the U object at the top so that you can pass it down to the other functions. Then, after U gets properties assigned to it, you can freeze it:
import { makeTextUtils } from "./text";
import { makeOutputUtils } from "./output";
export const U = {};
// dependency injections
const textUtils = makeTextUtils({ U });
const outputUtils = makeOutputUtils({ U });
// building parent util method
Object.assign(U, {
txt: textUtils,
out: outputUtils
});
Object.freeze(U);
U.out.logHello();
https://codesandbox.io/s/happy-benz-cu3n5
The passing of U inside an object and immediately destructuring it in the utility functions doesn't seem to do anything - unless there's a particular reason for that, feel free to just pass the U object alone (and to use the U parameter alone).
I have an array that I'm converting to Ember array with A() as I want to use some of Ember array methods, like filterBy(), but it's not producing the result I want. What is the proper way to convert a vanilla array into an Ember array?
Ember:
import Component from '#ember/component';
import { computed } from '#ember/object';
import { A } from '#ember/array';
export default Component.extend({
movieGenreIds: computed.map('movies', function(movie, index) {
return movie.genre_ids;
}),
genresNames: computed('movieGenreIds', 'allGenres', function() {
let genresArray = A(this.get('genres')); // <--- conversion here
this.get('movieGenreIds').forEach((movieGenreId, movieGenreIndex) => {
console.log('MOVIE_GENRE_IDS!!!', genresArray);
console.log('FILTERBY ID^^^', genresArray.filterBy('id', movieGenreIds.toString())); // <-- not returning desired results
});
}),
});
Ember route (data is from themoviedb api and models represent the data structure in the json provided):
import Route from '#ember/routing/route';
import RSVP from 'rsvp'
export default Route.extend({
model() {
return RSVP.hash({
movies: this.store.findAll('movie'),
genres: this.store.findAll('genre'),
})
.then(data => data);
},
});
Okay, first .then(data => data); makes literally nothing. Just remove this.
Next if you don't disable the prototype extension for Arrays you dont need to convert normal arrays to ember arrays. so replace this:
let genresArray = A(this.get('genres'));
with this:
let genresArray = this.get('genres');
or this in ember 3.1+:
let genresArray = this.genres;
Now an interesting question is, what is genre_ids on the movie model? I strongly assume its a computed property that returns an array. But some code would help.
However your dependency key for movieGenreIds is wrong. You probably should do this:
movieGenreIds: computed.map('movies.#each.genre_ids', function(movie, index) {
return movie.genre_ids;
}),
However your actual problem is now probably that this will probably return an array of arrays. So something like this:
[[1,2],[3],[],[4,5]]
Now you do .forEach on it, however movieGenreId will now probably still be an array. Next you do movieGenreId.toString() (you actually do movieGenreIds.toString(), but I assume this is a typo, because this wouldn't make sense because you don't use movieGenreId inside the loop then). However doing .toString() on an array will probably not give you the desired result - an id.
And so probably your fix is to fix the movieGenreIds CP (the code is for ember 3.1+):
movieGenreIds: computed('movies.#each.genre_ids', function() {
return this.movies
.map(m => m.genre_ids)
.reduce((a, b) => [...a, ...b]);
}),
I have this reducer:
import Immutable, { Map, Record} from 'immutable'
const tags = Map({ primary: ['tag1','tag2','tag3'], secondary: [] });
export default function (state=tags, action) {
switch (action.type) {
case 'ADD_PRIMARY_TAG': {
//not sure about this:
var oldArr = state.get('primary');
var newArr = oldArr.push(action.payload)
var newState = tags.set('primary', newArr);
return newState;
}
default:
console.log("Default Tags Reducer.");
return state;
}
}
However, I am not sure about this. So i have an immutable Map and there I have an array called primary, which contains some tags. Now I would like to add a tag to the existing array. So I get the current array with state.get('primary');, I push something into a copy of it, and then I set the new state to the new array and return it.
I don't see where I am going wrong, or whether I am using Immutable the wrong way maybe.
When i run this I get the following error:
Uncaught TypeError: oldArr.push is not a function
at exports.default (index_bundle.js:44134)
at combination (index_bundle.js:32954)
at dispatch (index_bundle.js:18293)
at index_bundle.js:44171
at Object.addPrimaryTag (index_bundle.js:32975)
at PhraseView.tagsSubmit (index_bundle.js:33813)
at Object.ReactErrorUtils.invokeGuardedCallback (index_bundle.js:6380)
at executeDispatch (index_bundle.js:4920)
at Object.executeDispatchesInOrder (index_bundle.js:4943)
at executeDispatchesAndRelease (index_bundle.js:3439)
at executeDispatchesAndReleaseTopLevel (index_bundle.js:3450)
Is the way I am using Arrays here (in the context of ImmutableJS) maybe completely wrong? Shoudl the Arrays in my Immutable Map be other immutable objects? Or how does this error come about?
You need to use concat() instead of push().
The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
Change:
var newArr = oldArr.push(action.payload);
To:
var newArr = oldArr.concat([action.payload]);
I just tried the following and it passes:
import Immutable, {Map} from 'immutable'
describe('immutable tests', () => {
it('Immutable array', () => {
const tags = Map({ primary: ['tag1','tag2','tag3'], secondary: [] });
let oldArr = tags.get('primary');
oldArr.push('blabla')
expect(oldArr).toHaveLength(4)
})
})
Are you sure the incoming state is what you expect it to be? Maybe it has some other structure, from the stacktrace it's not very clear what invokes your reducer
I am trying to clean up my mapStateToProps, using ramda I can just use r.pick to return a curried function by passing in my desired attrs like so:
const mapStateToProps = R.pick(['board', 'nextToken'])
Redux will then pass in the state to the returns function.
What I want to do is modify the state first, because my state is an immutable map, so i need to pass pick my state cast as plan js.
I can do this with the following but i am convinced that there is a cleaner way of doing this, functionally. Is it something simple that anybody can see?
import {Map} from 'immutable'
import R from 'ramda'
const immutable_state = Map({
name: 'alice',
age: 30
})
const pick_from_immutable = (attrs, obj) =>
// accepts a list of attrs
// returns a function that accepts an immutable and returns its attrs defined by attrs, also be nice to just return the value if called with an immutable 2nd arg
obj ?
R.pick(attrs, obj.toJS()) :
(imm) => R.pick(attrs, imm.toJS())
const pick_age_from_immutable = pick_from_immutable(['age'])
console.log(pick_age_from_immutable(immutable_state)) // -> { age: 30 }
const mapStateToProps = pick_age_from_immutable
I prefer to use Immutablejs functions for these kinds of things. It works fine with nested items. But I can see a use case if I am picking age (or some property) in multiple components then I might create a utility function for it.
Example:
var a = Map({
b: Map({
c: 10
})
})
// Get value of c in nested a object. Set not found value to null.
var valueOfC = a.getIn(['b', 'c'], null)