Vuex not reactive when mutate - vuejs2

I have an array tour_plan in my state which contains an object(s).
tour_plan = [
{
day:1,
location_id:3,
location_name:'Colombo'
}
]
I want to make this object
tour_plan = [
{
day:1,
location_id:3,
location_name:'Colombo',
tour_id:3,
tour_name:'City tour'
}
]
After gothourgh the docs in vuex in figureout that this won't work in reactive manner in vuex mutations.
state.tour_plan[state.tour_plan.length - 1].tour_id = payload.tour_id;
state.tour_plan[state.tour_plan.length - 1].tour_name = payload.tour_name;
So I tried these 2 ways.
state.tour_plan[state.tour_plan.length - 1] = { ...state.tour_plan[state.tour_plan.length - 1],
tour_id: payload.tour
};
Vue.$set(state.tour_plan[state.tour_plan.length - 1], 'tour_id', payload.tour);
But non of them also doesn't work.
Where is the problem in my code?

You are correct about the fact you can't mutate an object/array item by index access without "breaking" the reactivity system.
About what You've tried, according to the documentation of Vue.set, it expects as first argument the entire object/array rather than the length.
So the correct manner is:
Vue.$set(state.tour_plan, 0, payload.tour);
where 0 is the index of the element you want to update(replace)

This worked for me.
Vue.set(state.tour_plan[state.tour_plan.length - 1], 'tour_id', payload.tour)
Vue.set(state.tour_plan[state.tour_plan.length - 1], 'tour_payment', payload.tour_payment)
Vue.set(state.tour_plan[state.tour_plan.length - 1], 'tour_name', payload.tour_name)

Related

Pipeline generation - passing in simple datastructures like lists/arrays

For a code repository project in Palantir Foundry, I am struggling with re-using some of my transformation logic.
It seems almost trivial, but: is there way to send an Input to a Transform that is not a dataset/dataframe reference?
In my case I want to pass in strings or lists/arrays.
This is my code:
from pyspark.sql import functions as F
from transforms.api import Transform, Input, Output
def my_computation(result, customFilter, scope, my_categories, my_mappings):
scope_df = scope.dataframe()
my_categories_df = my_categories.dataframe()
my_mappings_df = my_mappings.dataframe()
filtered_cat_df = (
my_categories_df
.filter(F.col('CAT_NAME').isin(customFilter))
)
# ... more logic
def generateTransforms(config):
transforms = []
for key, value in config.items():
o = {}
for outKey, outValue in value['outputs'].items():
o[outKey] = Output(outValue)
i = {}
for inpKey, inpValue in value['inputs'].items():
i[inpKey] = Input(inpValue)
i['customFilter'] = Input(value['my_custom_filter'])
transforms.append(Transform(my_computation, inputs=i, outputs=o))
return transforms
config = {
"transform_one": {
"my_custom_filter": {
"foo",
"bar"
},
"inputs": {
"scope": "/my-project/input/scope",
"my_categories": "/my-project/input/my_categories",
"my_mappings": "/my-project/input/my_mappings"
},
"outputs": {
"result": "/my-project/output/result"
}
}
}
TRANSFORMS = generateTransforms(config)
The concrete question is: how can I send in the values from my_custom_filter into customFilter in the transformation function my_computation?
If I execute it like above, I get the error "TypeError: unhashable type: 'set'"
This looks like a python issue, any chance you can point out which line is causing the error?
Reading throung your code, I would guess it's this line:
i['customFilter'] = Input(value['my_custom_filter'])
Your python logic is wrong, if we unpack your code you're trying to do this call:
i['customFilter'] = Input({"foo", "bar"})
Edit to answer the comment on how to create a python transform to lock a variable in a closure:
def create_transform(inputs={}, outputs={}, my_other_var):
#transform(**inputs, **outputs)
def compute(input_foo, input_bar, output_foobar, ctx):
df = input_foo.dataframe()
df = df.withColumn("mycol", F.lit(my_other_var))
output_foorbar.write_dataframe(df)
return compute
and now you can call this:
transforms.append(create_tranform(inputs, outptus, "foobar"))

How can one dynamically update a progress indicator while a calculation started by changing an InputField value is running?

I have a Mathematica notebook that employs a fairly complicated user interface for controlling a long-running calculation. Among other things, the interface takes liberal advantage of Button, RadioButtonBar, Checkbox, and InputField.
When the effect of clicking a Button is an intermediate calculation that may take more than a couple seconds to complete, I like to provide a visual indication that the code hasn't crashed and is, in fact, doing something useful. A good way to do this is to start up a ProgressIndicator just before the intermediate calculation starts and then turn it off once the calculation is done. I have found this to be straightforward for calculations started by a Button click.
The same method does not work, however, for calculations that are initiated by changes to an InputField value. The simplified code below was written to do this but fails. The last two rows of the Grid are supposed to change automatically when updatingQ changes to True in the inner Dynamic command and then change back when updatingQ reverts to True, but it never happens. It appears that the outer Dynamic code is being blocked while the inner Dynamic code runs so it never even notices the changes to updatingQ.
On the other hand, the last two lines of the Grid respond as expected if one manually sets updatingQ=True on a separate input line.
(BTW, i) Pause[2] is just a stand-in for the intermediate calculation and ii) I multiply the input value by Pi is just to make it more obvious when the stand-in calculation is done.)
Apparently, the action portion of a Button behaves differently. Other pieces of code within the same Dynamic block can see and quickly respond when flags are changed there. It may be notable that I use Method->"Queued" in such cases. I tried the same with InputField (for which it is not a documented option) but to no effect.
I've tried various other things not shown here also without success.
A way to make this work would be much appreciated.
Clear[ProgressIndicatorTest]
updatingQ = False;
ProgressIndicatorTest = {
TextCell["ProgressIndicatorTest", "Subsubsection", Background -> LightBlue],
DynamicModule[
{filterTypes = {"Max energy", "Max length"}, filterValue, workingOn = "", iter = 0},
Scan[(filterValue[#[[1]]] = #[[2]]) &, Transpose#{filterTypes, {0.1, 100.}}];
Dynamic[
Grid[
Join[
Map[
Function[
filterType,
{filterType,
Dynamic#
InputField[
Dynamic[
filterValue[filterType],
Function[
value,
If[value > 0,
updatingQ = True;
Pause[2];
filterValue[filterType] = \[Pi] value;
updatingQ = False
]
]
], FieldSize -> 5, Alignment -> Right
]
}
], filterTypes
],
{{updatingQ, "-------"}},
{If[updatingQ,
{"Updating ... ",
ProgressIndicator[Appearance -> "Indeterminate"]},
Nothing
]}
], Alignment -> Left,
Background -> {None, {LightGreen, LightGreen, LightYellow, LightYellow}}
]
]
]
};
CellGroup[ProgressIndicatorTest]
As Forrest Gump never said, "Stackoverflow/Stackexchange is like a box of chocolates ... you never know what you'll get". And so today I found this answer which solves my problem.
Adapted to my particular case, the resulting code is as follows:
Clear[ProgressIndicatorTest]
calculation[n_] := Module[{a = .3}, Do[a = a (1 - a), {i, n 10^6}]]
updatingQ = False;
ProgressIndicatorTest = {
TextCell["ProgressIndicatorTest", "Subsubsection", Background -> LightBlue],
DynamicModule[{filterTypes = {"Max energy", "Max length"}, filterValue, upToDateQ = True},
Scan[(filterValue[#[[1]]] = #[[2]]) &, Transpose#{filterTypes, {0.1, 100.}}];
Dynamic[
Grid[
Join[
Map[
Function[
filterType,
{filterType,
DynamicWrapper[
InputField[
Dynamic[
filterValue[filterType],
Function[
value,
If[value > 0,
upToDateQ = False;
filterValue[filterType] = value
]
]
], FieldSize -> 5, Alignment -> Right
],
If[! upToDateQ,
Refresh[
updatingQ = True; calculation[2]; updatingQ = False;
upToDateQ = True,
None
]
],
SynchronousUpdating -> False
]
}
], filterTypes
],
{
If[updatingQ,
{"Updating ... ",
ProgressIndicator[Appearance -> "Indeterminate", ImageSize -> 80]},
Nothing
]
}
], Alignment -> Left,
Background -> {None, {LightGreen, LightGreen, LightYellow,}}]
]]
};
CellGroup[ProgressIndicatorTest]
This code does exactly what I want.
The key to success is wrapping DynamicWrapper around InputField and inserting a cleverly constructed second argument that performs the flag reset (upToDate=False in my case) that triggers the ProgressIndicator located elsewhere.
A couple more points.
Pause turns out not to be a good stand-in for a calculation. You may observe that the code behaves differently with a real function such as calculation.
It is interesting to note that upToDateQ can be a local variable whereas updatingQ cannot.
Kudos to Albert Retey for providing the code back in 2013.
The documentation for InputField says
"An InputField of type Expression [the default] replaces its
contents with the fully evaluated form every time the contents are
updated".
This seems to mean that InputField privately evaluates its content and all connected dynamics before releasing value changes, probably to prevent circular evaluations.
The following example condenses the problem. The first part works ok ...
changed = processing = False;
Column[{InputField[Dynamic[x, (changed = True; x = 2 #) &], FieldSize -> 5],
Dynamic[changed],
Dynamic[processing]}]
... until the dynamic below is also evaluated. Then changed never shows True because it is changed back to False before the update concludes.
Dynamic[If[changed,
processing = True;
Pause[2];
changed = processing = False]]
A alternative strategy would be to use a Button, e.g.
changed = False;
processing = Spacer[0];
Column[{InputField[Dynamic[y, (changed = True; y = #) &], FieldSize -> 5],
Button["Enter",
If[changed,
processing = ProgressIndicator[Appearance -> "Indeterminate", ImageSize -> 120];
Pause[2];
y = 2 y;
changed = False;
processing = Spacer[0]], Method -> "Queued", Enabled -> Dynamic[changed]],
Dynamic[changed],
Dynamic[processing]}]
This shorter version avoids the need to tab out of the input field.
changed = False;
processing = Spacer[0];
Column[{InputField[Dynamic[y], FieldSize -> 5],
Button["Enter",
processing = ProgressIndicator[Appearance -> "Indeterminate", ImageSize -> 120];
Pause[2];
y = 2 y;
processing = Spacer[0], Method -> "Queued"], Dynamic[processing]}]
Note the use of Method -> "Queued" gives Button the advantage over InputField. Without it Button appears to have the same problem.

How do I make an edit_traits() GUI item responsive to changes in its dependencies?

I'm designing a HasTraits subclass with dependent properties:
#!/usr/bin/env python
# Example for SO question on dynamically changing Dict contents.
from traits.api import HasTraits, Dict, Property, Trait, Int, cached_property
from traitsui.api import View, Item
class Foo(HasTraits):
"Has dependent properties, which I'd like to remain up-to-date in the GUI."
_dicts = [
{"zero": 0, "one": 1},
{"zero": 1, "one": 2},
{"zero": 2, "one": 3},
]
zap = Int(0)
bar = Property(Trait, depends_on=["zap"])
baz = Trait(list(_dicts[0])[0], _dicts[0])
#cached_property
def _get_bar(self):
return Trait(list(self._dicts)[self.zap], self._dicts)
traits_view = View(
Item("zap"),
Item("bar"),
Item("baz"),
width=500,
)
if __name__ == '__main__':
Foo().configure_traits()
When I run this code I see:
And if I change the value of Zap:
Note the following:
After changing Zap, the address of Bar has changed.
This means that changes to Bar are being dynamically updated in the GUI, while it's still opened; that's great! However...
The way Bar is displayed in the GUI is not very useful.
I'd love to have Bar displayed as Baz is displayed: selectable by the user.
What I'd like is to have the best of both worlds:
the dynamic GUI updating I see with Bar, and
the display format of Baz.
Does anyone know how I can get this?
I've tried several ways of updating a Baz-like item dynamically, to no avail.
(See this previous SO question.)
I assume you wish both bar and baz to be dict type (in traits Dict). Actually, there are default display widgets for pre-defined trait types, which are more useful than showing address. I believe traitsui doesn't know how to properly display your custom Trait object unless you explicitly assign an editor for it. Note that for baz, although a dropdown menu is generated, it is only displaying the keys, which is not very useful either.
With that said, the following codes might meet your expectations.
class Foo(HasTraits):
"Has dependent properties, which I'd like to remain up-to-date in the GUI."
_dicts = [
{"zero": 0, "one": 1},
{"zero": 1, "one": 2},
{"zero": 2, "one": 3},
]
zap = Int(0)
bar = Property(Dict, depends_on=["zap"])
baz = Trait(list(_dicts[0])[0], _dicts[0])
#cached_property
def _get_bar(self):
return self._dicts[self.zap]
traits_view = View(
Item("zap"),
Item("bar", style="custom"),
Item("baz"),
width=500,
)
The following code gets me the behavior I want:
#!/usr/bin/env python
# Example for SO question on dynamically changing Dict contents.
from traits.api import HasTraits, Dict, Property, Trait, Int, cached_property, Enum, List
from traitsui.api import View, Item
class Foo(HasTraits):
"Has dependent properties, which I'd like to remain up-to-date in the GUI."
_dict = {
"zero": 0,
"one": 1,
"two": 2,
}
_zaps = [
["zero", "one"],
["one", "two"],
["zero", "two"],
]
zaps = List(_zaps[0])
zap = Enum([0,1,2]) # Selection of `zap` should limit the items of `_dict` available for selection.
bar = Enum(_zaps[0][0], values="zaps")
bar_ = Int(_dict[_zaps[0][0]])
def _zap_changed(self, new_value):
self.zaps = self._zaps[new_value]
self.bar_ = self._dict[self.bar]
def _bar_changed(self, new_value):
self.bar_ = self._dict[self.bar]
traits_view = View(
Item("zap"),
Item("bar"),
Item("bar_", style="readonly"),
width=500,
)
if __name__ == '__main__':
Foo().configure_traits()
Immediately after program start-up:
And after changing to Zap to '1':

Using vue-router in Vuex store

I want to use use vue-router to route to a new component from a Vuex store. Is it possible to access the router from a Vuex store? I've seen a bunch of threads about it, but nothing that really worked for me... The error below is what I get when I try to use the router.
[Vue warn]: Error in mounted hook: "TypeError:
WEBPACK_IMPORTED_MODULE_2_vue_router.a.push is not a function"
Here's my store:
SENTENCE_TRACKER({commit, state}){
let chosenSentence
if(state.currSent < state.res.length) {
chosenSentence = state.res[state.currSent];
console.log(chosenSentence);
commit('CHINESE', {chineseData:chosenSentence.chinese})
commit('ENGLISH', {englishData:chosenSentence.english})
commit('PINYIN', {pinyinData:chosenSentence.pinyin})
commit('AUDIO', {audioData:chosenSentence.audio})
commit('WBW', {wbwData:chosenSentence.wbw})
state.currSent++
} else {
// THIS IS WHERE I'D LIKE TO USE VUE-ROUTER
router.push({path:'/summary'})
}
let arr = []
state.wbw.map(function(i){
arr.push('')
})
commit('WBW_STATE', {wbwStateData: arr})
let shuffled = state.wbw.slice(), i, j, k;
for (i = shuffled.length; i; i--) {
j = Math.floor(Math.random() * i);
k = shuffled[i - 1];
shuffled[i - 1] = shuffled[j];
shuffled[j] = k;
}
commit('SHUFFLED', {shuffledData: shuffled})
},
Thanks!
The answer was that I just had to not use the normal this.$router syntax in the store and instead just do this: router.push({name:'summary'}). Thanks!

Mocha ignores dynamically-generated tests

Trying to run a bunch of dynamically-generated tests within a single describe(). I've copied in the example from the Mocha docs, and it works fine:
describe('add()', function() {
var tests = [
{args: [1, 2], expected: 3},
{args: [1, 2, 3], expected: 6},
{args: [1, 2, 3, 4], expected: 10}
];
tests.forEach(function(test) {
it('correctly adds ' + test.args.length + ' args', function() {
var res = add.apply(null, test.args);
assert.equal(res, test.expected);
});
});
});
BUT, when I try iterating over an array (or cursor) from a database call the entire describe() module is ignored - no errors, just doesn't run at all and doesn't appear in the reporter.
i.e. if I replace the tests array with
const tests = Tests.find().fetch();
the whole thing is just skipped.
What am I doing wrong? Is there a way around this?