Directly update mapbox source features - javascript

I'm drawing orders on the map using layers with symbols, this orders has status. Whenever the status changes I'd like to change de color of the symbol.
My layer has this configuration:
map.addLayer({
id: "orders",
type: "symbol",
source: "order-markers",
layout: {
"icon-image": [
"match",
["get", "orderStatus"],
"UNASSIGNED",
"unassigned-marker",
"ASSIGNED",
"assigned-marker",
"IN_TRANSIT",
"intransit-marker",
"CONCLUDED",
"concluded-marker",
"UNASSIGNED-marker",
],
},
});
});
Is there a recommended way to directly access the source and update the status in the properties object ? Or is it right to always overwrite the whole data object with setData ?
I tried to create a state in React with the features but it seems that the features object dont react to the state changes.
Thanks.

I haven't found a way to update a single property on a single feature. What I've always done is to overwrite the data as you've mentioned (i.e. map.getSource('order-markers').setData(/*...*/)) with the whole data object where a feature's property was changed. This is the method mapbox uses in their example for updating with live data: live-update-feature
As an alternative however, you can update a single feature's state. If that's a possibility you can use map.setFeatureState.
for example:
map.setFeatureState(
{
source: 'order-markers',
id: feature.id,
}, {
status: "IN_TRANSIT"
}
);
You'll then have to update your layer style/expressions to use the state instead of property to properly reflect the data as they are slightly different in how you get the values.

Related

ref.update() removes omitted keys - works just like .set()?

I am attempting to use Firebase realtime database .update() to update a data node according to documentation. I am using .update() to selectively update only the referenced keys at the location - instead of replacing all the keys at the location.
My problem is that I have omitted key "createdAt" from the update object since I don't wish to update "createdAt" after initial create - but the "createdAt" key gets removed when using .update()! 「(゚ペ)
Database structure
Object sent to update()
{
"data": {
"notifyByEmail": false,
"notifyByPush": false,
"notifyBySms": false
},
"access/members": {
"1234567890": true,
"0987654321": true
}
}
Code
console.log('api', `${myPath}/${id}`, dbUpdateObj)
database.ref(`${myPath}/${id}`).update(dbUpdateObj).then(() => {
resolve({id, reduxStoreObj})
})
Logging
api categories/-LqXvFkeF_QfA5oHquYp [+] Object { data: {…}, "access/members": {…} }
Result
Why is "createdAt" removed? I thought .update() should only update defined keys and not touch omitted keys.
What have I missed?
Kind regards /K
A call to update acts like separate calls to set to each of the properties you pass in. So in your case the data and access/members get completely replaced, but all other properties under database.ref(`${myPath}/${id}`) stay unmodified.
If you want to update deeper level properties, include their entire path in the update map. So to maintain data/createdAt, specify the entire path to the notify... properties that you want to update:
{
"data/notifyByEmail": false,
"data/notifyByPush": false,
"data/notifyBySms": false,
"access/members": {
"1234567890": true,
"0987654321": true
}
}
I find that it's most common to have a flat map of complete paths in such multi-path update statements. So to also maintain the existing member of access/members, the above would become:
{
"data/notifyByEmail": false,
"data/notifyByPush": false,
"data/notifyBySms": false,
"access/members/1234567890": true,
"access/members/0987654321": true
}
You can use .set() with the {merge: true} option to avoid the (not changed) data be replaced by the new data.
See: https://firebase.google.com/docs/firestore/manage-data/add-data

Extjs multiple representations of data

I have a Rest endpoint, that sends and receives objects of the form
[
{id: 1, Name: "Type"},
{id: 2, Name: "Type:Subtype"},
...
]
I want to display this in an editable tree, using Sencha Extjs 6. I am confused as to where and when to transform the data, and how to keep changes synchronized without side effects. My current (not nice) method is to reload the data and then reset the tree's store using the converted values, but that collapses all of the expanded nodes
I can get and save entries using a model and a store
I can convert the data into a form suitable for use in a treepanel
I do not know the "right" way to do so, and to have changes in either store reflected in the other.
For clarity, the converted tree store has a data stucture:
[
{
text: "Type",
children: [
{
text: "Subtype",
isLeaf: true
}
]
}
]
Came up with the answer: a save button.
Bound the Load event of the Rest Store to the controller, and in that method copied the values into the TreeStore.
Edits are made to the TreeStore - kept purely client side
Save button copies new data back into Rest Store and calls sync(). Easy to follow. Code is kept mostly declarative. And I can provide default values in the VM's data field. Easy to do - shame it's not made clear in the documentation somewhere

Ember.js (2.5.0) how to set nested object value

Sounds like a simple enough thing to do yet is causing me all sorts of grief.
I have a simple server model which has a few nested objects,
export default DS.Model.extend({
type: DS.attr('string'),
attributes: DS.attr(),
tasks: DS.attr()
});
I can create a new record in the route using
export default Ember.Route.extend({
model() {
return this.store.createRecord('server');
},
actions: {
create(server) {
server.save().then(() => this.transitionTo('servers'));
}
}
});
and in the related .hbs I'm setting a few properties of attributes and tasks using value=model.attributes.name from a form for example.
This all works fine. I however want to add a few more properties from the route during create such as default values.
Using server.set('attributes.size', 'large'); doesn't work as Ember doesn't know about size yet as it's a new record.
I can use setProperties but this seems to wipe out every other value
server.setProperties({
attributes: {
size: "large"
},
tasks: {
create: true
}
});
size is now correctly set, however name is now null because I didn't specify it in the setProperties...
What's the proper way to go about this? Surely I don't need to map out all the properties in setProperties? That seems wasteful and very error prone.
Something I've thought is should attributes just be its own model and have a relationship with Server? Even though this is always a 1-to-1 and 1-to-1 relationship?
I would recommend using ember-data-model-fragments addon as a solution in this case.
https://github.com/lytics/ember-data-model-fragments
Other option using a separate model for attributes and setting up a 1-to-1 relation. Both would be belongsTo, however it is depend on your database and API also, so you have to align your backend system to match with this new structure.

Why isn't my view re-rendered again as expected?

I created a custom control which includes a currentValue property. I defined it in metadata as below:
properties:
{
currentValue:
{
type: 'int',
defaultValue: 0
},...
in my Main.controller.js I'm calling the custom control that I created and changed it's currentValue property as below.
var oCustomControl = this.getView().byId("customID1");
oCustomControl.setCurrentValue(75);
in this step in my control.js, I didn't create a setCurrentValue function. Because I know UI5 is creating it itself. But the currentValue property of my control couldn't been updated. So I'm thinking my control couldn't been rerendered. So I overwrote the currentValue setter and change it as below:
setCurrentValue : function(iCurrentValue)
{
this.setProperty("currentValue", iCurrentValue);
},
But still I couldn't see the value which I changed in my view.
Here is my renderer:
renderer :
{
render : function(oRm, oControl) {
var layout = oControl.createGauges();//I created layout
oRm.write("<div");
oRm.writeControlData(layout);
oRm.writeClasses();
oRm.write(">");
oRm.renderControl(layout);
oRm.addClass('verticalAlignment');
oRm.write("</div>");
}
},
I am thinking now maybe it is because I'm rendering layout as a control?
and my other properties are related with d3.js. And I coded d3.js codes in my onAfterRendering function.
Actually your code looks fine.
Accept that you have specified:
bindable: 'bindable'
What should that do?
And what does rerender()?
The method to render a control is called renderer().
But you don't need to call it when you say:
this.setProperty("currentValue", iCurrentValue);
If you dont say true it will rerender the control.
It would be nice to know whats inside your renderer() function, or what happens when you call getCurrentValue().
It is solved by changing
oRm.writeControlData(layout); this line as follow:
oRm.writeControlData(oControl);

How to pass custom data into Highcharts graph click event

Is possible to pass custom data when rendering a Highcharts graph (funnel, in this case), so that when I bind a click event, I can use this custom data point?
Currently, all that I can get is the "name" event.point.name, which I provide for the Label, but I also want to pass a song_id too.
http://jsfiddle.net/y4a2end3/1/
Is there a place in the graph code that I can add another data point, like "song_id"?
series: [{
name: 'Song Plays',
data: [
['song_name1',123, 'song_id_1'], /* song_id_1 would be the custom data */
['song_name2',234, 'song_id_2']
]
}]
If you want to attach additional data to points in a series you can initialize the points that need additional data as objects instead of arrays/ints. For example, with your code you could do:
series: [{
name: 'Song Plays',
data: [
{x:'song_name1', y:123, songid:'song_id_1'},
{x:'song_name2', y:234, songid:'song_id_2'}
]
}]
You can then get it from the point on click as event.point.songid. See this JSFiddle demo using point click and tooltip.
Note that in many cases x in the object will not be required. It is often automatic and sequential.
You can try
alert(event.point.series.userOptions.data[event.point.x][2])
Updated fiddle: http://jsfiddle.net/y4a2end3/2/
Or this:
alert(event.point.series.userOptions.data[event.point.series.data.indexOf(event.point)][2]);

Categories