I'm trying to use a Jump-To-Div kind of feature using document.getElementById().scrollIntoView() in a vue component.
The feature works fine if I call the function when I'm in the component. But if I try to call the function from the parent component using a ref, the element is not scrolled into view.
The parent component is a page and the children components are tabs in the page.
This is the child Vue Component in the parent component :-
<el-tab-pane label="Solution Details" name="Solution Details" v-loading="loading">
<solution-details
:formData="response"
ref="solutionDetails"
#done="$emit('done')">
</solution-details>
</el-tab-pane>
So there is a SolutionDetails.Vue child component which has a ref="solutionDetails".
I am calling this method in the parent component using the ref for the child component:
handleClick(command) {
this.activeTab = 'Solution Details';
this.$refs.solutionDetails.showCurrent(command);
},
There is a showCurrent function in the child component which should get executed for a argument "command". This is that function in the child component.
methods: {
showCurrent(index) {
document.getElementById(index).scrollIntoView();
},
As you can see, showCurrent should get the element in the page and should scroll into view. If SolutionDetails.vue is the active tab, then the corresponding element is being scrolled into view perfectly fine. But I'm executing the parent function from some other tab, then the this.activeTab = 'Solution Details'; is executing, ie. the active tab is changing to SolutionDetails.vue but the requested element is not scrolled into view.
What should I do to scroll to a element when some other tab is the activeTab?
The problem is that scrollIntoView is called before the tab is rendered on the page because renders are asynchronous. Essentially, when you call
this.activeTab = 'Solution Details';
Vue does not immediately render the page, it just queues a render. However immediately after that you tell Vue to look for the rendered element and scroll to it. It's not there yet.
I think my first stab at solving this would be to use $nextTick.
this.$nextTick(() => this.$refs.solutionDetails.showCurrent(command))
That should wait for the render that needs to happen to occur before you attempt to scroll into view.
Actually you have to reference the element in the component. Something like this for example:
this.$refs.[ref name here].$el.scrollIntoView({ behavior: 'smooth' });
1: Give the element you want a ref name. Inside the function on the parent element , it's better to try to console.log(this.$refs.[ref_name]) to check which part will have the effect. It is not always the parent element. Really depends on your CSS structure. When submit the form and get error message, then the view go to the part you want.
if(this.errorMessage) {
this.$refs.[ref_name].scrollIntoView();
}
2: For Vue.JS Multi Steps Form
It is normal to use step for process like signup.
What we have now is the view will stay the same as the first step.
For example, if step one form is quite long with scrolling then go to step two the view will stay at the bottom.
What we can do here to keep the view on the top:
Give the top element a ref name like ref="top".
In all the steps submit functions, just add this to keep the next page on the top:
this.$refs.top.scrollIntoView();
Related
In my routes I have set-up a slug for a particular route like so:
Main route: /members
Sub route: /members/:slug
Now when I go to www.website.com/members/some-slug
I will try to detect whether there's a slug or not with:
if (this.props.match.params.slug) {
// If have slug, open a modal window with data that corresponds to that slug
this.showModal(this.props.match.params.slug);
}
What's happening is that showModal will change the state of a Modal component to true (thus reveal it) and also trigger an API call to get the details pertaining to the slug parameter that's passed (e.g. the slug sarah-smith is used for a GET request as a param to get Sarah Smith's details).
So all of these are fine so far. However, the issue is the re-rendering of the page when I either:
Open the modal
Close the modal
We have transitions for each element on the page so if you visit a route, the elements will have a subtle fade-in transition.
When a modal is clicked what I do is (member.name being the slug):
<Link
to={`/member/${member.name}`}
</Link>
Which causes a re-routing hence you can see those small transitions in the background while the modal is fading into view (as if I am opening /member/slug for the first time).
Same with the modal close button, if I click on it, what I do is invoke:
closeModal() {
this.props.history.push("/member");
}
Which re-renders the entire page /member page and not just close the modal. I can't just use this.setState({ showModal: false }); since that wouldn't change the route back to /member.
Is there a way to resolve this? Or another way of doing it so that the page doesn't re-render while changing the URL?
You can wrap your modal in route like this.
Modal
Then this.props.history.push("/path");
I think React-Router docs already have such examples. You can check following
https://reacttraining.com/react-router/web/example/modal-gallery.
There is npm module available as well https://github.com/davidmfoley/react-router-modal
I have an application that produces many pages of content. In order to expedite review of the process, I have a linkbutton that opens a child window starting at the page I'm on so reviewers can enter comments. I open the child window in javascript
objCmtWindow = window.open(MyURL, "comments", "width=800,height=600,menubar=no,toolbar=no,status=no,location=no,resizable=yes,scrollbars=yes,directories=no");
objCmtWindow.focus();
The child window has some javascript (an excerpt from a larger script) to listen to an API in the parent window so that it can know what to load
function updtSlide(){
if(window.opener.MyAPI)
{
window.opener.MyAPI.addEventListener("MyEnterBtn", function(e)
{
updtContent();
});
}
}
It works on load to grab the data with the function updtContent as part of the page load. When I click for the next page (MyEnterBtn) in the parent window, the child window is not updating for the next page data it needs to display. Is there a piece I'm missing to poll the parent window? Since the parent can operate independantly of the child, I really can't put the command in the next page button. I know the API is working correctly and emitting next page events.
Thank You
In the js function, the first 'opener' is spelled wrong.
TL;DR: With Angular UI Router, is it possible to autoscroll to a view of the same state that the template displaying it belongs to?
The Setup
I have an Angular 1.4.1 app, using UI Router 0.2.15 and Angular Material 0.10.0.
Example Plunk
There is a parent state called website and three child states: home, foo and bar.
The parent state has a wrapper view, which is displayed via the <ui-view> tag in index.html. It also has two other views, header#website and footer#website, which it displays via the wrapper view template.
Each of the three child states has a main view, which is displayed via the parent state's wrapper view template.
What I Expect
In the parent state's wrapper view template, when I set autoscroll="true" on the ui-view tag for the header#website view, I expect that the page will scroll to the top whenever the state changes. See the accepted answer of this SO question.
What Is Happening
When any of the ui-sref links in the footer is clicked, the page does not scroll to the top.
What I've Tried
If I put autoscroll="true" on the ui-view tag for the main view, it works as expected. This is not what I want, however, because the header is hidden from view even when you navigate to a state from the address bar.
What I Suspect
I think the problem has to do with the fact that header#website is a view that belongs to the website state, and that autoscroll only works for views that normally are displayed on the current template. In other words, normally the header view would be displayed via index.html, not wrapper.html.
The Question
Is my suspicion above correct? Do I need to refactor, or is there a way to make this work as is?
Thanks in advance!
Though I don't advocate DOM manipulation outside of a directive, a quick and dirty solution would be to add a scrollTo method in a .run block of your top level module definition, for ex:
.run(function ($window, $rootScope) {
$rootScope.$on('$viewContentLoaded', function () {
var interval = setInterval(function () {
if (document.readyState == "complete") {
window.scrollTo(0, 0);
clearInterval(interval);
}
}, 1);
});
})
http://plnkr.co/edit/qPqy69o7F9aTjNbUTMGY?p=preview
(borrowed from here https://stackoverflow.com/a/22166553/1516309)
I tried using the $anchorScroll() method of ui-router, but since your links are at the bottom of the page, thats where the page scrolls to, so you dont actually notice it doing anything.
Having trouble showing, hiding, and then re-showing Marionette Layouts. I believe this problem also applies to regular Backbone Views and Marionette ItemViews as well though.
In summary, I have a parent view. When it is initialized, it creates two child layouts that are meant to be used as tab content. The problem is that when tab content from one tab is shown, then content from another tab is shown instead, when the original tab content is shown again, the events do not work anymore.
The child layouts are created in the initialize function of the parent layout and re-used because their states need to be preserved when navigation moves back to them.
Here is a sample application that demonstrates what I am talking about:
Here is a video showing the broken events: Video Link
Thanks so much!
The problem is that you don't create a new istance of your sub-layouts, and just re-use the one you initiate in your main layout. So when you change the content of your region the events get unbinded as part of the close() function of Marionette's View.
You should change your initialize function like that:
initialize: function(){
_.bindAll(this);
// CREATE SUB LAYOUTS
this.tab1Layout = B.tab1Layout;
this.tab2Layout = B.tab2Layout;
},
And call the layouts in this way:
// EVENT HANDLERS
on_show_tab_1_click: function(event){
this.content.show(new this.tab1Layout());
},
on_show_tab_2_click: function(event){
this.content.show(new this.tab2Layout());
}
If you don't want to re-initialize the tab views on every tab change you could call view.delegateEvents() manually:
// views[] is array of initialized tab views
// Swap from displaying views[0] to views[1]
currentTabIndex = 1
this.myRegion.show(views[currentTabIndex])
views[currentTabIndex].delegateEvents()
For preserving state, another option is to render both tabs and simply hide the inactive tab region:
// Assume both regions have initialised views, tab2Region is hidden,
// tab1Region is shown.
// Swap between tabs:
this.tab1Region.$el.hide()
this.tab2Region.$el.show()
In my system I have a main page, that when clicking on any element on that page a new page is opened, in some cases (button click on child page) from the child page will be open a new page, and so on... when I decide to close any child I need to close all levels to the top and refresh the top level page because on the child level I'm doing Database manipulations and I need to see them when I am returning to the main page.
Every thing is working great, the only problem is with the refresh of the first page!
I've tried doing onunload... in the last child...and I've tried JQuery focus for the page itself....
Any one have any idea please?
Some code:
1) this is in the top parent page:
$(document).live(
'focus',
function()
{
window.location.reload(true);
}
);
2) this is in the last child (the one that the top parent page calls):
<body class="RTL" onunload = "opener.close()">
And I've tried many variations of that....
If any one have any idea it will help a lot, because as for now I need to do Setinterval() and refresh the main page all the time and this is really not nice....
Consider using iframes to show the child pages. That way, the child can use window.parent.reload() to reload the page.
If that is not possible: Did you check that opener actually contains the correct window? It might be the second-last child instead.