How do I pass a can.compute into a can.Component? - canjs

How can I pass computes into components, such that changing the value in selected in one component will affect a value in a different component.
Example
http://jsbin.com/feleko/1/edit?html,js,console,output
I'm trying to set it up so that selecting a value in the first select changes the options available in the second. I think listening for dom change events should be straightforward, but I don't seem to be getting a compute I can update, or have access to the parent scope in order to use an attribute name to update it. Likewise the max value isn't an active object that receives updates.

I've found an obtrusive way. can.mustache provides a data helper that puts the current context on the element's data.
<select {{data 'context'}} value="a">
Then in the init event I can capture the element data and assign it to the scope so it's available to scope functions.
events: {
init: function(el, opt) {
opt.scope.context = el.data('context')
}
}
Looking up values is then possible if awkward.
this.context[this.attr('value')]

Related

How To Query Through <slot> Using Cypress While Testing Web Components

After years of testing one global DOM for end-to-end testing, I'm finding it very difficult, if not impossible, to test web components that use slots. Before I explain the problem, I want to say that I cannot change the generated markup to improve things as they are.
<wc-1 attributes-etc="">
<wc-2 attributes-etc="">
<slot>
<wc-3 attributes-etc="">
<slot>
...eventually get to an input...
<input type="text" name="firstName" />
There are a buttload of nested web components from some kind of form builder, and there are also plenty of slots used. The web components have attributes but the slots never do, so I use the web component name for querying.
document.querSelector('wc-1')
.shadowRoot.querySelector('wc-2')
.shadowRoot.querySelector('slot')
// Yields <slot>...</slot>
All fine to this point and Cypress has a .shadow() command I used, but I'm testing with just devtools here to see all the properties the slot has.
document.querSelector('wc-1')
.shadowRoot.querySelector('wc-2')
.shadowRoot.querySelector('slot')
.shadowRoot
// Yields "null".
// I don't know how to get to the .lightDOM? of wc-2?
Any property I try ends up being null or having 0 elements in the returned value. Using other front-end tools and the global DOM, I can always cy.get('div[data-testid="the-nested-element-i-want"]').type('important words') in one command.
So my main question is: How do people test these things once web components start piling up? Or don't do this and just test the web components in isolation/unit tests since it's so hard to query the nested shadow DOMs?
The main goal is to eventually get to a form input to cy.get('input[name"firstName"]').type('John'). Can someone give me the chained docuement.querySelector() command to get to <wc-3> in my example?
The answer involves assignedNodes(): https://developer.mozilla.org/en-US/docs/Web/API/HTMLSlotElement/assignedNodes
The assignedNodes() property of the HTMLSlotElement interface returns a sequence of the nodes assigned to this slot...
It made no difference for me to use that vs. assignedElements(). So, all you have to do is use that method once you've queried down to the slot you need. For my example, the answer is:
const wc-3 = document.querySelector('wc-1').shadowRoot
.querySelector('wc-2').shadowRoot
.querySelector('slot').assignedNodes()
.map((el) => el.shadowRoot)[0]
And then you can keep going down the chain...I know I only have one un-named slot, so that's why I grab it from the returned .map().
Props to this Q&A for pointing me on the right direction: Web components: How to work with children?
There will be no DOM content in your <slot>, as there is no DOM content moved to slots.
lightDOM content is reflected in slots, but remains invisible! in lightDOM.
(that is why you also style slotted content in lightDOM)
From the docs:
๐˜พ๐™ค๐™ฃ๐™˜๐™š๐™ฅ๐™ฉ๐™ช๐™–๐™ก๐™ก๐™ฎ, ๐™™๐™ž๐™จ๐™ฉ๐™ง๐™ž๐™—๐™ช๐™ฉ๐™š๐™™ ๐™ฃ๐™ค๐™™๐™š๐™จ ๐™˜๐™–๐™ฃ ๐™จ๐™š๐™š๐™ข ๐™– ๐™—๐™ž๐™ฉ ๐™—๐™ž๐™ฏ๐™–๐™ง๐™ง๐™š.
๐™Ž๐™ก๐™ค๐™ฉ๐™จ ๐™™๐™ค๐™ฃ'๐™ฉ ๐™ฅ๐™๐™ฎ๐™จ๐™ž๐™˜๐™–๐™ก๐™ก๐™ฎ ๐™ข๐™ค๐™ซ๐™š ๐˜ฟ๐™Š๐™ˆ; ๐™ฉ๐™๐™š๐™ฎ ๐™ง๐™š๐™ฃ๐™™๐™š๐™ง ๐™ž๐™ฉ ๐™–๐™ฉ ๐™–๐™ฃ๐™ค๐™ฉ๐™๐™š๐™ง ๐™ก๐™ค๐™˜๐™–๐™ฉ๐™ž๐™ค๐™ฃ ๐™ž๐™ฃ๐™จ๐™ž๐™™๐™š ๐™ฉ๐™๐™š ๐™จ๐™๐™–๐™™๐™ค๐™ฌ ๐˜ฟ๐™Š๐™ˆ.
So to test if something is "in" a slot
you need to check for slot=? attributes on lightDOM elements
and double check if that <slot name=? > actually exists in shadowDOM
Or vice versa
Or hook into the slotchange Event, but that is not Testing
pseudo code:
for the vice-versa approach; can contain errors.. its pseudo code..
function processDOMnode( node ){
if (node.shadowRoot){
// query shadowDOM
let slotnames = [...node.shadowRoot.querySelectorAll("slot")].map(s=>s.name);
// query lightDOM
slotnames.forEach( name =>{
let content = node.querySelectorAll(`[slot="${name}"]`);
console.log( "slot:" , name , "content:" , content );
});
// maybe do something with slotnames in lightDOM that do NOT exist in shadowDOM
// dive deeper
this.shadowRooot.children.forEach(shadownode => processDOMnode(shadownode));
}
}

CAPL on sysvar_change procedure with sysvar of custom struct data type

according to the manual,
The procedure on sysVar is called only when the value of the variable
changes. It can also be written as on sysVar_change. If you want to be
notified of value updates to the variable which donโ€™t change the
value, you should use on sysVar_update instead.
In my example scenario, I have a system variable s::sysv of custom Struct Data Type X, where X has two fields: A and B.
In my CAPL script I put the following:
on sysvar_change s::sysv.A
{
// do stuff
}
Expected output is to do stuff only when s::sysv.A changes. However, since s::sysv.B is often updated when my simulation is running, then the procedure on sysvar_change s::sysv.A is called a lot more times than I expect, even if A doesn't change its value.
I don't understand why, and I'm putting a lot of workaround in place to avoid this, can anybody help?
Edit:
according to one reply, the event handler is not the struct element, but still the variable. However, the keyword this is now pointing to the struct element and not to the variable.
This bit of the manual is also relevant:
You can also react in the same way to value changes of specific
elements of a system variable of type struct or generic array. For
this, add the element to the name of the variable.
I have tried this functionality in the latest CANoe and it works as expected. The following is my code.
on key 'a'
{
#sysvar::Var_Struct1.StructMem1++;
}
on key 'b'
{
#sysvar::Var_Struct1.StructMem2++;
}
on sysvar_change Var_Struct1.StructMem1
{
write("StructMem1 value changed");
}
on sysvar_change Var_Struct1.StructMem2
{
write("StructMem2 value changed");
}
Whenever I press the key 'a' or 'b', the corresponding event is triggered.
Your variable is s::sysv. The event handler is called whenever the value of the variable changes. No matter whether A or B changes.
There is no way to restrict it only to certain changes of the value.
This is similar to the fact that you can also not be notified when, e.g. only the 3rd bit of an integer changes.
To me, it seems best to reconsider your setup and ask yourself, whether using the struct is the right approach, or whether it might be better to use two separate system variables A and B.

Mixpanel: Undo or delete an event

On MixPanel, I track an event like so:
mixpanel.track('Action A')
I allow visitors to undo their actions when filling out a sign-up form. I would like to be able to send another event to undo the previous event:
mixpanel.decrement('Action A')
However, the decrement function in Mixpanel is only available on user properties, not events. I don't have unique_ids on these events because it's server-side and triggered by anonymous users, but I would like the ability to increment and decrement an accurate count of Action A. How can I delete the initial event or decrement the count by 1?
There is no way to delete events that are ingested by Mixpanel with no unique_id's connected to them.
It is possible to hide them so they don't appear in reports, but that sounds like it will defeat the purpose of what you are trying to accomplish.
Mixpanel does have documentation on making an incremental super property, which is tied to events and not people. A super property is a property that is sent with every event. The method mixpanel.register() is what is used to create Super Properties, but it also allows values to be overwritten which is one way to build an incremental/decremental event property.
This unfortunately involves building a function, but it should serve as a workaround. If you are using JS the function would look something like:
//define the incrementing function
incrementer = function(property) {
value = mixpanel.get_property(property);
update = {}
//Ensure that 'value' has a type = number
if(value && typeof(value) == 'number') {
update[property] = value +1;
}
else {
update[property] = 1
}
mixpanel.register(update);
};
There is some documentation on this here.
I think this will involve a little bit of tweaking depending on your implementation, but let me know if that helps solve it.

Is Durandal router "cacheViews" property accessible and changeable at runtime?

I'm building a Durandal SPA that may benefit from a cacheViews setting of true or false, depending on the individuals usage of the app.
This is how I currently have it set in shell.html:
<div class="page-host" data-bind="router: { cacheViews:true }"></div>
And in a child router like in samples/index.html:
<div>
<!--ko router: { cacheViews:false }--><!--/ko-->
</div>
1) Can these be changed at runtime? If so, how?. Does it matter that one is using data-bind and the other is using ko comment style?
2) How granular is this value? Is it per "router" or per "route"? As you can see, I have a parent router and a child router, so there are 2 places in my html code where I can set cacheViews. From my testing, it appears as if they are independent of each other. Can anyone confirm? Can I set this value on individual routes like /#page1, /#page2, /#samples/list, etc?
3) Because the page event life-cycle is different between true/false I need to have some specific logic in my vm depending on this value. How can I retrieve it for the current route?
Thanks
1) Can these be changed at runtime? If so, how?
With any knockout binding, the binding is recomputed if the bound value triggers subscriptions. That means cacheViews can be changed at runtime if it is an observable.
I'm not sure if the right way to do this is ko.observable({ cacheViews: false }) or { cacheViews: ko.observable(false) }. In fact, I believe which one will work is somewhat dependent on the version of knockout you're running, but one of them will work.
2) Does it matter that one is using data-bind and the other is using
ko comment style?
No.
3) How granular is this value? Is it per "router" or per "route"?
Per router. More specifically, per binding.
4) Can I set this value on individual routes like /#page1, /#page2,
/#samples/list, etc?
There's nothing out of the box that lets you set cacheViews per route. cacheViews is processed in the compose binding, which is being called under the hood in the router and so you would need to hook in there. However, Durandal is excellent about exposing hooks at every part of the lifecycle for custom logic. I'm sure with a little digging you can come up with your own customization to handle this.
5) Because the page event life-cycle is different between true/false I
need to have some specific logic in my vm depending on this value. How
can I retrieve it for the current route?
You would want to create the observable from (1) in your viewModel and have your associated view read it, like any other observable in knockout. Then, you will be able to access the current value directly off the viewModel. For example:
viewModel
viewModel = {
settings: ko.observable({
cacheViews: false
})
}
view
<!-- ko router: settings -->

With boost::msm eUML, if I give attributes_ << to the state machine or state, how do I (re)set them?

If I add attributes to an event, I know I can then use the event name like a function...
BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(*someEvent*, *someAttributeList*)
someStateMachine.process_event(
someEvent (
valueOfSomeAttribute1, // sets the attribute value here
valueOfSomeAttribute2))
and that inside an action I can this back by writing
evt.get_attribute(someAttribute1); // retrieve the attribute value
Now, if I set an attribute for an entire machine, like so:
BOOST_MSM_EUML_DECLARE_STATE_MACHINE((transition_table,
init_ << initState,
Entry_Action,
Exit_Action,
attributes_ << someAttribute1 << someAttribute2,
configure_<< someConfigurationStuff ),
newStateMachineType)
How do I go about setting a value for someAttribute1?
Same question for states:
BOOST_MSM_EUML_STATE(
(someEntryAction,
someExitAction,
attributes_ << someAttribute1,
configure_<< someConfigurationStuff)
,newStateName)
How do I go about setting a value for someAttribute1?
Finally,
Is there a way to change the attributes after the object is created?
For instance, I'd like to have an attribute for the state machine, and in one of my states, remember some piece of information that I can store in the state machine. (In this case, I want to store a socket.)
Thanks.
How do I go about setting a value for someAttribute1?
You can:
change the reference you just got (get_attribute returns a reference): ++evt.get_attribute(someAttribute1).
use the functors to write the attribute in your table directly. For example, following action is possible: /++fsm_(someAttribute1)
For states, you can do the same. And for state machines, well, ditto.
Again, you can either use the Fsm template parameter in your actions, or the functors (fsm_, event_, etc.)
You can find good example of all in the examples or tests (for example test/CompositeEuml.cpp or test/AnonymousEuml.cpp).
HTH,
Christophe

Resources