AmCharts5 set active state on country - amcharts

Been stuck on this for quite a while.
I have an amcharts5 map in which I want to pre-zoom to a specific country and set it's state to active.
The zoom part was easy - polygonSeries.zoomToDataItem()
But I can't seem to understand how to set the state to active, in amchart4 it was straightforward (https://www.amcharts.com/docs/v4/tutorials/pre-zooming-map-to-a-country/#Highlighting_selected_country), but in amcharts5 ..
Thanks

Got it!!!
polygonSeries.mapPolygons.template._entities[9].set("active", true)
9 - index of country

let previousPolygon = null;
polygonSeries.mapPolygons.template.setAll({
toggleKey: 'active',
});
polygonSeries.mapPolygons.template.on('active', (active, target) => {
if (previousPolygon && previousPolygon !== target) {
previousPolygon.set('active', false);
}
if (target.get('active')) {
polygonSeries.zoomToDataItem(target.dataItem);
} else {
chart.goHome();
}
previousPolygon = target;
});
the code above allows to zoomIn/zoomOut onclick and toggle active state for target country
https://www.amcharts.com/demos/zooming-to-countries-map/

Related

Recalculating a function after update with VueJS

I won't even pretend I know what I am doing but I am going to ask as this is one of the most frustrating features of VueJS, well, at least for me.
So this is the situation. I have a number of sections creating their own totals and a final total made up of those calculated totals.
I'll give you the basics:
Parking
Transport
Mileage*
Subsistence
Each of the sections above creates a total from a number of lines of items which works fine at the moment. I'll get to mileage in a minute.
Each of these totals is then calculated into a grand total and also displayed.
At the moment, I am doing something like this:
computed: {
totalAmount: function() {
return this.calculateAmount(this.total);
},
transportAmount: function () {
if(this.isLoading === true) return 0;
return this.total.find(total => total.name == "transport").amount = (this.isLoading === false) ? this.calculateAmount(this.transport) : 0;
},
mileageAmount: function () {
if(this.isLoading === true) return 0;
return this.total.find(total => total.name == "mileage").amount = (this.isLoading === false) ? this.calculateAmount(this.mileage) : 0;
},
parkingAmount: function () {
if(this.isLoading === true) return 0;
return this.total.find(total => total.name == "parking").amount = (this.isLoading === false) ? this.calculateAmount(this.parking) : 0;
},
subsistenceAmount: function () {
if(this.isLoading === true) return 0;
return this.total.find(total => total.name == "subsistence").amount = (this.isLoading === false) ? this.calculateAmount(this.subsistence) : 0;
}
},
I am sure this is not the most efficient and almost certainly the wrong way to approach this (please correct me, I am self taught).
So my problem is with mileage.
I currently run a calculation for the total of each line based on a cost per mile AT INITIAL RUNTIME (i.e. when it first loads).
calculateMileage(ppm) {
this.mileage.forEach(e => {
e.amount = e.mileage * ppm;
});
},
Nothing too complex but it works.
Here is my problem. The amount for mileage is only created here. I am attempting to update the amount to then automatically update all the rest of the totals.
So I have the following setup:
Main component displaying all items
Sub components for adding or updating (pop up boxes)
So adding and updating works great for all other sections as they have a dedicated amount column stored in the database but the mileage one is calculated based on a pence per mile cost and dynamically displayed.
What should I use/try to calculate this figure on adding and updating. I tried using calculated methods but it appears you can't calculate from another calculated method.
Any ideas would be helpful. I can provide more code if necessary.
EDIT:
This is how the data is pulled from a database into the this.mileage variable
await axios.get(API_BASE_URL+'/claims/travel/mileage').then(response => {
this.mileage = response.data.data;
this.calculateMileage(0.55);
let totalModel = {};
totalModel.name = 'mileage';
totalModel.amount = (this.mileage.length > 0) ? this.calculateAmount(this.mileage) : 0;
this.total.push(totalModel);
});
mileage contains the following fields:
create table mileages
(
id bigint unsigned auto_increment
primary key,
claim int not null,
vehicle text not null,
mileageType int not null,
mileageDate date not null,
mileageTime time not null,
mileage double not null,
reason text not null,
origin text not null,
destination text not null,
created_at timestamp null,
updated_at timestamp null
)
collate=utf8mb4_unicode_ci;
EDIT 2:
I have added this watcher in place but it only fires at the initial load
watch: {
deep: true,
mileage: {
handler(){
console.log("Fired");
this.calculateMileage();
}
}
}
I suspect I need to make it watch the object items specifically somehow but I am at a loss at the moment.

Shopify Hide unavailable option variants

Using the Debut theme in Shopify, I have a product with variants that are not compatible. At present they show as unavailable but I want to hide them totally so that only valid options show with each other. So as example if I have Red, Blue, Green shoes and have sizes 9,10,11. But can only get Red shoes in Size 10. I don't want to see options for 9 or 11 ever.
Online someone pointed to theme.js and the code below, but I'm not sure what to change.
Thanks
$(this.singleOptionSelector, this.$container).on(
'change',
this._onSelectChange.bind(this)
);
}
I've just spend most of the day on this, and have come up with the following, which seems to work nicely on the site I'm developing.
The answer I came up with involves editing the assets/theme.js file. At present, the code below disables the select options which are not relevant by checking them against the available variant combinations, but you could easily adapt the below to hide them and then show them instead with CSS.
assets/theme.js
The _hideUnavailableOptions method below needs to be added to the Variants.prototype object.
You then need to call the method from two different places, see below.
_hideUnavailableOptions: function() {
const option1 = document.getElementById("SingleOptionSelector-0");
const option2 = document.getElementById("SingleOptionSelector-1");
if (option2) {
const secondOptions = option2.options;
const variants = this.product.variants;
let possibles = [];
variants.forEach((variant) => {
if (variant.options.includes(option1.value)) {
possibles.push(variant.options)
}
})
for (let option of secondOptions) {
const value = option.value;
let flag = false;
possibles.forEach((possible) => {
if (possible.includes(value)) {
flag = true;
}
})
if (flag === false) {
option.removeAttribute('selected');
option.setAttribute('disabled', 'disabled');
} else {
option.removeAttribute('disabled');
}
} option2.querySelector(':not([disabled="disabled"])').setAttribute('selected', 'selected');
}
},
Call the method as follows:
function Variants(options) {
//stuff before this, then...
this.enableHistoryState = options.enableHistoryState;
this._hideUnavailableOptions(); //N.B. this MUST be before the next line
this.currentVariant = this._getVariantFromOptions();
}
...and again, call the method from Variants.prototype._onSelectChange() - make sure it's the first line in there...
_onSelectChange: function() {
let hideUnavailable = this._hideUnavailableOptions(); //N.B. this MUST be before the next line
var variant = this._getVariantFromOptions();
//lots more stuff follows...
}

Why session.getSaveBatch() is undefined when child record was added - Ext 5.1.1

Well the title says it all, details following.
I have two related models, User & Role.
User has roles defined as:
Ext.define('App.model.security.User', {
extend: 'App.model.Base',
entityName: 'User',
fields: [
{ name: 'id' },
{ name: 'email'},
{ name: 'name'},
{ name: 'enabled', type: 'bool'}
],
manyToMany: 'Role'
});
Then I have a grid of users and a form to edit user's data including his roles.
The thing is, when I try to add or delete a role from the user a later call to session.getSaveBatch() returns undefined and then I cannot start the batch to send the modifications to the server.
How can I solve this?
Well after reading a lot I found that Ext won't save the changed relationships between two models at least on 5.1.1.
I've had to workaround this by placing an aditional field on the left model (I named it isDirty) with a default value of false and set it true to force the session to send the update to the server with getSaveBatch.
Later I'll dig into the code to write an override to BatchVisitor or a custom BatchVisitor class that allow to save just associations automatically.
Note that this only occurs when you want to save just the association between the two models and if you also modify one of the involved entities then the association will be sent on the save batch.
Well this was interesting, I've learned a lot about Ext by solving this simple problem.
The solution I came across is to override the BatchVisitor class to make use of an event handler for the event onCleanRecord raised from the private method visitData of the Session class.
So for each record I look for left side entities in the matrix and if there is a change then I call the handler for onDirtyRecord which is defined on the BatchVisitor original class.
The code:
Ext.define('Ext.overrides.data.session.BatchVisitor', {
override: 'Ext.data.session.BatchVisitor',
onCleanRecord: function (record) {
var matrices = record.session.matrices
bucket = null,
ops = [],
recordId = record.id,
className = record.$className;
// Before anything I check that the record does not exists in the bucket
// If it exists then any change on matrices will be considered (so leave)
try {
bucket = this.map[record.$className];
ops.concat(bucket.create || [], bucket.destroy || [], bucket.update || []);
var found = ops.findIndex(function (element, index, array) {
if (element.id === recordId) {
return true;
}
});
if (found != -1) {
return;
}
}
catch (e) {
// Do nothing
}
// Now I look for changes on matrices
for (name in matrices) {
matrix = matrices[name].left;
if (className === matrix.role.cls.$className) {
slices = matrix.slices;
for (id in slices) {
slice = slices[id];
members = slice.members;
for (id2 in members) {
id1 = members[id2][0]; // This is left side id, right side is index 1
state = members[id2][2];
if (id1 !== recordId) { // Not left side => leave
break;
}
if (state) { // Association changed
this.onDirtyRecord(record);
// Same case as above now it exists in the bucket (so leave)
return;
}
}
}
}
}
}
});
It works very well for my needs, probably it wont be the best solution for others but can be a starting point anyways.
Finally, if it's not clear yet, what this does is give the method getSaveBatch the ability to detect changes on relationships.

change state for itemRenderer when drag complete

I have a List with dragEnabled = true, and selectionColor = "#ff0000", when the user selects one item, it behaves as what I expect. But when I complete drag and release item, it still remains selected state. What I want is when drag complete, the item back to normal state, NOT selected state. What I do is that I check if the data dropComplete property is true, then change the state, but it can NOT work.
my code is following:
override protected function getCurrentRendererState():String {
currentState = super.getCurrentRendererState();
if(data.dropComplete) {
currentState = null;
data.dromComplete = false;
}
}
select state:
normal state:
I made a wrong direction. The solution is really simple, just listen for dragComplete event on List, code below:
private function myDragCompleteHandler(event:DragEvent):void{
this.selectedIndices = new Vector.<int>;
}

Magento- configurable products options order to match the order they are in the attribute set

I have a magento site I'm building (1.6) my site has a bunch of configurable options with 6 or so attributes set as dropdowns for the customer to pick from. After saving a configurable product the order of the attributes changes. I've been able to find what I think is happening, it is reordering them according to the attribute id not the order I have them set up in the attribute set. I need to find a way to get magento to keep the order of the attributes the same as they are in the attribute set. Any help is greatly appreciated.
Trick is pretty simple.
Just drag'n'drop them in product->edit->associatedProduct tab ;)
The order of attributes from this page is saved to catalog_product_super_attribute table.
I was also looking for the same and finally i found this and it works for me hope it will work for others too.
From Admin Panel > Catalog > Attributes > Manage Attributes select the one like if you want to make it like for the capacity 4GB > 8GB > 16GB and so on then do this small changes.
Select Manage Label / Options > Manage Options (values of your attribute) and if you already created the variables just add the position manually, like:
4GB - 1
8GB - 2
16GB - 3
Save and flush the cache.
That's it, now it should show the attributes as per the position that you assign.
It is an old question but I have found a solution right now having the same problem.
If you are still interesting in changing the order of the configurable attribute you may want to look into this method:
Mage_Catalog_Model_Product_Type_Configurable::getConfigurableAttributes()
getConfigurableAttributes() load the collection of attributes.
The first time the collection is loaded, before saving the configurable, there is no position value, so I think the attribute ID rules on the display order.
If you want to alter this order you can only add a sort for attribute_id after the ->orderByPosition() and revert the order ( this will preserve the position functionality )
For example, here I have added ->setOrder('attribute_id','DESC')
public function getConfigurableAttributes($product = null)
{
Varien_Profiler::start('CONFIGURABLE:'.__METHOD__);
if (!$this->getProduct($product)->hasData($this->_configurableAttributes)) {
$configurableAttributes = $this->getConfigurableAttributeCollection($product)
->orderByPosition()
->setOrder('attribute_id','DESC')
->load();
$this->getProduct($product)->setData($this->_configurableAttributes, $configurableAttributes);
}
Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__);
return $this->getProduct($product)->getData($this->_configurableAttributes);
}
OR
In case you want to modify the order in more radical way, you can also act on this method:
Mage_Adminhtml_Block_Catalog_Product_Edit_Tab_Super_Config::getAttributesJson()
This is basically calling the getConfigurableAttributes().
To understand if this is the first configurable load, you can check all the attributes in the array $attributes to see if they all have a position ==0 and then proceed with a manual reorder )
Example
I'm omitting all the module creation and the rewrite part.
Here an example modifying getAttributesJson() in order to have the color attribute always on the top.
public function getAttributesJson()
{
$attributes = $this->_getProduct()->getTypeInstance(true)
->getConfigurableAttributesAsArray($this->_getProduct());
if (!$attributes) {
return '[]';
} else {
// == START ==
// checking if I can re-order
if ($this->isNoSavedPosition($attributes)) {
$attributes = $this->attributeReorder($attributes);
}
// == END ==
// Hide price if needed
foreach ($attributes as &$attribute) {
if (isset($attribute['values']) && is_array($attribute['values'])) {
foreach ($attribute['values'] as &$attributeValue) {
if (!$this->getCanReadPrice()) {
$attributeValue['pricing_value'] = '';
$attributeValue['is_percent'] = 0;
}
$attributeValue['can_edit_price'] = $this->getCanEditPrice();
$attributeValue['can_read_price'] = $this->getCanReadPrice();
}
}
}
}
return Mage::helper('core')->jsonEncode($attributes);
}
public function isNoSavedPosition($attributes)
{
foreach ($attributes as $attribute) {
if (isset($attribute['position']) && $attribute['position'] != 0) {
return false;
}
}
// there is no position saved
// - this is the first time the configurable is loaded
// - (the position is saved on second save action)
return true;
}
public function attributeReorder($attributes)
{
// we want the Color attribute to be always on the top
$newAttributesOrderArray = array();
foreach ($attributes as $key => $attribute) {
if (isset($attribute['label']) && $attribute['label'] == 'Color') {
$newAttributesOrderArray[] = $attribute;
unset($attributes[$key]);
}
}
$newAttributesOrderArray = array_merge($newAttributesOrderArray,$attributes);
return $newAttributesOrderArray;
}

Resources