SvelteKit loading indicator when a page load time threshold is exceeded - sveltekit

I am using SvelteKit for a website that is mainly server-side rendered pages. The client-side navigation on SvelteKit works beautifully and is super snappy. However, sometimes, when the backend server is called in the route load() function, the response is not instant and maybe several seconds because of the database long-tail latency.
What would be the best way to create a loading indicator that is only displayed if the loading time is not instant (sub 200ms)? I am aware of the generic navigating pattern, but I specifically hope to create a loading indicator that is not triggered unless the page load is not instant.

Sveltekit has a store variable called "navigating" which literally indicates if the client is in-between loading pages. From the Sveltekit docs:
navigating is a readable store. When navigating starts, its value is { from, to }, where from and to both mirror the page store value. When
navigating finishes, its value reverts to null.
Note, that if your page is pre-rendered, it should be "instant" and thus not show a loading indicator. If not, this "navigating" variable will be not null for as long as it takes for your content to be fetched and/or rendered. If you wanted to, you could make the "Spinner" component show nothing...until after 200ms (using a setTimout timer)...which it sounds like you do want.
Here's a sample implementation from a wrapper component I have used in the past (pardon the tailwindcss syntax...I copied and pasted it). You can customize this idea in __layout.svelte or wrap components or entire "pages" with this {#if $navigating} logic:
<script>
// USE THIS:
import { navigating } from '$app/stores'
// Example spinner/loading component is visible (when $navigating != null):
import Spinner from '$lib/design/Spinner.svelte'
...
</script>
<main class="py-4">
<div class="pagePaddingMain flex-col">
{#if title}
<h1 class="text-4xl text-center text-cText pb-4 sm:text-5xl">{title}</h1>
{/if}
{#if $navigating} <!-- LOOK HERE -->
<div class="m-8">
<Spinner />
<h1 class="ext-3xl text-center text-cText">Fetching {title}...</h1>
</div>
{:else}
<slot />
{/if}
</div>
</main>
That's it. Good luck!

Related

Next js 13 Script tag won't render in the right position

I am using the Next Js Script tag to embed a form from Convertkit into my project. The embed gets pulled in, and it works, I can subscribe and my email appears in the Convertkit Dashboard, everything works as expected.
However, the form does not render where I am trying to place it, which is in my about page, but rather, gets appended to the bottom of each screen, as it if was outside of the html document (outside of the parent div in my return statement, no background styling or anything takes effect). Sometimes its rendered at the top of the screen, over every other content.
You can check out the test project here: https://testfetchdata-uier.vercel.app/about
This is what my embed looks like:
<div className="flex flex-row space-x-4">
</div>
<>
<Script
async
data-uid="PROJECT_ID"
src="https://MYPROJECT.ck.page/PROJECT_ID/index.js"
></Script>
</>
</div>
</div>
Any ideas as to what might be happening?
Thanks a lot.

SvelteKit routing for components

I have a left section containing a mini navbar (buttons: users, create, coins) and a right section containing the content I want to display (display coins section when coins button is clicked, ...)
However currently, the routing I want to achieve is not what I want at the moment.
When for example, the users button is clicked, I want the routing/url to be shown as settings/users, rather than just settings, settings/create for the create button and settings/coins for the coins button.
The following is the code:
settings/+page.svelte
<script>
import { page } from '$app/stores'
import Create from './create/+page.svelte'
import Users from './users/+page.svelte'
import Coins from "./coins/+page.svelte"
export let data
let loading = false
let currentSetting = 'create'
$: currentSetting = $page.url.hash?.substring(1) || 'create';
</script>
<div class="flex mt-5 h-full">
<!-- Left section -->
<div class="flex flex-col items-center gap-4 w-44">
Create
Users
Coins
</div>
<div class="divider divider-horizontal"></div>
<!-- Right section -->
<div class="flex-1">
{#if currentSetting==='create'}
<Create categories={data?.categories}/>
{:else if currentSetting==='users'}
<Users bind:loading={loading} users={data.allUsers}/>
{:else if currentSetting==='coins'}
<Coins bind:loading={loading} users={data.allUsers}/>
{/if}
</div>
</div>
where coins, create and users are all components in settings/coins/+page.svelte, settings/create/+page.svelte and settings/users/+page.svelte respectively.
When a button is clicked, currentSetting changes such that the display changes to the correct display (users button clicked, displays users settings.
I have tried using a pseudo route for each component, but I do not want to use it as it cannot have its own +page.server.js.
Thus, changing the link of a anchor tag is needed.
I was thinking of using slug, but I'm unsure of how to actually use it.
I believe you are confusing pages and components.
In SvelteKit, pages are placed under /src/routes/ in a hierarchy that mirrors your desired URL paths to access each page. SvelteKit will then infer the routing from that /src/routes/ directory structure. This is where you will define +page.svelte, +page.js, etc. files for page declarations, +layout.svelte etc. files for layouts, +server.js files for endpoints, etc.
You do not need to import pages in order to link to them, you simply use the standard HTML ... tag with the correct URL to reach the page you want linked to. For instance, a page declared as /src/routes/settings/users/+page.svelte will be, by convention, accessible on your site at yoursite.com/settings/users and can be linked to with Users.
You can read about SvelteKit routing in detail here.
In addition to pages, you can also create components, which are like reusable building blocks for your pages. These are typically placed under /src/lib/ and have names like MyForm.svelte. These components will have to be imported in your page files before you can use them there.
You can find a typical SvelteKit project structure here.
Complementary information: using layouts
Layouts are part of the /src/routes tree and help define common structural elements shared by several pages.
Layouts usually apply to all the pages that are located below them in the /src/routes/ directory hierarchy, although there are special mechanisms available to escape individual page files from using layout(s) that would normally apply to them.
Layouts can be nested. In your particular use case, for instance, you would want to locate your side navigation in /src/routes/settings/+layout.svelte. This is what it that file would look like:
// /src/routes/settings/+layout.svelte
<script>
export let data; // from the layout's load() function, see further below
$: current = data.route.id
</script>
<div class="flex mt-5 h-full">
<!-- Left section -->
<div class="flex flex-col items-center gap-4 w-44">
Create
Users
Coins
</div>
<div class="divider divider-horizontal"></div>
<!-- Right section goes here -->
<div class="flex-1">
<slot></slot>
</div>
</div>
And the accompanying /src/routes/settings/+layout.js:
// /src/routes/settings/+layout.js
export function load({ route }) {
return { route };
}
The above load function will simply get the name of the route from the incoming request and pass it on to the layout (and its child pages). You need this in order to properly set the btn-active class in your side navigation for the currently active page.
With the layout defined above, any pages declared under /src/routes/settings/ will apply this layout and be loaded into the <slot></slot> declared in the layout.
You can read more about SvelteKit layouts here.
Note that all of this is really the A-B-C of SvelteKit. I urge you to go back to the docs to get a solid grasp of a SvelteKit project structure and a thorough understanding of its routing/page structure conventions.

Optimize page load of multiple pages

I've a multiple page site. I created a single component enclosing all the texts/para/images. I've refered this component to all the multiple pages. I used a class name to hide/show the content to the respective pages via css. Problem is that the page load is slow. Since all contents are hidden using css they are still loaded making my site slow. Any help on this is much appreciated. Thanks
There are multiple things you can check.
Check the internet connectivity, I hope you have good internet connectivity to load the
page.
As you say there are multiple page with single component, check if you can move those into sub-component.
I am not sure how you're hiding those components use css property display:none if those are light weight.
you can use something like this:
<style type="text/css">
.hidden { display:none; }
</style>
render: function() {
return (
<div className={this.props.shouldHide ? 'hidden' : ''}>
This will be hidden if you set <tt>props.shouldHide</tt>
to something truthy.
</div>
);
}
// or in more modern JS and stateless react
const Example = props => <div className={props.shouldHide}/>Hello</div>
source

React : best way to inject Component in dynamically loaded HTML?

I'm new on React (I more at ease w/ jQuery or AngularJS). I have a special case and I don't find a good way to resolve it...
My app contains an area which is like a "document viewer". It loads an HTML content from the backend (via API, using Fetch) and inject it in the "viewer" component. The HTML content loaded looks like an "university report" (it's just a formatted text, only <span> and <p> with class="..." attributes, nothing more).
Ex : <p>Lorem ispum <span>some text</span> loreb bis <span>ipsum</span></p> ...
I load the content, and inject it this way in the render() of my component <Viewer> :
<div dangerouslySetInnerHTML={ getFreshlyLoadedHTML() } />
Easy, it works just fine !
But... Now, I want to inject some "interactive" components in the loaded HTML. For example, some button to give a feedback etc. The API must decide where to place the component between the words/nodes of the formatted text (HTML).
Ex :
<p> Lorem ispum <span>some text</span>
loreb bis <span>ipsum</span>
<MyFeedbackButton paragraph="1.3"/>
</p><p>Other Lorem Ipsum<p><span>...</span>
There, I'm stucked because I cannot use dangerouslySetInnerHTML if there are components inside the loaded HTML...
First attempt : I've tried modifying the API, and instead of sending the HTML in a string to the app, I send a custom JSON structure that represents almost the final JSX structure that I want. Then, in my react page, the render function only have to parse the JSON and build the JSX (here, a JsFiddle example if it's not clear : https://jsfiddle.net/damienfa/69z2wepo/34536/ )
It works, but I can't believe it's the good way...
I see a major problem : all the HTML node (span, p...) that I build from the render function are referenced by reactJs, is it really necessary ? Mostly, there are "dead" nodes (I mean, dom node that won't never changed, this is static formatted text).
Just take a look a all those "data-reactid" on nodes that never will be interactive...
What would be your advice on that case ?
What about my attempt with a JSON-structure sent by the API ?
Is there a way to say to react "do not reference that element" ?
Do you clearly see a better solution to my problem ?
Your current workflow is not very secure and subject to many potential errors and open doors, especially concerning code injection ...
The overload due to react tracking the nodes is not an issue, React could track 10 000 nodes and not have a problem (well actually on many of my apps React has more than 100 000 nodes to care about and it still rurns perfectly).
I see different solutions here:
If there are only 3 or 4 possibilities of dynamic components and order, you might have components like "templates" to which you would simple send text arguments. This is the safest and easiest option.
If it doesn't suit your use-case but the JSON file can contain only a limited set of components, the components should be located in your main app, and then rendered with custom props from the JSON. Actually given the structure of data you could consider using xml instead of json and build a xml tree that you would parse and render. Only components from your white list would be rendered and it would limit drastically the potentials security issues. If needs quite some work on the XML parser though.
If the JSON file can contain many many different and unpredictable components or if the behaviour of those components is largely dynamic and independant of your app, you might as well consider using an iframe, with its own JS and HTML, so that this part of the code is isolated from the rest.
Try using an inline anonymous function within the inner content from within React using JSX. It works! Just be careful about how you wire up the data so there isn't a route where a user can inject HTML from an input or text field.
<div className="html-navigation-button">{(() =>
{
const CreateMarkup = ( sNavItemName :string ) => {
return {__html: sNavItemName };
}
var sTextToAddHtmlTo = props.nextNavItem.name.toString();
sTextToAddHtmlTo = sTextToAddHtmlTo.replace( "/", "/<wbr>" );
return (
<div dangerouslySetInnerHTML={CreateMarkup( sTextToAddHtmlTo )} >
</div>
);
})()}
</div>
I didn't override the React internals of 'render()', but only used a React Component with props wiring to pass down data to it for rendering.
I added the hook for 'dangerouslySetInnerHTML' deep within the return content of the React Component so there would be no easy way to intercept and manipulate it.
As such, there is no 100% guarantee on safety, but that's where adding good security to web services, databases, and use of CORS and CORB would be helpful to lock down security risks.

Detecting page scroll/ current section being viewed in AngularJs

My page is divided into sections : #page-1 and #page-2
See Plnkr: http://plnkr.co/edit/RZJLmsWDfs63dC0QuDJi
<body>
<section id="page-1">
This is page 1. It takes the whole height of the browser. User has to scroll down to see page-2.
</section>
<section id="page-2">
<span class="animated bounce">This is page 2 </span>
</section>
</body>
Animation classes are being applied to different elements in #page-2.
However by the time the user scrolls down to these elements, the animation has already finished. Hence they just look like static objects.
Is there anyway I can detect when #page-2 is currently being viewed and then call a function to addClass('animated bounce') to certain elements ?
I would like to achieve this in angularjs if possible
I have found a angularjs directive that is probably helpfull for you in this case. Inview tries to solve this exact problem by reporting to you if a dom element is visible on the screen. Unfortunately I have been unable to test my solution because I couldn't find a minified js file of Inview but I assembled some code that should work:
<section id="page-2" in-view="{$inview ? isFocused=true;}">
<div ng-class="{'animated bounce': isFocused}">This is page 2 </div>
</section>
The $inview is supposed to be true whenever the element is in visible in the browser. This leads to the scope variable isFocused being set to true and therefor the animation class is added to your div.
This should work as you have intended in your question, if it does not work for some reason please let me know so I can improve my answer.

Resources