I'm making a very basic client (ie, essentially a website) using Router5. I have my code set so that when the user clicks a button, the elements that I don't want are hidden (the class hidden is added). The elements that I want to appear have the hidden class removed. Works great. However, when I am in the non-index state and I refresh, an element from the index state reappears.
I looked in the console for debugging purposes. When I refresh, the hidden class from the index view is indeed removed, and I'm not sure why.
I'm relatively new to coding, so any help that I could get on this would be greatly appreciated.
This is my transition code. As I've said, it works when I'm going from one view to the other, but when I refresh a view that isn't index, one of the index elements reappears:
const transition = function() {
return (toState, fromState, done) => {
if (fromState) {
$(`#${fromState.name}`).addClass('hidden');
}
$(`#${toState.name}`).removeClass('hidden');
done();
};
};
Other relevant code:
const routes = router.rootNode.children.map((route) => {
return route.name;
});
const registerPaths = () => {
routes.forEach((route) => {
$(`a[href="#${route}"]`).on('click', (event) => {
event.preventDefault();
router.navigate(route);
});
});
};
$(document).ready(function () {
require('./router/index').start();
require('./router/events').registerPaths();
});
I think that's everything. Please let me know if you need to see other code in order to help.
Thanks!
The issue was that even the DEFAULT view state needed to have .hidden class in the HTML. I'm still learning more about Router!
Related
I am trying to toggle a view between grid and list view mode on my frontend HTML page. I am able to do this fine with dom and HTML classes manipulation by toggling "display: none" between two containers. However, when I go to the next product page(through pagination) or when I reload the page, the default view is the one that appears and not the one that was last toggled. Is there a way to persist the view in case a page reload or product pagination changes? thank you.
here is the dom code that achieves this :
viewList.addEventListener('click', function() {
this.classList.add('view__active');
viewGrid.classList.remove('view__active');
gridItem.classList.add('hidden');
listItem.classList.remove('hidden');
});
viewGrid.addEventListener('click', function() {
this.classList.add('view__active');
viewList.classList.remove('view__active');
gridItem.classList.remove('hidden');
listItem.classList.add('hidden');
});
So far I found that I have to use localStorage to achieve this. but is there a better way to do this?
Essentially what is happening is when you request something from the server, the server responds with an HTML document, and whichever scripts associated with that document is run, So whatever JS executed in the first request is not in context when the second request(paginate or reload) is made.
So you need a way to persist information across these page loads, For that, you have 3 options.
Use sessionStorage.
Use localStorage
Use Cookies.
Of the 3 above the easiest would be to use either option 1 or 2.
Replying to your comment,
Also, If I am using localStorage, What am I using to store the view state?
I'm not quite clear as to what you mean by "What you are using to store the state" If your question is about where your data is stored, you need not worry about it as this is handled by the browser. If your question is about "How" to store it you can go through the MDN docs attached in option 1 or 2. This is simply storing a key-value pair as shown in the docs
localStorage.setItem('preferedView', 'grid'); You can add this to your on click handlers as follows,
viewList.addEventListener('click', function() {
this.classList.add('view__active');
viewGrid.classList.remove('view__active');
gridItem.classList.add('hidden');
listItem.classList.remove('hidden');
localStorage.setItem('preferedView', 'grid');
});
viewGrid.addEventListener('click', function() {
this.classList.add('view__active');
viewList.classList.remove('view__active');
gridItem.classList.remove('hidden');
listItem.classList.add('hidden');
localStorage.setItem('preferedView', 'list');
});
Then when loading a new page at the top of your script you can get the users preferedView(if existing) via const preferedView = localStorage.getItem('preferedView');
Here is a complete example from MDN
In order for anyone to find an answer for a similar task, thanks to #Umendra insight, I was able to solve this by using this :
function viewToggeler(viewBtn1, viewBtn2, view1, view2, viewStord) {
viewBtn2.classList.add('view__active');
viewBtn1.classList.remove('view__active');
view1.classList.add('hidden');
view2.classList.remove('hidden');
sessionStorage.setItem('preferedView', viewStord);
}
viewList.addEventListener('click', () => {
viewToggeler(viewGrid, viewList, gridItem, listItem, 'list');
});
viewGrid.addEventListener('click', () => {
viewToggeler(viewList, viewGrid, listItem, gridItem, 'grid');
});
if (sessionStorage.getItem('preferedView') === 'grid') {
viewToggeler(viewList, viewGrid, listItem, gridItem, 'grid');
} else if (sessionStorage.getItem('preferedView') === 'list') {
viewToggeler(viewGrid, viewList, gridItem, listItem, 'list');
}
I ended up using sessionStorage over localStorage because it empties itself on window/tab closing which might be the most desirable result. localStorage persists even after exiting the browser and opening it back.
Also, at any point someone wants to empty the sessionStorage on exit, I used :
window.addEventListener('onbeforeunload', () => {
sessionStorage.removeItem('preferedView');
});
I have a chat application in react and have used npm package react-scroll-to-bottom until now. But it's not maintained and throwing warnings so i thought it should not be too hard to write this myself.
I'm following various answers on stackoverflow but cant seem to get it right.
It should just scroll to the bottom of the container as the page loads.
Any ideas what i'm doing wrong? Thanks in advance!
EDIT: now i have a div at the end of the tree which has the ref because i thought it would be easier to scroll to that instead of trying to tell a div to scroll inside it.
const chatHistoryRef = React.useRef();
React.useEffect(() => {
window.scrollTo(0, chatHistoryRef.current.offsetTop);
}, [chatHistoryRef]);
Your useEffect does not have any dependencies, so if the ref changes (as in, the div gets rendered) the effect will not be run. Dependencies are set in a second parameter as an array, like so:
useEffect(() => {
// do something with chatHistoryRef.current
}, [chatHistoryRef.current])
Found the solution. As i'm working with polling in my app right now i only get chat messages every two seconds. So my function was in fact scrolling, but there just wasnt anything to scroll away from. :)
This is my solution. Thanks everyone for the help. By adding length to message dependency this is only fired if there are new chat messages
const chatHistoryRef = React.useRef();
React.useEffect(() => {
chatHistoryRef.current.scrollIntoView({
behavior: 'smooth',
block: 'end'
});
}, [chatHistoryRef, messages.length]);
When a user changes the theme, I use mainWindow.webContents.send to change a class in the DOM. I also save it in the store, under the key theme.
mainWindow.webContents.send('theme:change', theme);
store.set('theme', theme);
Then in renderer.js:
ipcRenderer.on('theme:change', (event, theme) => {
document.querySelector('body').className = `${theme}`;
});
This successfully changes the theme and saves it in the store. However, now I want that theme to load up when starting the application rather than going back to the default. To do this, in app.on('ready') I am doing this:
mainWindow.webContents.send('theme:change', store.get('theme'));
However, nothing is happening. It's like it isn't being sent. Where am I going wrong? Essentially what needs to be done is for the class in body to be changed when the application loads to the one in the store.
Figured it out. I had to put:
mainWindow.webContents.once('dom-ready', () => {
mainWindow.webContents.send('theme:change', store.get('theme'));
})
I was trying mainWindow.on('dom-ready') which is why it wasn't working.
I'm not sure why I can't get the button element using my UI hash. This is what my Layout looks like:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
ui: {
btnSave: "#btnSave"
},
events: {
"click #ui.btnSave": "onSave"
},
onInitialize: function () {
this.listenTo(App.vent, "DisableSaveButton", function(val) {
this.disableSaveButton(val);
},this);
},
disableSaveButton: function () {
this.ui.btnSave.prop("disabled",val).toggleClass("ui-state-disabled",val);
},
onSave: function () {
alert("saved!");
}
})
In VS2013, when my breakpoint hits the line inside disableSaveButton method, I entered $("#btnSave") into the Watch window and I was able to get the element back. I could tell because it had a length of 1. From this, I know the button is rendered. However, if I enter this.ui.btnSave into the Watch window, I would get an element with length of 0.
My BaseLayout object is basically a custom object extended from Marionette.Layout
Marionette version: 1.8.8
Any ideas why I can't find the button element using this.ui.btnSave?
Thanks in advance!
Got some help from a coworker and the issue might be because the element is out of scope. Basically, inside the Layout object, 'this' does not contain the element. We were able replace 'this.ui.btnSave' with '$("#btnSave",this.buttonset.el)' and that works fine. buttonset is the region that actually contains the html element.
This seems like an inconsistency because even though the ui hash didn't work, the click event utilizing the ui hash did work.
UPDATE 6/3/2015:
Another coworker of mine provided a better solution. Basically, in my Layout I use a display function to display my view. It looks something like this:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
display: function() {
$(this.buttonset.el).html(_.template($("#buttonset-view").html(), {"viewType": viewType}));
}
})
Basically, I'm saying to set the html of my region, which is this.buttonset.el, to my template's html. As of now, my layout doesn't know any of the elements inside the region. It just contains a region which displays the elements. So there is some sort of disconnect between my layout and the elements in my region.
The correct solution, as opposed to my earlier workaround, is to simply add the following line of code at the end:
this.bindUIElements();
From Marionette Annotated Source:
This method binds the elements specified in the “ui” hash inside the
view’s code with the associated jQuery selectors.
So this final code looks like this:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
display: function() {
$(this.buttonset.el).html(_.template($("#buttonset-view").html(), {"viewType": viewType}));
this.bindUIElements();
}
})
With this, I was able to finally able to retrieve my element using this.ui.btnSave.
I've been following a tutorial about using backbone and jqm altogether which consists of disabling the jqm router and using backbone's one instead, but i frankly dislike it's routing approach :
var AppRouter = Backbone.Router.extend({
routes:{
"":"home",
"page1":"page1"
},
initialize:function () {
// Handle back button throughout the application
$("body").on('click', '.back', function(event) {
window.history.back();
return false;
});
},
home:function () {
console.log('#home');
this.changePage(new HomeView());
},
page1:function () {
console.log('#page1');
this.changePage(new Page1View());
}
changePage:function (page) {
$(page.el).attr('data-role', 'page');
page.render();
$('body').append($(page.el));
$.mobile.changePage($(page.el), {changeHash:false, transition: $.mobile.defaultPageTransition, allowSamePageTransition:true});allowSamePageTransition:true});
What it does is that upon each hash change, it calls changePage which does a view creation, a template rendering, a div creation by appending it to the body, and a changePage to this new element.
Now, this means creating a div on every page change
. Also, views aren't changing everytime you change page, but only when models change, and the "render" should be fired upon model change, and the rendered view will be stored in view.el.
What is done here is calling the render at every page show, even if the page is still the same.
How can I tell jqm to show the page.el with a transition, without having to do a or appending a new div to the body everytime ?
I thought of storing my views in an array or a collection and telling the router to pull them from there, and then show it by appending a "temp div" or something, but there must be a better way.