so i am trying to create a wrapper for mapbox-gl-draw in my kotlin react project.
my starter wrapper looks like this
#JsName("default")
external fun mapboxDraw()
i am trying to base it off of:
TS:
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/mapbox__mapbox-gl-draw/index.d.ts
JS:
https://github.com/mapbox/mapbox-gl-draw/blob/f1c3d595828880b383ec88403b849163cc376bc1/index.js
when i try and use it the browser keeps telling me
mapBoxDraw is not a function
i eventually have to use it in kotlin js code based off this
type DrawControlProps = ConstructorParameters<typeof MapboxDraw>[0] & {
position?: ControlPosition;
onCreate?: (evt: {features: object[]}) => void;
onUpdate?: (evt: {features: object[]; action: string}) => void;
onDelete?: (evt: {features: object[]}) => void;
};
export default function DrawControl(props: DrawControlProps) {
useControl<MapboxDraw>(
() => new MapboxDraw(props),
({map}: {map: MapRef}) => {
map.on('draw.create', props.onCreate);
map.on('draw.update', props.onUpdate);
map.on('draw.delete', props.onDelete);
},
({map}: {map: MapRef}) => {
map.off('draw.create', props.onCreate);
map.off('draw.update', props.onUpdate);
map.off('draw.delete', props.onDelete);
},
{
position: props.position
}
);
return null;
}
DrawControl.defaultProps = {
onCreate: () => {},
onUpdate: () => {},
onDelete: () => {}
};
but i need to get the mapboxdraw wrapper working first... What am i doing wrong.
Related
I'm currently working on a movie database with graphql and mongodb. When a new review is added I want to push the review id immediately to the reviews array of movie entity. I understand that I have to use transactional management. I'm trying to do it like this, but I'm getting this error : throw new MongooseError('Callback must be a function, got ' + callback);
this is the mutation where I'm trying to implement it.
import Review from "../../db/models/ReviewModel.js";
import Movie from "../../db/models/MovieModel.js";
import mongoose from "mongoose";
const generateReviewModel = () => ({
queries: {
getAll: () => Review.find({}),
getReviewByID: (id) => Review.findOne({ _id: id }),
getAllHavingKey: (keys) =>
Review.find({
_id: { $in: keys.map((key) => mongoose.Types.ObjectId(key)) },
}),
},
mutations: {
addReview: (review) => {
let session = null;
Review.startSession()
.then((_session) => {
session = _session;
return session.withTransaction(() => {
return new Review(review).save(review, {
session: session,
});
});
})
.then(() => session.endSession());
},
modifyReview: (body) => Review.findByIdAndUpdate(body.id, body.query),
deleteReview: (id) => Review.findByIdAndDelete(id),
},
});
export default generateReviewModel;
in my case localesCollection within compareValue is having an old (not updated) value. State is not real-time. I assumed bind was here to save my day.
// Redux
let localesCollectionValues = useSelector((state: IStore) => state.localesStoreSlice.localesCollectionValues);
const compareValue = (fieldValue: any): void => {
console.log('compareValue', fieldValue, localesCollectionValues);
}
const attachListeners = () => {
console.log('attachListeners');
Object.keys(sdk.entry.fields).forEach((field: string) => {
const fieldRef: any = sdk.entry.fields[field];
fieldRef.locales.forEach((localeKey: string) => {
fieldRef.getForLocale(localeKey).onValueChanged(compareValue.bind(this));
});
});
};
edit:
is was coming from having the arrow function directly in the callback with the same issue as described above
edit2:
the value of 'localesCollectionValues' is the value on the moment that the code get initialised and the handler gets set. After that its not updated and it just keeps hold of n old reference. Typical memory pointer / scope issue, in need of a direction to solve this.
fieldRef.getForLocale(localeKey).onValueChanged(() => {
console.log(localesCollectionValues) // <- not real time,
// contains old value
});
In hindsight I just had to follow React Hooks directions for State Management, makes perfect sense:
const [changedLocale, setChangedLocale] = useState('');
useEffect(() => {
console.log('changedLocale', changedLocale, localesCollection);
// accurate, real-time
}, [changedLocale, localesCollection]);
const attachListeners = () => {
Object.keys(sdk.entry.fields).forEach((field: string) => {
sdk.entry.fields[field].locales.forEach((localeKey: string) => {
const fieldRef: any = sdk.entry.fields[field];
fieldRef.getForLocale(localeKey).onValueChanged(() => {
setChangedLocale(localeKey);
});
});
});
};
I want to chain effects with the latest syntax of ngrx. I've searched and found this question on stackoverflow but it has old syntax.
This is the current effect:
export class DeleteCommentEffect {
deleteComment$ = createEffect(() =>
this.actions$.pipe(
ofType(DeletingComment),
mergeMap((action) => this.commentService.deleteComment(action.dossierId, action.commentId)
.pipe(
map((statusCode: number) => {
return DeleteCommentSuccess({ statusCode });
}),
catchError((error: HttpErrorResponse) => {
return of(DeleteCommentError({ error }));
})
))
)
);
constructor(
private actions$: Actions,
private commentService: DBCommentService) {
}
}
I want to chain this effect after successfully delete a comment.
export class GetCommentEffects {
getComment$ = createEffect(() =>
this.actions$.pipe(
ofType(GettingComment),
mergeMap(action =>
this.commentService.getAllComments(action.dossierId).pipe(
map((comments: Comment[]) => {
return GetCommentSuccess({comments});
}),
catchError((error: HttpErrorResponse) => {
return of(GetCommentError({error}));
})
))
)
);
constructor(
private actions$: Actions,
private commentService: DBCommentService
) {}
}
I've searched in ngrx docs but it seems like it does not mention about how to chain effects.
Create an effect that listens for DeleteCommentSuccess action that dispatches GettingComment.
deleteCommentSuccess$ = createEffect(() =>
this.actions$.pipe(
ofType(DeleteCommentSuccess),
map(() => CommentActions.GettingComments())
)
);
In ionic 2, (and angularfire2#4.0.0-rc.0, firebase#3.9.0)
I tried this code, but complete function doesn't work!
constructor(public af: AngularFireDatabase, ...) {
this.temp = af.list('/myTest').subscribe(data => {
... // my tastks
}, error => {
console.log(error);
}, () => {
console.log("done!"); // I can't see this output!
});
}
I already tried very many times, but I can't see "done".
In another function, I tried this but the result is same with first.
console.log(this.temp.closed); // false
this.temp.unsubscribe()
console.log(this.temp.closed); // true
,, What can I do..?
AngularFire list and object observables do not complete. Their internal Firebase refs remain connected, listening for changes to the database.
If you want the list to complete after the first value is received, you can use the first operator:
import "rxjs/add/operator/first";
constructor(public af: AngularFireDatabase, ...) {
this.temp = af.list('/myTest')
.first()
.subscribe(data => {
... // my tastks
}, error => {
console.log(error);
}, () => {
console.log("done!");
});
}
Note that complete handlers are not called upon unsubscription. They are called when the observable completes.
If you want a callback to be invoked upon either completion or unsubscription, you can use the finally operator instead:
import "rxjs/add/operator/finally";
constructor(public af: AngularFireDatabase, ...) {
this.temp = af.list('/myTest')
.finally(() => {
console.log("done!");
})
.subscribe(data => {
... // my tastks
}, error => {
console.log(error);
});
}
I'm using react-select library to display a select box. I'm using Select.Async because I need to pull my options from an API. I use Select with loadOptions and it works during the intial page render. However, I'm also using redux-form which can change the value of a Field dynamically (using change). However, when I change the value of the Field like this, the value of the input does change (and I can verify this), but react-select's loadOptions is never called again (even though I thought it was supposed to be listening to a change of value). My question is, is there a way to dynamicaly call loadOptions every time the input value changes?
Thanks,
Edit: Answered on github here
this.state = {
store: '',
};
this.handleStoreSelect = this.handleStoreSelect.bind(this);
handleStoreSelect = (item) => {
this.setState({
store: item.value
}
};
<Select.Async
name="storeID"
value={this.state.store}
loadOptions={getStores}
onChange={this.handleStoreSelect}
/>
const getStores = () => {
return fetch(
"api to be hit",
{
method: 'get',
headers: {
'Content-Type': 'application/json'
}
}
)
.then(response => {
if(response.status >= 400){
throw new Error("error");
}
return response.json()
})
.then(stores => {
let ret = [];
for(let store of stores) {
ret.push({value: store._id, label: store.name})
}
return {options: ret};
})
.catch(err => {
console.log('could not fetch data');
console.log(err);
return {options: []}
})
};
Using this we can fetch the data and pass this object in the loadoptions.
copy this code outside the class. and also i'm posting the code to be implemented for loadoptions
It might be a better solution than this, but a quick one is to set a ref to your Select.Async component, and when a change action is triggered (like the change of an input - your case, or one button click event - like in the code below) you can update its options. I'm using a similar example with the example of their docs.
class YourClass extends React.Component {
getOptions = (input, callback) => {
setTimeout(function () {
callback(null, {
options: [
{value: 'one', label: 'One'},
{value: 'two', label: 'Two'}
]
});
}, 500);
};
updateOptions = () => {
this.selectAsync.state.options.push(
{value: 'three', label: 'Three'}
)
}
render() {
let props = this.props;
return (
<div>
<Select.Async
ref={selectAsync => this.selectAsync = selectAsync}
loadOptions={this.getOptions}
/>
<button onClick={this.updateOptions}>
Load more items
</button>
</div>
)
}
}