In Vue JS, how do I store the "data" (true/false) of a component on a page refresh? - laravel

Using Vue Js with Laravel I would like to keep open an expanded nav bar when the user refreshes the page (if they have chosen to open it initially).
The nav bar open/close status is stored in the component's data with a true/false boolean.
I'm a bit confused of which approach to take as have researched various options, so seek the best advice as a noob. Ideally there is a simple way to have the 'data' persist in my component rather than it getting re-rendered to the default of false! Therefore I guess it needs storing in the user's "session" state locally right?
But what do I use and how?
sessionStorage
localStorage
Vuex - https://v2.vuejs.org/v2/guide/state-management.html
a plugin - https://github.com/vuejs/vuex
Laravel session - https://laravel.com/docs/5.5/session
Thanks.

Vuex with a plugin such as this one if your application is large/is growing in complexity. Its a bit of a learning curve though so if you only have a few things to keep track of their states, then localstorage would be a good solution.
Localstorage has the advantage of being easy to use, widely adopted and if you need to in the future, it integrates nicely with state management systems like vuex. The api is quite simple, really just getItem and setItem for common simple use cases. It persists across browser sessions as well.
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
Sessionstorage is roughly equivalent to localstorage with the key difference being once you close the browser, it gets wiped.

Related

What's the best way to propagate data through your Vue3 App?

Is there a recommended way of how to propagate data through your vue components? What I'm trying to do is get the data from backend once and propagate it everywhere in my project but I can't find the right strategy.
sessionStorage: Works great and resets on refresh/close window but as soon as you need to create target="_blank" anchor tags, it will not propagate your data to new tabs.
localStorage: Requires in my opinion more work than sessionStorage because you need to delete data manually to keep things tidy. One big problem for me is that it looks like you can't pass markdown and arrays properly, at least without stringify. I've built a project with localStorage and ended up sending ajax requests from most of my components because I couldn't propagate the data through my app how I wanted. At this point my frontend is backend.
My personal problems with localStorage: I am using the marked package to display Markdown but it throws errors if passed undefined. This gets problematic, when I want to use it in a reactive state because instead of resulting in undefined, it throws an error and crashes the whole app. The point I am trying to make is that when you pass an undefined localStorage value to marked in an either or field like so:
const state = reactive({
value: marked(localStorage.value) || ""
})
it crashes your app, if localStorage.value is empty.
Another problem is that I fetch text content depending on a locale and store it in localStorage. This is great until the user changes locale and all content strings have to be replaced by the translated strings. It gets really tricky, if I want to use one component as template to load in different locales.
vuex: I've tried vuex shortly and found it useful but didn't see the benefit over just using localStorage for my purposes. Prolly I'll give it another go.
How do you propagate data through your app?
There are a few good arguments why Vuex is better than Local Storage:
https://www.quora.com/What-is-the-benefit-of-using-Vuex-over-LocalStorage-to-store-the-state-of-an-application-in-Vue-js
You can also try composables. They are reusable functions (similar to mixins) in composition-api (you need composition-api plugin in vue2, in vue3 it is built-in). It can be also the place you store your data. It can be easier and more intuitive than Vuex.
First, create directory /composables and add javascript file (it's
good practice to create file beginning with use word) useState.js:
import { reactive, toRefs } from "vue";
const state = reactive({
isMenuOpened: false
});
const toggleMenuState = () => {
state.isMenuOpened = !state.isMenuOpened;
};
export default {
...toRefs(state),
toggleMenuState
};
toRefs converts all of the properties, to a plain object with
properties that are refs
Now you can use composable in vue components:
<script>
import useState from "./composables/useState";
export default {
setup() {
const { isMenuOpened, toggleMenuState } = useState;
return {
isMenuOpened,
toggleMenuState,
};
},
};
</script>
Demo:
https://codesandbox.io/s/happy-chandrasekhar-o05uv?file=/src/App.vue
About composition api and composables:
https://v3.vuejs.org/guide/composition-api-introduction.html
Since you have mentioned local storage and session storage, I believe you must be trying to share your state across tabs/windows rather than just different components on a single page. At this scale I don't think this is necessarily a VueJS specific issue/pattern. Generally speaking, you want data to be shared pretty much across process boundaries.
Session Storage used to be one of the most sensible ways because it is shared between one window and all the child windows it has created, until all of them are closed at which point the storage will be discarded as well. However, depending on your use cases, Chrome (within the past year) made a change to NOT inherit the session storage from the original window if the popup windows is opened as noopener, hence if you are relying on the noopener (and its performance implications), session storage is no longer usable for this purpose.
Vuex does not solve this issue neither. In fact, Vuex is pretty much irrelevant here. Given the application architecture implied, the state management capability Vuex brings to your app will be likely redundant because any state mutation will probably be submitted to your backend anyway. In some sense the Vuex store is on your backend rather than your frontend.
So we typically do one of the three approaches:
directly broadcast from backend to all frontend tabs. E.g. there is no state sync-ing directly between frontend tabs. Every single tab (child window) communicates directly with the server: it mutates the state by submitting actions to the server, and only the server can change the state and broadcast the changes back to all the tabs in real time (again, conceptually it feels like the Vuex store is on your backend)
use SharedWorker. SharedWorker is shared by all the browsing context with the same origin. It is activated the moment the first browsing context (of a certain origin) is created, and is kept alive until the last browsing context is destroyed. In some sense its sharing semantic is similar to that of the old session storage. You can use SharedWorker as the single entity to communicate to your backend. States can be maintained by the SharedWorker and accessed from the tabs in a RPC fashion. Or states can be maintained separately in each tab and SharedWorker just broadcast the changes to the tabs.
if you actually do not have a backend, but you just want to build multi-window single page application, you can make one of your tabs special and act as the owner of the state store. For all the child windows created from this "master" window, their local store will be a proxy - the actions they perform against the local store will be proxied over to the master window; and the master window performs the action in its store, and broadcast the changes to all the child windows.
By the way, I have used the word "store" many times, but I do not necessary mean the Vuex store. The store is just a shared place where you keep your state.

What is the best practice to get the required data into VueJS component?

I'm building my first VueJS application which is intended to be used by hundreds of people in the future. I tried to make the individual components reusable and indpendent as possible. To achieve this i decided to let every component fetch its required data themselves. This works fine but i'm not sure if its best practice. I could also pass the data between the components or even using the 2-way data binding functionality.
The sketch bellow describes one of the situations i have. Note that 1 account has 1..* users. As you can see i need to fetch the accounts to display them in the accountOverviewComponent.
Currently i only fetch the accounts in the accountOverviewComponent and fetch the users when the account edit button by the passed accountId in the accountOverviewComponent is clicked. This way i don't fetch data i don't need at the time.
I can also include the users (god knows which data/relations will be added in future) to the fetch account response as wel so i can pass all required data to the accountShowComponent when a account edit button is clicked. This way i can save requests to the server with the side note that i fetch users of accounts i dont need. A possible disadvantage is that the account is updated in the accountShowComponent and not in the accountOverviewComponent (for example when the accountShowComponent is a modal on top of the accountOverviewComponent. So i need to pass the updatet account back or re-fetch the accounts after a save or something.
As third option I can do the same in option 2 but than with the 2-way data binding which handles the data synchronization between the components. This will probably restrict the usage of the accountShowComponent to cases where the accountShowComponent is used "on top" of a parent which contains the data.
I can also store the data in a Vuex store and update the stores all the time. I read that this is bad practive as it should be only used for data which is required accros the SPA. I think Vuex is overkill in "simple" situations like this?
What is the best practice of the described situation? I have a bunch of comparable situations/scenarios in my application. Performance (also for mobile devices), scalability and being "future proof" (extendability/modularity) are important for me. Can someone help me out because i'm a bit lost in the options i have?
UPDATE
The reason i think Vue is overkill is comming from this article which makes totally sense from a software engineer perspective to me (i may be wrong). As my components have a kind of "parent - child" relation so i can solve my "issue" easily with passing data (or use 2-way data binding) and callback-events.
The number one use case for storing data in a centralized store like Vuex, is, because the data must be accessible in multiple places of your application, by components which oftentimes are not related in any way (they neither are parents or children of each other). An example of this would be certain user settings to configure how your application looks or what date format should be used, to name a concrete example.

Render a long list on page load with VueJS

I have a long list that I want to let the user manipulate in a browser-based web application. Using jQuery, the most straightforward way would be to render it on the server as part of the initial page load, and include a small script that registers event handlers for AJAX requests and DOM manipulation.
In the case of VueJs however, it seems to me that the most straightforward way is for the initial request to load the page layout only, then call an API to get the data for the long list. In other words, VueJs renders the initial list, not the server.
While this is workable, I am hesitant to introduce this second request unless I really have to. Is there a more straightforward way to go about this? Am I missing something about how VueJS works? I would really like to render the initial list on the server side if possible. For example, would it be workable to somehow include the initial list as 'transcluded' content?
I don't want to have to get in to VueJS' complete server side rendering, since it looks like an advanced topic (and this is a simple task). I have experimented with passing the initial list data as JSON in the <head> of the page (inside tags that register it as a javascript variable), but that seems like a hack/workaround.
In the case of VueJs however, it seems to me that the most straightforward way is for the initial request to load the page layout only, then call an API to get the data for the long list. In other words, VueJs renders the initial list, not the server.
Yes, it is most straightforward way, and considered as anti-pattern also. Just for the reason in your next sentence: "While this is workable, I am hesitant to introduce this second request"...
I think you should read following post on medium.com first. It is about Vue and Laravel framework, but the principles herein can be considered universal:
https://medium.com/js-dojo/avoid-this-common-anti-pattern-in-full-stack-vue-laravel-apps-bd9f584a724f

Speeding Up A Vue.js Component Under Laravel

I have a Vue.js component which gets a big list of releases from the database, at which point we can use all the lovely reactive list-filtering functionality to drill down to what we're looking for. The problem is that there's a very noticeable lag after page load before the list appears on the page.
Obviously loading the data in via ajax may not be instantaneous, but I thought I might be able to get better results by e.g. getting the data on the server-side with Laravel, and then passing it to the component from its containing Blade template as a prop. Not much luck so far though, again, a noticeable wait for the component to receive and display the data.
Are there any simple approaches for having a Vue component ready to go as quickly as possible? I looked at the prerender-spa-plugin for webpack but I don't know if that would interact properly with the Laravel routing. Likewise server-side rerendering with node seems like it could be more trouble than it's worth.
Has anyone experienced similar issues and found anything like a great solution?
you have too spot where you could do somethings. 1 on the server. by caching queries or somethings. And the second is on client side. when you handle the received / fetched collection. If you have, lets say 10000 entry, this would take sometime to a: parse the json object, b: create necessary vue components (if you're using vue component for row), and generating corresponding dom. So if you split and handle the recieved data in sized chunk. Vue would update the dom accordingly. and Fill the dom more like a stream. Not always the best approch but working in many case.

Handling forms with many fields

I have a very large webform that is the center of my Yii web application. The form actually consists of multiple html form elements, many of which are loaded via AJAX as needed.
Because of the form's size and complexity, having multiple save or submit buttons isn't really feasible. I would rather update each field in the database as it is edited by asynchrously AJAXing the new value to the server using jeditable or jeditable-like functionality.
Has anyone done anything like this? In theory I'm thinking I could set up an AJAX endpoint and have each control pass in its name, its new value, and the CRUD operation you want to perform. Then the endpoint can route the request appropriately based on some kind of map and return the product. It just seems like someone has to have solved this problem before and I don't want to waste hours reinventing the wheel.
Your thoughts on architecture/implementation are appreciated, thanks for your time.
In similar situation I decided to use CActiveForm only for easy validation by Yii standarts (it can use Ajax validation), avoiding "required" attribute. And of course to keep logical structure of the form in a good view.
In common you're right. I manually used jQuery to generate AJAX-request (and any other actions) to the controller and process them there as you want.
So you may use CRUD in controller (analyzing parameters in requests) and in your custom jQuery (using group selectors), but you can hardly do it in CActiveForm directly (and it's good: compacting mustn't always beat the logic and structure of models).
Any composite solution with javascript in PHP will affect on flexibility of your non-trivial application.
After sleeping on it last night, I found this post:
jQuery focus/blur on form, not individual inputs
I'm using a modified version of this at the client to update each form via AJAX, instead of updating each field. Each form automatically submits its data after a two seconds of inactivity. The downside is the client might lose some data if their browser crashes, but the benefit is I can mostly use Yii's built-in controller actions and I don't have to write a lot of custom PHP. Since my forms are small, but there are many of them, it seems to be working well so far.
Thanks Alexander for your excellent input and thanks Afnan for your help :)

Resources