react native arrow functions and geoQueries - react-native

I am still struggling a little bit with arrow functions. I have previously made these posts:
react native and globally accessible objects
react native arrow functions and if statements
But now I am having a new issue with how these can be used with geoQuery. I have tried the code shown below:
myFunction = () => {
var onKeyMovedRegistration = () => geoQuery.on("key_moved", function(key, location, distance) {
console.log('Test log');
if (key != this.props.userID) {
arraySearchResult = findKeyInArray(key, this.props.arrayOfKeys);
if (arraySearchResult != "keyNotFound") {
console.log(key + " moved within query to " + location + " (" + distance + " km from center)");
}
}
})
}
So myFunction is bound, and I am trying to bind the geoQuery as well, as otherwise this.props.userID and this.props.arrayOfKeys are not accessible if I use the original line:
var onKeyMovedRegistration = geoQuery.on("key_moved", function(key, location, distance) {
With the attempt above, nothing fires. I do not get "Test log" to the console. There is no crash or error, it's just clearly not how this is supposed to be done.
Anyone know how I can get past this one?

Since your geoFire listener function is not bound therefore you can't access class level props to it.
Therefore the simplest way would be to use the arrow function here as
geoQuery.on("key_moved", (key, location, distance) =>
After this, this will be bound to the class and you will be able to access the class level props.

Related

Vue axios delete request not working. How do I fix it?

Im having issues with delete request, my post, get are working fine.
What am I doing wrong?
removeUser(id) {
axios.delete('https://jsonplaceholder.typicode.com/users' + id)
.then(function(response) {
const user = response.data;
this.users.splice(id, user);
});
if response.status === 204, then delete is succeed.
for the client, here is an axios example, notice there is a ' after users
destroy() {
return request.delete('/api/users/' + id)
}
for the server, here is an Laravel example:
if( $article->delete() ) {
return response()->json(null, 204);
} else {
abort(409);
}
I can see only 1 problem on the code you provided.
You're trying to modify the Vue instance $data users object by executing this.users.splice(id, user);. But you're inside the callback function and this no longer represents the Vue instance.
To fix this & make the users object actually modify after the response comes you'll need to do it like this :
removeUser(id) {
let that = this;
axios.delete('https://jsonplaceholder.typicode.com/users' + id)
.then(function(response) {
const user = response.data;
that.users.splice(id, user);
});
Now , I don't have any code from the back-end so I'll just make some assumptions :
The route might not be well defined > if you're using NodeJS then you should check your routes , it should look like this :
router.route('/users:id').delete(async function(req,res,next){ /* ... */ });
You might have a route problem because / is missing before the user value
1 hint : Again , if you're using NodeJS , you could use this inside your .delete route :
res.status(200).json({ errorCode : null , errorMessage : null , users : [] });
To see if you're receiving it on front-end.
I think you do need to append the trailing '/' to the URL, that way the URL is properly formed, such as "https://jsonplaceholder.typicode.com/users/123" (rather than "users123" at the end).
Aside from that, the first parameter to Array.prototype.splice is the position where item removal should begin. The second (optional) parameter, deleteCount, is the number of items to remove. Beyond deleteCount, you can pass a collection of objects which are to be inserted after the start position and after items have been removed.
You just need to find the object in your this.users array and remove it. If you want to use Array.prototype.splice for that, then you can use Array.prototype.findIndex to find the index of the user in the array then remove it:
// Find the index of the item to remove
const indexOfUserToRemove = this.users.findIndex(u => u.id === id);
// Call splice to remove the item
this.users.splice(indexOfUserToRemove, 1);

vue function not returning dynamic property instead showing initial value

I am trying to make a progress bar the progress bar works fine but its not changing text within html and keeps static 0%. N.B I am pasting here only relevant codes to avoid a large page of code.
<div class="progressTopBar"><div class="inner-progressBar" :style="{width: this.ProgressBar }">
#{{ getProgressBar() }}
</div></div>
//property
data: function () {
return {
ProgressBar:"0%",
}
}
//function on change to upload and make progress
fileSelected(e) {
let fd = new FormData();
fd.append('fileInput', $("#file")[0].files[0], $("#file")[0].files[0].name);
axios.post("/admin/chatFileUpload", fd, {
onUploadProgress: function (uploadEvent) {
this.ProgressBar = Math.round((uploadEvent.loaded / uploadEvent.total)*100) + '%';
$(".inner-progressBar").css("width", this.ProgressBar);
}
});
},
//getting progress bar value in text which only returns preset value
getProgressBar() {
return this.ProgressBar;
},
You need to make getProgressBar() a computed property instead of a method.
computed: {
getProgressBar() {
return this.progressBar;
}
}
Also, you should use camel case or snake case for your variables.
The problem is the scoping of this in the code below:
onUploadProgress: function (uploadEvent) {
this.ProgressBar = Math.round((uploadEvent.loaded / uploadEvent.total)*100) + '%';
Because this is a new function it has its own this value, it does not use the this value from the surrounding code.
The simplest way to fix this is to use an arrow function:
onUploadProgress: (uploadEvent) => {
this.ProgressBar = Math.round((uploadEvent.loaded / uploadEvent.total)*100) + '%';
An arrow function retains the this value from the surrounding scope.
I also suggest getting rid of the jQuery line starting $(".inner-progressBar"), that shouldn't be necessary once you fix the this problem as it will be handled by the template instead.
Further, it's unclear why you have a getProgressBar method at all. If it is just going to return ProgressBar then you can use that directly within your template without the need for a method.

How to prevent closing of cell edit mode on validation errors with custom vue components in ag-grid

I have succesfully rendered my own component as the cellEditor and would like and on-leave I would like it to try to validate the value and prevent the closing if it fails.
If I look at this then https://www.ag-grid.com/javascript-grid-cell-editing/#editing-api there's cancelable callback functions for editing. But in this callback function is there a way to access the current instantiated component? I would think that would be the easiest way to handle this.
I'm using vee-validate so the validation function is async, just to keep in mind.
Use Full row editing.
Create a global variable like
var problemRow = -1;
Then Subscribe to this events:
onRowEditingStarted: function (event) {
if (problemRow!=-1 && event.rowIndex!=problemRow) {
gridOptions.api.stopEditing();
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
},
onRowEditingStopped: function (event) {
if (problemRow==-1) {
if (event.data.firstName != "your validation") {
problemRow = event.rowIndex
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
}
if (problemRow == event.rowIndex) {
if (event.data.firstName != "your validation") {
problemRow = event.rowIndex
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
else{
problemRow=-1;
}
}
},
I had a similar issue - albeit in AngularJS and the non-Angular mode for ag-grid - I needed to prevent the navigation when the cell editor didn't pass validation.
The documentation is not very detailed, so in the end I added a custom cell editor with a form wrapped around the input field (to handle the niceties such as red highlighting etc), and then used Angular JS validation. That got me so far, but the crucial part was trying to prevent the user tabbing out or away when the value was invalid so the user could at least fix the issue.
I did this by adding a value parser when adding the cell, and then within that if the value was invalid according to various rules, throw an exception. Not ideal, I know - but it does prevent ag-grid from trying to move away from the cell.
I tried loads of approaches to solving this - using the tabToNextCell events, suppressKeyboardEvent, navigateToNextCell, onCellEditingStopped - to name a few - this was the only thing that got it working correctly.
Here's my value parser, for what it's worth:
var codeParser = function (args) {
var cellEditor = _controller.currentCellEditor.children['codeValue'];
var paycodeId = +args.colDef.field;
var paycodeInfo = _controller.paycodes.filter(function (f) { return f.id === paycodeId; })[0];
// Check against any mask
if (paycodeInfo && paycodeInfo.mask) {
var reg = new RegExp("^" + paycodeInfo.mask + '$');
var match = args.newValue.match(reg);
if (!match) {
$mdToast.show($mdToast.simple().textContent('Invalid value - does not match paycode format.').position('top right').toastClass('errorToast'))
.then(function(r) {
_controller.currentCellEditor.children['codeValue'].focus();
});
throw 'Invalid value - does not match paycode format.';
}
}
return true;
};
The _controller.currentCellEditor value is set during the init of the cell editor component. I do this so I can then refocus the control after the error has been shown in the toast:
CodeValueEditor.prototype.init = function (params) {
var form = document.createElement('form');
form.setAttribute('id', 'mainForm');
form.setAttribute('name', 'mainForm');
var input = document.createElement('input');
input.classList.add('ag-cell-edit-input');
input.classList.add('paycode-editor');
input.setAttribute('name', 'codeValue');
input.setAttribute('id', 'codeValue');
input.tabIndex = "0";
input.value = params.value;
if (params.mask) {
input.setAttribute('data-mask', params.mask);
input.setAttribute('ng-pattern','/^' + params.mask + '$/');
input.setAttribute('ng-class',"{'pattern-error': mainForm.codeValue.$error.pattern}");
input.setAttribute('ng-model', 'ctl.currentValue');
}
form.appendChild(input);
this.container = form;
$compile(this.container)($scope);
_controller.currentValue = null;
// This is crucial - we can then reference the container in
// the parser later on to refocus the control
_controller.currentCellEditor = this.container;
$scope.$digest();
};
And then cleared in the grid options onCellEditingStopped event:
onCellEditingStopped: function (event) {
$scope.$apply(function() {
_controller.currentCellEditor = null;
});
},
I realise it's not specifically for your components (Vue.js) but hopefully it'll help someone else. If anyone has done it a better way, I'm all ears as I don't like throwing the unnecessary exception!

React Native: Can't use this.setState() to set a variable inside a 2D array, but this.state.x= works

I have a 2D array of objects with key/value pairs as a state variable, and I'm trying to use the recommended way of setting/changing state variables, which is to use this.setState({x:y}) instead of directly setting it using this.state.x = y and then forceUpdate(). However, when I try to do that, it gives me an "unexpected token" error.
I basically want to flip a variable from one state to the other, so I'm usng a ternary operator. This code works
toggleBookmark(category, index) {
this.state.menuItems[category][index].bmIcon = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
}
This code, which I'd expect to do the same thing, gives an error
toggleBookmark(category, index) {
this.setState({menuItems[category][index].bmIcon: (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o"});
}
I thought it might be the ternary operator, so I put the value into a variable and tried setting the state variable with that, but it still gives the same error.
toggleBookmark(category, index) {
var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
this.setState({menuItems[category][index].bmIcon: iconText});
}
Am I doing something wrong? Is what I want to do possible with setState()?
In Javascript, you cannot use an expression as a key for an object when creating that object inline.
The problem here is that you have done {menuItems[category][index].bmIcon: iconText} which will throw a syntax error.
If you want a quick way to solve this, you may create the object first, then assign the value to that key like this:
var state = {};
state[menuItems[category][index].bmIcon] = iconText;
this.setState(state);
It's worth noting however that ES6 Provides a sugar for doing this, and there is another answer here that might provide more insight
How do I create a dynamic key to be added to a JavaScript object variable
Update:
I now see what you meant, I had previously assumed that menuItems already defined, but what you want to do is change the value of a key inside a nested object that is in this.state
This is something that React is not really built to do, you should keep your state relatively simple, and make separate React components for each menu item, then have them manage their own state. I would strongly recommend this approach because it will keep your code clean and robust. Don't be afraid to make more components!
However if you do want to keep all this nested state in one component (not advised), then you should first make a copy of the object you want to setState on.
var newMenuItems = _.clone(this.state.menuItems);
var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
newMenuItems[category][index].bmIcon = iconText;
this.setState({ menuItems: newMenuItems });
OR
var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
this.state.menuItems[category][index].bmIcon = iconText;
this.forceUpdate();
(First method preferred, but it requires you have something like underscore or lodash installed )
I have the data chat:
chat: {
id: 'ss3k5e6j1-6shhd6-sdasd3d3-23d5-gh67',
agentName: 'egaliciar',
agentAvatar: 'http://i.imgur.com/DY6gND0.png',
messages: [
{
id: 1,
lines: [
'Me pueden ayudar?',
'Tengo problemas con mis boletos',
'Hola buen dia...',
],
time: '17:20',
},
{
id: 2,
lines: ['¿Me podria regalar su nombres', 'Con gusto...'],
time: '17:22',
date: '23/ene/2012',
},
],
},
};
and when i do
const oldLines =Object.assign({}, this.state.chat);
oldLines.messages[0].lines.push('newValue');
My state Changed..... without this.setState({});
I Made a Clone;
var clone = JSON.parse(JSON.stringify(this.state.chat));
clone.messages[0].lines.push('new Value');
and the State maintain their state;
thus, the complete solution is for me:
var clone = JSON.parse(JSON.stringify(this.state.chat));
clone.messages[0].lines.push(questionAreaMessage); //the state maintains
this.setState({chat:clone}); //here the State change!!!!

Dojo custom component's property gives default always

I have written a custom component to create a html button.
custom component is defined as follows
dojo.provide("ovn.form.OvnButton") ;
require([ "dojo/_base/declare",
"dojo/dom-construct",
"dojo/parser",
"dojo/ready",
"dijit/_WidgetBase"],
function (declare, domConstruct, parser, ready, _WidgetBase){
return declare ("ovn.form.OvnButton",[_WidgetBase],{
label: "unknown",
constructor : function(args){
this.id = args.id;
args.props.forEach(function(prop) {
if(prop.name == 'label'){
this.label = prop.value;
alert("found label " + this.label);
}
});
alert("from constructor " + this.label);
},
postMixInProperties : function(){
},
buildRendering : function(){
alert("from renderer label is " + this.label);
this.domNode = domConstruct.create("button", { innerHTML: this.label }); //domConstruct.toDom('<button>' + this.label + '</button>');
},
_getLabelAttr: function(){
return this.label;
},
_setLabelAttr : function(label){
alert("from set input is " + label)
this.label = label;
},
postCreate : function(){
alert("Post create label is " + this.label);
},
startUP : function(){
}
});
});
This is how I am instantiating the component
var button = new ovn.form.OvnButton({
id:'run',
props:[{"name":"label","value":"Run"},{"name":"class","value":"btn"}]
});
In the constructor of the custom component, I am iterating through the array passed and assigning to the instance variable called 'label'. To my surprise when we print the instance variable in buildRendering function, it is still printing the default instead of the assigned value.
can somebody give some light on why this is so.
FYI:
I am getting the following sequence of messages on the console
1.found label Run
2. from constructor unknown
3. from renderer label is unknown
4. from set input is unknown
5. Post create label is unknown
This happens because inside the little forEach function, this actually points to something completely different than your OvnButton object.
Javascript's this keyword is quite strange in this regard (it doesn't have anything to do with Dojo, actually). You can read more about how it works here: http://howtonode.org/what-is-this . It's a quite fundamental concept of Javascript, different from other languages, so it's well worth your time to get familiar with.
But there are various different ways you can quickly solve it, so here are a few!
Use a regular for loop instead of forEach and callback
The easiest is probably to use a regular for loop instead of forEach with a callback.
....
// args.props.forEach(function(prop) {
for(var i = 0, l = args.props.length; i < l; i++) {
var prop = args.props[i];
if(prop.name == 'label'){
this.label = prop.value;
alert("found label " + this.label);
}
}//); <-- no longer need the closing parenthesis
The takeaway here is that Javascript's this magic only happens for function calls, so in this case, when we just use a for loop, this continues to point to the right thing.
... or use forEach's second thisArg argument
But perhaps you really want to use forEach. It actually has a second argument, often called thisArg. It tells forEach to make sure this points to something of your choice inside the callback function. So you would do something like this:
....
args.props.forEach(function(prop) {
if(prop.name == 'label'){
this.label = prop.value;
alert("found label " + this.label);
}
}, this); // <--- notice we give forEach two arguments now,
// the callback function _and_ a "thisArg" value
I'm not completely sure the above works in all browsers though, so here's another way to solve your issue:
... or use a temporary "self" variable
We will make a temporary variable equal to this. People often call such a variable self, but you can name it anything you want. This is important: it's only the this keyword that Javascript will treat differently inside the callback function:
....
var self = this; //<--- we basically give `this` an alternative
// name to use inside the callback.
args.props.forEach(function(prop) {
if(prop.name == 'label'){
self.label = prop.value; //<--- replaced `this` with `self`
alert("found label " + self.label); //<--- here as well
}
});
... or use hitch() from dojo/_base/lang
Some people don't like the self solution, perhaps because they like to consistently use this to refer to the owning object. Many frameworks therefore have a "bind" function, that makes sure a function is always called in a particular scope. In dojo's case, the function is called hitch. Here's how you can use it:
require([....., "dojo/_base/lang"], function(....., DojoLang) {
....
args.props.forEach(DojoLang.hitch(this, function(prop) {
if(prop.name == 'label'){
this.label = prop.value;
alert("found label " + this.label);
}
}));
... or use Javascript's own bind()
Dojo and pretty much every other framework out there has a hitch() function. Because it's such a commonly used concept in Javascript, the new Javascript standard actually introduces it's own variant, Function.prototype.bind(). You can use it like this:
....
args.props.forEach(function(prop) {
if(prop.name == 'label'){
this.label = prop.value;
alert("found label " + this.label);
}
}.bind(this));
That was a very long answer for a pretty small thing, I hope it makes some sense!