how to use shift on behaviorSubject? - node.js

I have a class with BehaviorSubject:
export class WordsService {
private words = new BehaviorSubject<WordType[]>([]);
It was fed with subscription:
init() {
this.databaseService.fetchWords().subscribe(
(listaWords: WordType[]) => {
this.words.next(listaWords);
},
errors => console.error('err'),
() => console.log('suceed')
)
}
And as I'm refactoring this code:
private fetchWord(): void{
this.word = this.wordService.getWords().shift();
}
I'm trying to get variable word to have data with .shift so it can take one element from observable at once, and when it will take all elements fetching's done.

It looks like you are trying to transform the results from a WordType[] to a single WordType.
You can do this by applying the map() operator like this:
init(){
this.databaseService.fetchWords().pipe(
map(words => words.shift())
)
.subscribe(
(listaWords: WordType[]) => {
this.words.next(listaWords);
},
errors => console.error('err'),
() => console.log('suceed')
)
}
However, you don't actually need a BehaviorSubject to do this, you can simply declare your observable directly from your service call:
public word: Observable<WordType> = this.databaseService.fetchWords().pipe(
map(words => words.shift()),
catchError(error => console.log(error))
);
Now, the word observable will only emit the value you are interested in.
This allows you to possibly use the async pipe in your template to manage the subscription and not need to do it yourself in the controller.

I would do it without shifting.
Imagine you have any observable value, for each emition of this observable value you want to pull a word. In my example this observable value is a page click.
Then you can do something like this:
const clicked$ = fromEvent(document, 'click');
const words$ = of(['AA', 'BB', 'CC', 'XX']);
const wordToPrint$ = zip(
clicked$,
words$.pipe(concatAll()),
).pipe(
map(([,word]) => word),
);
wordToPrint$.subscribe(console.log);
See stackblitz: https://stackblitz.com/edit/rxjs-ep1k3v?file=index.ts

Related

Tiptap how to create a paragraph (p) on Shift-Enter, instead of a br?

Using TipTap, I'm trying to avoid adding a <br />, but create a <p></p> instead, with the focus inside that <p>|</p> when the user hit shift-Enter but I can't make it work.
Here's what I did so far:
new (class extends Extension {
keys () {
return {
'Shift-Enter' (state, dispatch, view) {
const { schema, tr } = view.state
const paragraph = schema.nodes.paragraph
console.log(tr.storedMarks)
const transaction = tr.deleteSelection().replaceSelectionWith(paragraph.create(), true).scrollIntoView()
view.dispatch(transaction)
return true
}
}
}
})()
How can I do this?
I don't know if this is still relevant but as I was looking for the same thing, I found two ways to make this work.
NOTE:
I'm using tiptap v2, if that's not a problem, then:
I overrode the HardBreak extension, since it's the one that use the Shift-Enter keybinding. It looks something like;
const CustomHardBreak = HardBreak.extend({
addKeyboardShortcuts() {
return {
"Mod-Enter": () => this.editor.commands.setHardBreak(),
"Shift-Enter": () => this.editor.commands.addNewline(),
};
},
});
And used it like so;
editor = new Editor({
extensions: [
customNewline,
CustomHardBreak,
]
});
Use the default editor command createParagraphNear. E.g this.editor.commands.createParagraphNear()
I tried creating a custom extension from your code and ended up with something similar to the command above, i.e;
export const customNewline = Extension.create({
name: "newline",
priority: 1000, // Optional
addCommands() {
return {
addNewline:
() =>
({ state, dispatch }) => {
const { schema, tr } = state;
const paragraph = schema.nodes.paragraph;
const transaction = tr
.deleteSelection()
.replaceSelectionWith(paragraph.create(), true)
.scrollIntoView();
if (dispatch) dispatch(transaction);
return true;
},
};
},
addKeyboardShortcuts() {
return {
"Shift-Enter": () => this.editor.commands.addNewline(),
};
},
});
And added this as an extension in my editor instance.
PS:
They both work, almost exactly the same, I haven't found a difference yet. But there's somewhat of a 'catch' if you would call it that; Both these methods don't work on empty lines/nodes, a character has to be added before the cursor for it to work, any character, even a space.
In TipTap 2.0 I am able to use this custom extension:
const ShiftEnterCreateExtension = Extension.create({
addKeyboardShortcuts() {
return {
"Shift-Enter": ({ editor }) => {
editor.commands.enter();
return true;
},
};
},
});
To make shift + enter behave like enter.
In my case I actually wanted enter to do something different. So I use prosemirror events to set a ref flag on whether shift was pressed. Than I check that flag under the "Enter" keyboard event -- which could be triggered normally or through the shift + enter extension.

Redux Toolkit - Slice utility methods

I'm building a React app with redux-toolkit and I'm splitting my store into some slices with redux-toolkit's helper function createSlice.
Here it is a simple use case:
const sidebar = createSlice({
name: "sidebar",
initialState:
{
menus: {}, // Keep track of menus states (guid <-> open/close)
visible: true
},
reducers:
{
show(state, action)
{
state.visible = action.payload.visible;
},
setMenuOpen(state, action)
{
const { id, open } = action.payload;
state.menus[id] = open;
return state;
}
}
});
export default sidebar;
Everything works fine until I "add" actions (that change the store) to the slice but consider your team looking for an utility function "getMenuOpen": this method doesn't change the store (it's not an action and cannot be addeded to reducers object). You can of course read directly the data from the store (state.menus[<your_id>]) but consider a more complex example where manipulating the data requires some library imports, more complex code, etc...I want to modularize/hide each slice as much as possible.
Actually I'm using this workaround:
const sidebar = createSlice({ /* Same previous code... */ });
sidebar.methods =
{
getMenuOpen: (state, id) => state.menus[id]
};
export default sidebar;
The above code allows importing the slice from a component, mapStateToProps to the redux store, and invoke the utilty function getMenuOpen like this:
import sidebar from "./Sidebar.slice";
// Component declaration ...
const mapStateToProps = state => ({
sidebar: state.ui.layout.sidebar,
getMenuOpen(id)
{
return sidebar.methods.getMenuOpen(this.sidebar, id);
}
});
const mapDispatchToProps = dispatch => ({
setMenuOpen: (id, open) => dispatch(sidebar.actions.setMenuOpen({id, open}))
});
The ugly part is that I need to inject the slice node (this.sidebar) as fist param of getMenuOpen because it's not mapped (as for actions with reducers/actions) automatically from redux-toolkit.
So my question is: how can I clean my workaround in order to automatically map the store for utility functions? createSlice doesn't seem to support that but maybe some internal redux's api could help me in mapping my "slice.methods" automatically to the store.
Thanks

Angular 7 HttpClient get - can you access and process the return object?

I know this is a general question but I have exhausted google and tried many approaches.Any feedback is appreciated.
The HTTPClient is Angular 5+ so it returns an object created from the response JSON data. I get a massive JSON response from an endpoint I have no control over and I want to use about 20% of the response in my app and ignore the rest.
I am really trying hard to avoid using a series of templates or export objects or whatever and trying to force this massive untyped Observable into a typed object with hundreds of fields many being Arrays. All I need for the app is just a Array of very small objects with 3 fields per object. The 3 fields are all over within the JSON response and I want to map them to my object .map only seems to work when you are using the full response object and I can't find an example where .map does custom work besides in the case where you are mapping a few fields to 1 object and I am trying to map to an Array of my small objects.
UPDATED
Basically I want this service to return an object of Type DislayData to the module that subscribes to it but I get just an Object back. This is not what I ultimately need to do but if I can prove I can map the body of the response to my needed return type I can then start to break down the response body and return an Array of the Type I really need based on my silly DisplayData object. Thanks again!
export interface DislayData {
body: any;
}
...
export class DataService {
constructor(private http: HttpClient) { }
/** GET data from the black box */
getData(): Observable<DislayData> {
return this.http.get<HttpResponse<any>>(searchUrl, { observe: 'response' })
.pipe(
map(res => {
return res.body as DislayData;
}
tap(res => console.log(//do stuff with entire respoonse also)),
catchError(err => this.handleError(err)));
}
private handleError(error: HttpErrorResponse) {
...
Do you know the structure of the answering object?
If yes, you can do something like this:
item$ = new BehaviorSubject<any>({});
item = {
foo: 'a',
bar: 'b',
iton: [1, 2, 3],
boo: {
far: 'c'
}
};
logNewItem() {
this.item$
.pipe(
map(response => {
if (response.foo
&& response.iton
&& response.iton.length >= 3
&& response.boo
&& response.boo.far) {
let newItem = {
foo: response.foo,
iton2: response.iton[2],
far: response.boo.far
};
console.log(newItem); // output: Object { foo: "a", iton2: 3, far: "c" }
}
})
)
.subscribe();
this.item$.next(this.item);
}
Basically, you can simply make sure the properties exist, call them directly and map them to a better fitting object.
I heavily recommend creating an interface for the object you're receiving and an interface or class for the object you're mapping to. In that case you can also write the code more compact like this:
[...]
map(response: MyAPIResponse => {
let newItem = new NewItem(response);
console.log(newItem); // output: Object { foo: "a", iton2: 3, far: "c" }
}
})
[...]
class NewItem {
foo: string;
iton2: string;
far: string;
constructor(apiResponse: MyAPIResponse) {
//Validate parameter first
this.foo = apiResponse.foo;
this.iton2 = apiResponse.iton[2];
this.far = apiResponse.boo.far;
and make your code a lot more readable.

React-native and Redux healthy way to call actions on props change

I've been using react-native with redux for a while, and the way i learn to call actions when something change on prop is using the componentWillReceiveProps, but when I use it I need to pass between if's and some times it goes to the wrong if, then I need to add more stuff to prevent it.
Here's an example I have done. I know this is not the best way to do it, but it is what I could think of.
componentWillReceiveProps(newProps) {
if(Object.keys(newProps.selected_product).length > 0) {
if(Object.keys(this.props.current_location).length > 0 || Object.keys(newProps.current_location).length > 0) {
this._handleNextPage(2);
this.props.verifyProductById(newProps.selected_product, newProps.current_location, this.props.token);
} else {
this.props.statusScanner(false);
this._handleNextPage(1);
}
} else if(Object.keys(newProps.historic_product_confirm).length > 0) {
if(newProps.historic_product_confirm.location._id == newProps.current_location._id)
this.props.handleModalConfirmPrice(!this.props.modal_confirmPrice_status)
} else if(newProps.scanResult != "") {
this.props.statusScanner(false);
if(Object.keys(newProps.current_location).length > 0) {
this._handleNextPage(2);
} else {
this._handleNextPage(1);
}
} else {
this._handleNextPage(0);
}
}
What I need is a healthy way to call my actions when the props change.
Edit:
Here i have the full OfferScene and an action file example:
OfferScene:
https://gist.github.com/macanhajc/0ac98bbd2974d2f6fac96d9e30fd0642
UtilityActions:
https://gist.github.com/macanhajc/f10960a8254b7659457f8a09c848c8cf
As mentioned in another answer, componentWillReceiveProps is being phased out, so I would aim for trying to eliminate it where possible. You'll be future-proofing your code and keeping your component logic more declarative and easy to reason about. As someone who has been responsible for (and been frustrated by) lifecycle method abuse like this, here are some things that have helped me.
Remember that when using redux-thunk, along with passing dispatch as the first argument, you can also pass getState as the second. This allows you to access state values in your action logic instead of bringing them into your component's props and adding clutter. Something like:
export const ExampleAction = update =>
(dispatch, getState) => {
const { exampleBool } = getState().ExampleReducer
if (exampleBool) {
dispatch({
type: 'UPDATE_EXAMPLE_STATE',
update
})
}
}
Using async/await in action logic can be a lifesaver when your action depends upon fetched results from an API call:
export const ExampleAction = () =>
async (dispatch, getState) => {
const { valueToCheck } = getState().ExampleReducer
, result = await someAPICall(valueToCheck)
.catch(e => console.log(e))
if (result.length > 0) {
dispatch({
type: 'UPDATE_EXAMPLE_STATE',
update: result
})
}
}
For cases where your component's rendering behavior depends upon certain state values after your state has been updated, I highly recommend reselect. A very basic example would be something like:
component.js
import React, { Component, Fragment } from 'react'
import { connect } from 'react-redux'
import { shouldDisplayItems } from '../selectors'
import MyListviewComponent from './myListview'
class ItemList extends Component {
render() {
const { shouldDisplayItems, items } = this.props
return (
<>
{shouldDisplayItems && <MyListviewComponent items={items} />}
</>
)
}
}
const mapStateToProps = ({ ListItems }) => shouldDisplayItems(ListItems)
export default connect(mapStateToProps)(ItemList)
selectors.js:
(Assuming your ListItems reducer has the params items and visibilityFilter)
import { createSelector } from 'reselect'
export const shouldDisplayItems = createSelector(
[state => state],
({ items, visibilityFilter }) => {
return {
shouldDisplayItems: visibilityFilter && items.length > 0,
items
}
}
)
I should mention that another option would be using higher-order components, but it can be tricky to use this approach before having a good grasp on how to keep too much imperative logic out of your components (I learned this the hard way).
I agree with #AnuragChutani and #Goldy in terms of clarity of the code; break it down some more into more components or functions.
Now after some review of your componentWillReceiveProps function, it is definitely not specific enough to narrow down exactly which prop changes. If any connected redux variable changes, the componentWillReceiveProps function will be invoked each time.
So e.g. if 'token' or 'selected_product' updates, componentWillReceiveProps will be triggered, even though you did not want it to trigger for token updates.
You can use a comparison for a specific variable update in the props.
E.g Using lodash
if(!_.isEqual( nextProps.selected_product, this.props.selected_product ))
// if props are different/updated, do something
Secondly, you can call actions/callbacks in your actions to narrow down navigation.
E.g.
takePicture = (camera, options){
...
//on success
dispatch(handleModalConfirmPrice())
...
}}

Passing path parameters in axios

I am using Axios with NodeJs and trying to pass path parameters in axios.get() method. For example, if URL is url = '/fetch/{date}', I want to replace {date} with the actual date while calling axios.get(url).
I went through the source code on Github and StackOverflow, but couldn't find any method.
Is it possible to keep URLs with parameters as a placeholder and replace them while actually calling the get method of Axios?
Axios doesn't have this feature and it looks like the team don't want to add it.
With credit to previous responders for inspiration, to me this seems like the solution closest to what you (and me) are looking for:
1 - Where you want to store all your URLs and their parameters, define them as functions which use a template string to return the composed URL:
export var fetchDateUrl = (date) => `/fetch/${date}`;
If you need any type-specific formatting of the value being concatenated into the URL, this function is a good place to do it.
2 - Where you want to make the request, call the function with the correct parameters:
import { fetchDateUrl } from 'my-urls';
axios.get(fetchDateUrl(someDateVariable))...;
Another variation, if you really like the idea of naming the parameters at the call site, you can define the URL function to destructure an object like this:
var fetchDateUrl = ({date}) => `/fetch/${date}`;
which you'd then use like this:
axios.get(fetchDateUrl({date: someDateVariable}));
Use template strings
url = `/fetch/${date}`
Or just tag it on
url = '/fetch/'+ date
I think using axios interceptors is better to do this :
//create your instance
const instanceAxios = axios.create({
baseUrl: 'http://localhost:3001'
]);
instanceAxios.interceptors.request.use(config => {
if (!config.url) {
return config;
}
const currentUrl = new URL(config.url, config.baseURL);
// parse pathName to implement variables
Object.entries(config.urlParams || {}).forEach(([
k,
v,
]) => {
currentUrl.pathname = currentUrl.pathname.replace(`:${k}`, encodeURIComponent(v));
});
const authPart = currentUrl.username && currentUrl.password ? `${currentUrl.username}:${currentUrl.password}` : '';
return {
...config,
baseURL: `${currentUrl.protocol}//${authPart}${currentUrl.host}`,
url: currentUrl.pathname,
};
});
// use like :
instanceAxios.get('/issues/:uuid', {
urlParams : {
uuid: '123456789'
}
})
For typescript users, you will need to add this, in one of your .d.ts
declare module 'axios' {
interface AxiosRequestConfig {
urlParams?: Record<string, string>;
}
}
( this is a POC, not really tested, doesn't hesitate if you see something wrong )
You can use template strings ie:
let sellerId = 317737
function getSellerAnalyticsTotals() {
return axios.get(`http://localhost:8000/api/v1/seller/${sellerId}/analytics`);
}
Given some API /fetch/${date} you likely want to wrap your axios call in a function.
const fetchData = (date) => axios.get(`/fetch/${date}`);
fetchData(dateObject.toFormat('yyyy-mm-dd'))
.then(result => { ... });
This requires the calling code to format date correctly however. You can avoid this by using a DateTime library that handles date string parsing and do the format enforcement in the function.
const fetchData = (date) => axios.get(`/fetch/${date.toFormat('yyyy-mm-dd')}`);
fetchData(dateObject)
.then(result => { ... });
you can do like this:
getProduct = (id) => axios.get(`product/${id}`);
I always do it like this:
const res = await axios.get('https://localhost:3000/get', { params: { myParam: 123 } });
I find this to be much clearer than template strings.
More explanation here

Resources