how to force a Polymer.Element extended class to execute its lifecycle without attaching it to the dom? - polymer

Consider this element (minimal for the purpose of the question) :
class MyCountDown extends Polymer.Element
{
static get is () { return 'my-count-down'; }
static get properties ()
{
return {
time: { /* time in seconds */
type: Number,
observer: '_startCountDown'
},
remains: Number
}
}
_startCountDown ()
{
this.remains = this.time;
this.tickInterval = window.setInterval(() => {
this.remains--;
if (this.remains == 0) {
console.log('countdown!');
this._stopCountDown();
}
}, 1000);
}
_stopCountDown () {
if (this.tickInterval) {
window.clearInterval(this.tickInterval);
}
}
}
customElements.define(MyCountDown.is, MyCountDown);
If I get one instance and set the property time,
let MyCountDown = customElements.get('my-count-down');
let cd = new MyCountDown();
cd.time = 5;
the property time changes but the observer and the _startCountDown() function is not called. I believe Polymer is waiting for the Instance to be attached to the DOM because in fact when I appendChild() this element to the document the count down starts and after 5 seconds the console logs 'countdown!' as expected.
My goal is to execute this lifecycle without attaching anything to the document because the instances of MyCountDown are not always attached to the view but/and they need to be live-code between the different components of my web application.
One solution is to attach the new MyCountDown instances to an hidden element of the dom to force the Polymer lifecycle but I think this is not so intuitive.

I don't know the exact place to call, but the problem you have is that the property assessors are not in place.
I think you might get a clue from this talk https://www.youtube.com/watch?v=assSM3rlvZ8 at google i/o
call this._enableProperties() in a constructor callback?

Related

Forge tool handleButtonDown and handleButtonUp functions not getting called

I was looking at the sample code for the tutorial at https://forge.autodesk.com/blog/custom-window-selection-forge-viewer-part-iii which is located at https://github.com/Autodesk-Forge/forge-rcdb.nodejs/blob/master/src/client/viewer.components/Viewer.Extensions.Dynamic/Viewing.Extension.SelectionWindow/Viewing.Extension.SelectionWindow.Tool.js as well as the documentation at https://developer.autodesk.com/en/docs/viewer/v2/reference/javascript/toolinterface/ --- Most of these functions are getting called properly in my tool such as handleSingleClick, handleMouseMove, handleKeyDown, and so on, but two of them are not getting hit -- handleButtonDown and handleButtonUp. I was using viewer version 3.3.x but I have updated to use 4.0.x thinking that that might help to resolve the problem, but the same issue occurs in both versions. Thanks for any help.
The following code block from theAutodesk.Viewing.ToolController#__invokeStack(), _toolStack stands for activated tools in the ToolController, the method stands for callback functions started with handle, i.e. handleSingleClick, handleMouseMove, handleKeyDown, handleButtonDown, handleButtonUp, etc.
for( var n = _toolStack.length; --n >= 0; )
{
var tool = _toolStack[n];
if( tool[method] && tool[method](arg1, arg2) )
{
return true;
}
}
Based on my experience, if there is a handle function such as handleButtonDown or handleButtonUp executed before your custom tools' and returned true, then your handles will never be called.
Fortunately, Forge Viewer (v3.2) starts invoking a priority mechanism for custom tools registered in ToolController. ToolController will use the priority number to sort the tools in it, and the priority number of each tool is 0 by default. You can override the priority to make your tools be hit before other tools like this way, to add a function getPriority() to return a number greater than 0:
this.getPriority = function() {
return 100;
};
I found out that when using ES6 and the class syntax, extending your tool from Autodesk.Viewing.ToolInterface will prevent the overrides to work properly, probably because it is not implemented using prototype in the viewer source code.
You can simply create a class and implement the methods that are of interest for your tool:
// KO: not working!
class MyTool extends Autodesk.Viewing.ToolInterface {
getName () {
return 'MyTool'
}
getNames () {
return ['MyTool']
}
handleButtonDown (event, button) {
return false
}
}
// OK
class MyTool {
getName () {
return 'MyTool'
}
getNames () {
return ['MyTool']
}
handleButtonDown (event, button) {
return false
}
}

Iron-signal alternate in Polymer 2?

I have been using iron-signals almost everywhere in my application.
Now I was upgrading my polymer 1 application to polymer 2 and I found that <iron-signals> is not used anymore.
What is the alternate path to achieve the same. I basically want to pass data between different pages in my web app.
You should be able to simply dispatch events on window from one element and listen for them in other elements.
Example:
// Element 1
class FooElement extends Polymer.Element {
connectedCallback() {
super.connectedCallback()
}
ready() {
super.ready()
window.addEventListener('bar-was-called', e => {
console.log(e.detail) // logs 'hello-bar'
})
}
}
// Element 2
class BarElement extends Polymer.Element {
connectedCallback() {
super.connectedCallback()
}
ready() {
super.ready()
}
doBar() {
window.dispatchEvent(new CustomEvent('bar-was-called', {
detail: 'hello-bar'
}))
}
}
Side note
Keep in mind that iron-signals was removed for a reason. AFAIK it's that it promotes a hard-to-debug communications architecture.
From <iron-signals> README:
Note: avoid using iron-signals whenever you can use a controller (parent element) to mediate communication instead.
Iron Signals have been deprecated in polymer 1.
Replace uses of iron-signals with iron-meta.

Refresh previous screen on goBack()

I am new to React Native. How can we refresh/reload previous screen when returning to it by calling goBack()?
Lets say we have 3 screens A, B, C:
A -> B -> C
When we run goBack() from screen C it goes back to screen B but with old state/data. How can we refresh it? The constructor doesn't get called 2nd time.
Adding an Api Call in a focus callBack in the screen you're returning to solves the issue.
componentDidMount() {
this.props.fetchData();
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
() => {
this.props.fetchData();
}
);
}
componentWillUnmount() {
this.willFocusSubscription.remove();
}
UPDATE 2023: willFocus event was renamed to focus
componentDidMount() {
this.props.fetchData();
this.focusSubscription = this.props.navigation.addListener(
'focus',
() => {
this.props.fetchData();
}
);
}
componentWillUnmount() {
this.focusSubscription();
}
How about using useIsFocused hook?
https://reactnavigation.org/docs/function-after-focusing-screen/#re-rendering-screen-with-the-useisfocused-hook
const componentB = (props) => {
// check if screen is focused
const isFocused = useIsFocused();
// listen for isFocused, if useFocused changes
// call the function that you use to mount the component.
useEffect(() => {
isFocused && updateSomeFunction()
},[isFocused]);
}
For react-navigation 5.x use
5.x
use
componentDidMount() {
this.loadData();
this.focusListener = this.props.navigation.addListener('focus', () => {
this.loadData();
//Put your Data loading function here instead of my this.loadData()
});
}
For functional component
function Home({ navigation }) {
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
loadData();
//Put your Data loading function here instead of my loadData()
});
return unsubscribe;
}, [navigation]);
return <HomeContent />;
}
On your screen B constructor will work like magic :)
this.props.navigation.addListener(
'didFocus',
payload => {
this.setState({is_updated:true});
}
);
Yes, constructor is called only for the first time and you can't call it twice.
First: But you can separate the data getter/setter from the constructor and put it in a function, this way you can pass the function down to the next Scene and whenever you're going back you may simply recall the function.
Better: You can make a go back function in your first scene which also updates the scene while going back and pass the go back function down. This way the second scene would not be aware of your update function which is reasonable.
Best: You can use redux and dispatch a go-back action in your second scene. Then in your reducer you take care of going back & refreshing your scene.
The built in listener function which comes with React-Navigation would be the easiest solution. Whenever a component is 'focused' on a again by navigating back, the listener will fire off. By writing a loadData function that can be called both when loading the Component AND when the listener is notified, you can easily reload data when navigating back.
componentWillMount(){
this._subscribe = this.props.navigation.addListener('didFocus', () => {
this.LoadData();
//Put your Data loading function here instead of my this.LoadData()
});}
Easy! insert the function inside useFocusEffect(func)
import { useFocusEffect } from '#react-navigation/native'
I have a similar situation and the way i refreshed was to reset the route when the back button is pressed. So, what happens is when the back button is pressed the screen is re-pushed into the stack and the useEffect on my screen loads the data
navigation.reset({
index: 0,
routes: [{ name: "SCREEN WHERE THE GOBACK BUTTON SHOULD GO" }],
});
Update for react-navigation v5 and use the React Hooks. Actually, the use is the same with react base class. For more detail, please checkout the documentation here
Here is the sample code:
function Profile({ navigation }) {
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// do something
});
return unsubscribe;
}, [navigation]);
return <ProfileContent />;
}
As above code, We add the event listener while the variable navigation change then We do something like call function refresh() and finally, we return the function for removing the event listener. Simple!
I think we have a very easy way (which works in 2021) to do so. Instead of using goBack or navigate, you should use push
this.props.navigation.push('your_route_B').
You can also pass params in the same way as we pass in navigate.
The only difference b/w navigate and push is that navigate checks if the route which we are passing exists in the stack. Thus taking us to the older one but, push just sends us there without checking whether that is in the stack or not (i.e, whether the route was visited earlier or not.)
This can be achived by useFocusEffect from '#react-navigation/native'
useFocusEffect will effect every time when screen is focus
Ref: https://reactnavigation.org/docs/use-focus-effect/
import { useFocusEffect } from '#react-navigation/native';
function Profile({ }) {
useFocusEffect(
React.useCallback(() => {
//Below alert will fire every time when profile screen is focused
alert('Hi from profile')
}, [])
);
return // ...code ;
}
You can use this event: navigation.addListener('focus'
And you can implement like this:
const Cards = ({ navigation }) => {
...
useEffect(() => {
const load =async ()=>{
const a = await selectGlobalCards()
}
navigation.addListener('focus',() =>{
load();
});
}, [])
or you can use useIsFocused, and you can use that as a dependecy for useEffect
import { useIsFocused } from '#react-navigation/native'
const Cards = ({ navigation }) => {
const isFocused = useIsFocused()
useEffect(() => {
const load =async ()=>{
const a = await selectGlobalCards()
}
load()
}, [isFocused])
For react navigation (5.x), you just need to add a focus subscription and put your component initializing logic in a separate function like so:
componentDidMount() {
this.init();
this.didFocusSubscription = this.props.navigation.addListener(
'focus',
() => {
this.init();
}
);
}
init = async () => {
//fetch some data and set state here
}
If you're trying to get new data into a previous view, and it isn't working, you may want to revisit the way you're piping data into that view to begin with. Calling goBack shouldn't effect the mounting of a previous component, and likely won't call its constructor again as you've noted.
As a first step, I would ask if you're using a Component, PureComponent, or Functional Component. Based on your constructor comment it sounds like you're extending a Component class.
If you're using a component, the render method is subject to shouldComponentUpdate and the value of your state is in your control.
I would recommend using componentWillReceiveProps to validate the component is receiving the new data, and ensuring its state has been updated to reflect the new data.
If you're using the constructor to call an API or async function of some kind, consider moving that function into a parent component of both the route you're calling goBack from and the component you're wanting to update with the most recent data. Then you can ask your parent component to re-query the API, or update its state from a child component.
If Route C updates the "state/data" of the application, that update should be propagated to a shared parent of routes A, B and C, and then passsed down as a prop.
Alternatively, you can use a state management solution like Redux to maintain that state independent of parent/child components - you would wrap your components in a connect higher-order component to get the latest updates any time the application state changes.
TL;DR Ultimately it sounds like the answer to your question is rooted in where your application state is being stored. It should be stored high enough in your component hierarchy that each route always receives the latest data as a prop, passed from its parent.
Thanks to #Bat.
I have spent a lot of hours on finding the answer and finally, I got a basic solution which is working according to my needs. I was quite worried though.
Simply make a function like this in your previous activity make sure to bind it.
changeData(){
var mydata= salesmanActions.retrieveAllSalesman();
this.setState({dataListFill: mydata});
alert('' + mydata.length);
}
Simple, then in constructor bind this,
this.changeData= this.changeData.bind(this);
After that, as I am using react native navigation, so I will simply pass this function to the second screen just like the code below:
onPress={() => this.props.navigation.navigate('Add Salesman', {doChange:
this.changeData} )}
So when the new screen registered as "Add Salesman" will be called, a parameter named "doChange" which is assigned a function will also be transfered to other screen.
Now, in other screen call this method anywhere, by :
this.props.route.params.doChange();
It works for me. I hope works for you too, THANKS for the idea #Bat.
let we have 2 screen A and B , screen A showing all data . and screen B is responsible for adding that data. we add some data on using screen B and want to show instant changes on Screen A . we use below code in A
componentDidMount(){
this.focusListener = this.props.navigation.addListener('focus', () => {
thi`enter code here`s.startData();
//Put your Data loading function here
});
}
This is what you can do with react navigation v6.
Create a separate stack in stack navigator like this:
const PropertyListStack = () => {
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name={ROUTE_PROPERTY_LIST} component={PropertyList}/>
</Stack.Navigator>
)};
Now, whenever you you want to reload your initial screen navigate using this stack. like this:
navigation.navigate(
ROUTE_DASHBOARD_TABS,
{screen: ROUTE_PROPERTY_LIST_STACK}
);
This will reload your base screen. In my case base screen is PropertyList.
If you know the name of the Screen you want to go , then you can use this code.
navigation.navigate("Screen"); navigation.replace("Screen");
This code works fine if you don't have nested routes.
This answer assumes that the react-native-navigation library is being used, which is unlikely because it doesn't actually have a goBack() method...
The constructor doesn't call a second time because screen A and B are still rendered (but hidden behind screen C). If you need to know when screen B is going to be visible again you can listen to navigation events.
class ScreenB extends Component {
constructor(props) {
super(props);
// Listen to all events for screen B
this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
}
onNavigatorEvent = event => {
switch (event.id) {
case 'willAppear':
// refresh your state...
break;
};
}
Other events: willDisappear, didAppear, didDisappear
An alternate solution to your problem is to use a state management solution like Redux to provide the state to all screens whenever it is updated (rather than just on screen transitions. See old react-native-nav/redux example.

React upgrade: "this" visibility in getDefaultProps

I am upgrading some older react component I inherited (v0.10.0) to work with the latest version of react (v0.14.8).
The following scenario stopped working:
// within a react component
onClick: function() {
// DO SOMETHING
}
getDefaultProps: function () {
return {
someProp: 'prop',
onClick: this.onClick
}
}
This is easily resolved moving the code into an anonymous function, like the following:
getDefaultProps: function () {
return {
someProp: 'prop',
onClick: function() {
//DO SOMETHING
}
}
}
My question is: why has the visibility of 'this' changed at that level and what's the best way to refactor this code? And what if I had-to/wanted-to use 'this' at that level?
Any help appreciated, as a disclaimer I am a react super-beginner!
The result of getDefaultProps() is shared across all instances of a component. That means that the result can't rely on any particular instance of the component. The reason it changed is likely because of the performance benefit from caching, although I can't say for sure.
As for refactoring the code, I'm not sure there's a silver-bullet here. From my perspective what you currently have seems like an anti-pattern. Props are meant to be passed in by consumers that have no knowledge of the inner workings of the component, so it seems odd that a default value for a prop would depend on the inner workings. Without knowing exactly what you're doing, I would say your best bet is to just use null as the default value for the prop, then check the value at runtime when you do have access to the this context.
handleSomeAction() {
if (!this.props.onClick) {
// DO SOMETHING
}
}

NPAPI Plugin[FireFox]: Invoke() / HasProperty() / HasMethod() not getting called

I am developing NPAPI Plugin for Firefox on windows. here is the my java script:
document.addEventListener('load', documentLoad, true);
function loadPlugin(doc)
{
var objWebMon = doc.getElementById("my_firefox");
if(!objWebMon)
{
var objWebMonEmbed = doc.createElement('embed');
objWebMonEmbed.setAttribute('id', 'my_firefox');
objWebMonEmbed.setAttribute('type', 'application/npplugin');
objWebMonEmbed.setAttribute('style', 'height: 10px; width:10px; display:block;');
if(doc.body)
{
doc.body.insertBefore(objWebMonEmbed, doc.body.firstChild);
}
}
}
function documentLoad(event) {
try
{
var doc = event.originalTarget; // doc is document that triggered "onload" event
loadPlugin(doc);
var myplugin = doc.getElementById('my_firefox');
if(myplugin)
{
myplugin();
myplugin.myAction();
}
} catch(err)
{
}
}
as I am calling myplugin()
bool ScriptablePluginObject::InvokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result)
gets called sucessfully but on calling function myplugin.myAction()
bool ScriptablePluginObject::Invoke(NPIdentifier name, const NPVariant *args,
uint32_t argCount, NPVariant *result)
function doesn't called. I have declared myAction inside ScriptablePluginObject::HasProperty(NPIdentifier name) even HasProperty method is not getting called.
Inside catch block i am getting this error. TypeError: fasso.myAction is not a function.
Here are a couple of things to try:
Use an object tag instead of an embed -- I've had more consistent success with object tags, despite the wide popularity of using embed
Never ever ever set the type of an object or embed tag before you add it to the DOM -- doing so causes it to instantiate the plugin and then puts it in a kinda weird state when it gets moved. I don't think this is causing your issue this time, but it's worth trying.
You may need a slight delay between inserting hte plugin into the DOM and using it. Try adding a setTimeout with a delay of 50ms and accessing the plugin in the callback function.
Honestly, #3 is the one I think most likely will make a difference, but I present the other two as they have bitten me on weird things in the past. Good luck!