I've found a tool for table management and I love it. The only problem is that it needs initializing as shown below, which means that it only gets called once - when the page's been loaded.
mounted: () => {
$(document).ready(function () {
$("table").DataTable();
}
}
I've put the code in the mounted section and I also tried to use created. For some reason (probably rendering order), I have to keep it in ready method - otherwise it doesn't come up.
This poses two problems for me. Firstly, I'm reusing the same component as a matrix view in multiple components (it's dynamically set up based on the store). Secondly, when I navigate from the page and then go back, it doesn't reinitialize.
How should I make the code inside ready method to run each time the component gets in the view?
I've googled a bit but it's a not so common issue and I'm out of ammo. The best hit I got was the life cycle of the component where I couldn't see anything ground breaking. I also found that the data table instance needs to be destroyed but that only helps if I get to invoke the stuff, which seems not to happen.
I believe you just need to do following:
mounted () {
$("table").DataTable();
}
as $(document).ready detects that the document is ready, but in vue case, mounted is called after the instance has just been mounted where el is replaced by the newly created vm.$el, which should be equivalent of document.ready.
I have also checked in vue 2.x.x that mounted gets called if you navigate from the page and then go back.
If this code is dependent on data being loaded and re-rendering of component, you can use updated instead of mounted which is called after a data change causes the virtual DOM to be re-rendered and patched.
updated () {
$("table").DataTable();
}
Related
I am working on a Stenciljs component where an array needs to be filled with fetch calls when the component is created. The array's contents will change throughout the component's life cycle. Right now the only way I can get it to be filled is by having a button that when clicked calls the fill method. How can I fill the array as the component is created or loaded so there is no user interaction required? In other words, how can I call the fill method without a button?
Thank you for your help.
You can use the Lifecycle Methods, in your case componentWillLoad is probably the right one.
componentWillLoad() {
this.fill();
}
You can also return a Promise which will prevent the component from rendering until the Promise is resolved.
There's also connectedCallback which will be called each time the component is added to the DOM (even if it moves).
One of the things I'm still hung up on with Angular is understanding all of the lifecycle hooks and when to use what when.
I often need to stick a little plain'ol JS into a component to deal with some DOM issue that, alas, I can't handle via Angular (usually because I'm working within a component that needs to access some elements from a parent component's rendered DOM that we have no access to the Angular code for...I do realize this isn't the 'proper' Angular way but...).
An example right now is a few pages I'm working on that use a component need that needs to hide a DOM element on the page that isn't a part of this component. I need to use JS for this (a whole other story why CSS isn't the solution for this one).
But I only want to do this once the DOM is fully rendered.
Sometimes this seems to work when inserted into ngAfterViewInit -- but sometimes not. It seems that there's no guarantee the full DOM is ready using that lifecycle.
Moving that logic into ngAfterViewChecked does work. However, the issue with ngAfterViewChecked is that it's getting called dozens of times on some pages--and the first few times it's called, the DOM isn't even ready. Not the end of the world, but there's no reason for me to be attempting to grab the same DOM object 40 times per page render. I somewhat remedy this by adding a boolean flag to tell this bit of JS to stop running once it finds the DOM elements but that's hacky.
So my question is: What is the proper (if there is one) way to handle JS manipulation of the DOM after the DOM is fully rendered in the context of an Angular component? Is it to use one of the angular lifecycle events? Something else? Or this whole idea of manipulating DOM objets outside of the component I'm working in just anathema to the 'Angular way' so just isn't something accommodated within Angular?
I have one ReactJS App which I reduced to the minimum as possible on the diagram below:
Side note: On this App I use Redux to manage state changes.
This App contains:
Component: UploadScreen with an image holder and a button. When that button is clicked, the user gets displayed a Popup Window which let him to pick an image from his device file system. Then that image is displayed on the image holder.
Component: AuxWidget which is a totally different component (needs to be separate) which also contains a button that when it is clicked it should popup the Select File window. I was thinking in something like triggering the click event of the first button.
Any idea on how to achieve that?
First I though about using Redux but I think that's not a too good idea because even though you can send messages with it from one component to another, that causes a render update and I don't want that.
Also, I was thinking on using jQuery but that's not the best approach when it comes to ReactJS.
Also, I thought about using the attribute: ref="foo" to get a reference to the other component but I think that's normally done when you want the interaction to be between parent and child components.
Also, I was thinking about EventEmmitter but I don't know if that's the best approach on this case (I'm using Redux to manage the state changes between components).
One of the best ways I can suggest using RxJS, you can create a Subject and pass it to your components. In one component you will need to subscribe to it and whenever you will call next on your subject from the second component, the other will be notified, so you can trigger open popup. You can even create your own implementation for this in case you don't want to add new library to your project.
The upload window could be triggered when a certain state in the app changes. The relevant state on the app could be changed from different places, like from AuxWidget and UploadScreen. That way they are not coupled with the upload window. They merely call a function that is passed to them and that function changes the state on the app and it will display the window.
If you have a shared component between two unrelated component I think it is best to lift that common component and let its state sit on a higher level.
If I understand things correctly, your primary concern is code-reuse as opposed to wanting to call a sibling method. Basically, you want a SelectFilePopup component that can be re-used (open/closed) cleanly. I think React Portals could be a good solution for this. I found a good example (https://github.com/Assortment/react-modal-component/blob/master/src/components/Modal.js) of how a Modal can be isolated into a component and be called anywhere in the codebase.
The usage of the Modal looks like this (copied and slightly modified from App.js in the github project above)
import Modal from './components/Modal';
<Modal><div>Click me to open Modal</div></Modal>
And the Modal component implementation (simplified)
render() {
return (
<Fragment>
<ModalTrigger
onOpen={this.onOpen}
/>
{isOpen &&
<ModalContent/>
}
</Fragment>
)
}
By default the Modal component shows a trigger (i.e button) when isOpen state is false. Once clicked, and isOpen switches to true, the ModalContent (i.e can be the FilePickerPopup) is dynamically created and attached to document body. You can check out the source code for more details. I think its a very clean solution to modals. So in your case, your code could end up looking something like this
UploadScreen.js
import FileSelectPopup from './components/FileSelectPopup';
<FileSelectPopup>{Upload Image}</FileSelectPopup>
AuxWidget.js
import FileSelectPopup from './components/FileSelectPopup';
<FileSelectPopup>{Upload Image or some other text}</FileSelectPopup>
So basically, AuxWidget doesn't even need to know about where the FileSelectPopup is located at. It's an independent component that can be called anywhere. The caveat is that the Modal implementation in the project I linked to is not a singleton (although it can be modified to be one). So if AuxWidget and UploadScreen are visible to the user at the same time, clicking both Upload Image buttons will create two instances of the Popup.
I would define the function in the parent component and pass it to both children as props
the problem is this, I have a little jquery code to handle a menu with dropdowns lists inside of it, at first I have a login component when the user logs-In I call that code from the mounted hook of the main component (there is window onClick inside the code and I put a console.log to make tests), and everything works fine, now when the user logs-out and then another user (or the same user) logs-in again the code its been called again (because of the mounted hook) and when he/she clicks something I get two console.logs.
if the page is reloaded everything works great and the function is called once but as soon as the user logs-in, logs-out, logs-in, etc..... then I get multiple console.logs.
What is the proper way to load this jquery code (I know that everything jquery does, vue do it as well).
How to destroy/re-call that code without having it twice.
a dirty solution that comes to me is to load the code in the main .html file and then reload the page when the user is in the main page.
thanks in advance.
UPDATE:
Hi, thanks for your replies, Im using vue-router so in the loggin component there is no need of the jquery code, I need it in the dashboard component where the menu lives thats why I load the code in the mounted hook, a piece of code is just a window.addEventListener('click', ......) and this code shows a console.log just for testing, so when the user logs-in the jquery its been loaded, when the user logs-out and then log-in the jquery code its been loaded again so I will have 2 console.logs when the user clicks the window, I'll try your solutions.
FINAL:
Well, thanks to everyone, what I ended up doing was to use jquery's bind function $(window).bind('click' .....) and then in the beforeDestroy hook just $(window).unbind() and eveything works great.
First I would recommend to not use jQuery together with Vue! As you already said, Vue is a framework which can manipulate the DOM without needing jQuery's help.
But anyways, a quick 'vue way' solution is to use the beforeDestory hook on your login component, and clear your jQuery events.
beforeDestroy() {
// Clean the listeners.
}
P.S. For next time, would be better to show some code :)
If I understand your problem correctly, you need to check to see if your handler exists already before setting it. That way if it already exists you won't add it again.
In mounted() check if typeof window.onclick == 'object' if it does, then you haven't set it yet and you can set your handler window.onclick = function() {}
If it is set then typeof window.onclick would equal 'function'
I have just started using React, and a couple of times I have thought to myself: "Why is there no componentDidRender event?".
Say that I have a component that renders a table to the DOM, and I want to use bootstrap-sortable on this table to allow the user to sort on whatever column he wants. In the case of bootstrap-sortable you need to run $.boostrapSortable() after the table is drawn, in order to initialize the plugin.
As I see it, there are two handlers on a React component that would be logical to consider to use for this purpose:
componentDidMount: This does not work because the DOM does not seem to be updated at this point of the execution.
componentDidUpdate: This could possibly work, but it does not fire on the initial render.
I am not saying that React is actually missing a componentDidRender function, because I assume that there is a perfectly logical explanation as to why it is not there. I am just asking if someone could explain why such a function is not present, and what would be the "React way" to handle a case like the one above.
In componentDidMount you can do: this.getDOMNode() to get a reference to the underlying DOM for that component. So if you do want to use your mounted component with jQuery you can do:
componentDidMount: function() {
$(this.getDOMNode());
}
http://facebook.github.io/react/docs/working-with-the-browser.html
Here's a fiddle which shows jQuery acting on the DOM node of a react component:
http://jsfiddle.net/sa5e88ys/1/
As you can see, it adds a border to the div as expected. If you're still having problems I guess it could be with the plugin you're using rather than jQuery or react?
Although there's no componentDidRender, you can make a method with the desired behavior and call it in both componentDidMount (which is only called after the first render) and componentDidUpdate (which is called after every render but the first).
Also, this is the preferred way of getting a ref to a DOM node from within the component:
https://facebook.github.io/react/docs/refs-and-the-dom.html