Backbone.Marionette Layouts and Navigation - backbone.js

I run on some interesting issue during working with nested Layouts.
Lets go right to example. A index.html have just one region:
...
<div id="screen" />
...
Now I have a login view which render on this region. When I succeed login I present home page(within ApplicaitonLayout) which contains so statistics. ApplicationLayout looks like this
<div id="screen">
<header />
<nav id="menu" />
<section id="content"/>
<footer />
</div>
what I do here is render ApplicationLayout in screen region. Important thing here is that I want home navigation to be routable. I omit a lot of JS/Marionette code but basically I have something like that(base on Brain Mann style)
var applicationLayout = ApplicationLayout();
var navigation = {
goHome: {
new MenuController({region: applicationRegion.menu});
new HomeController({region: applicationRegion.content});
}
}
app.screen.show(applicaitonLayout);
new HomeRoute(controller: navigation);
So what I do here is I ALWAYS need to render menu even if I don't need that for example someone click different menu option and only applicationRegion.content should be rerendered.
To be more general how to handle region which are partially static (After login menu always will be displayed) when navigation change?
Edit:
So the question is: Should I need always render menu on every change in menu navigation, even this menu is already there?
To be more specific we have 2 ways to reach home page:
- 1st route from browser, in which we would like to render whole home page(include menu)
- 2nd Navigate from menu item, in this case we should NOT need to render menu, because it should be there already.

For that purpose, use an application region: https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.region.md
Your menu would be displayed in a region, and the main content (which can contain layouts) would be in another region.
You can see an example (from my book on marionette) here: https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/app.js#L3
And a live example here: http://davidsulc.github.io/marionette-gentle-introduction/#contacts
Edit based on comments:
If your layout has been rendered (and therefore the menu is visible), you only need to change the layout's subview when a menu item is clicked: myLayout.show(newView).
The communication can be handled through reqres, see for example https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/header/list/list_controller.js#L11 (event trigger) and https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/contacts/contacts_app.js#L27 (event handler)

Related

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.

SvelteKit loading indicator when a page load time threshold is exceeded

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!

AngularJS display login page on full screen

I have my angularjs app that has a topbar navigation, a left sidebar navigation and the rest is for the content (using data-ng-view I get different views).
My question is, if I have a view called login and a route that redirects me to the login page, how can I show that view actually in the full screen of the page?
Now if I navigate to http://applicationurl.com/#/login the view is like this:
I want it to look like this:
I cannot figure it out how can I achieve this in AngularJS?
The html is something like this:
<html>
<head>
</head>
<body>
<header>...</header> // top navigation bar
<div id="container">
<nav>...</nav> //left bar navigation
<div id="content">
<div id="wrap" data-ng-view="">
// here are loaded all the views ...
</div>
</div>
</div>
</body>
</html>
The correct solution would be to move your header/footer/sidebar into Angular Views and then use the Angular UI Router to build the pages with multiple named views where needed.
If you do not want to modify your code, it is not possible to achieve what you want to accomplish. By virtue of your login page being rendered by ng-view, it will be inserted within that tag on the page. You could hide the header and navbar with ng-hide set on some scope variable that would be set when the login page was rendered, and then the variable could be reset upon successful login/traversal to another page. This is a bit hacky, and is certainly not ideal, but is a quick and dirty fix. If you want to learn and do it correctly, Justin's suggestion is a great place to start.

Extjs - url navigation

Extjs prefers your app to be a single page app, but I'd still like to be able to do things like refresh my page and keep my current location in the app, and enter a url to get directly to a particular point in the app. Is there a solution for this?
Yes, I do the same in my app. You can use the Ext JS history mechanism to do so. Have a look at this example from Sencha.
You can listen to the history change event like this
Ext.History.on('change', function(token) {
// you navigate-to-target code goes here, e.g. change the viewport content
}
You can then initiate navigation by setting the browser hash to some navigation target
document.location.hash = yourNavigationToken;
This gives you also the ability to use deep-linking and forward/backward navigation with the browser buttons.
You need to init the history:
// The only requirement for this to work is that you must have a hidden field and
// an iframe available in the page with ids corresponding to Ext.History.fieldId
// and Ext.History.iframeId. See history.html for an example.
Ext.History.init();
and add an iframe and a hidden input field to your page, like in the example:
<form id="history-form" class="x-hide-display">
<input type="hidden" id="x-history-field" />
<iframe id="x-history-frame"></iframe>
</form>

Jquery mobile ui-btn-active in navbar

I've been trying to get this to work for 4 days now, with no luck.
I have a very simple jquery mobile app.
The app has a header, content and footer.
The footer is being generated dynamically on the 'pagecreate' event because it is always the same and I don't want to have its HTML in every page.
So I do something like this:
$(document).delegate('[data-role="page"]', 'pagecreate', function (e) {
DrawHeader($(this));
DrawFooter($(this));
SetFooterEvents($(this));
SetActiveFooter($(this));
});
DrawHeader() and DrawFooter() simply prepent the header div to the page and append the footer div.
SetFooterEvents() sets the onclick events of the footer navbar buttons and SetActiveFooter() is SUPPOSED to set the ui-btn-active to the current active footer link.
To do this, I've added the data-active-footer attribute to the page div and the data-name attribute to the navbar elements. I'm searching for the current element according to the data-active-footer in the page and apply the ui-btn-active class.
function SetActiveFooter(page) {
page.children('div[data-role="footer"]')
.find('a[data-name="' + page
.attr("data-active-footer") + '"]').addClass("ui-btn-active");}
So far so good.
Now, say I've changed to a page and the navbar is lit (it has successfully recieved the ui-btn-active class), and I'm clicking on the previous page, the lit item in the navbar doesnt change back!
If i click on the the page again (ie: changed to second page [corrent lit], changed back to first page [second page is still lit], then clicked on first page again) it does light the navbar button.
What I found out was that jqm also changed the navbar of the previous page when I'm changing the navbar of the current page in the 'pagecreate' event.
I've tried to overwrite this behaviour using the 'pageshow' event, that is, trying to apply the ui-btn-active class to the current element in the navbar but the problem is that $(this) and e.currentTarget objects in the 'pageshow' event DO NOT CONTAIN THE FOOTER ELEMENT!!!
$(".ui-page").live('pageshow', function (e) {
alert($(this).children('div').length); // returns 2!
alert($(this).children('div[data-role="footer"]').length); //returns 0
alert($(e.currentTarget).children('div').length); // returns 2!
alert($(e.currentTarget).children('div[data-role="footer"]').length); //returns 0});
Any ideas?!
Thanks.
Before delving into more detail, please try adding .ui-state-persist together with .ui-btn-active
This makes sure active buttons stay active when you changePage and the footer is the same. Also make sure, all your footers have the same data-id attribute.
On a sidenote: check the latest blog post about upcoming features for jqm 1.1 - it will include a fetch link utility, which allows to ajax-update portions of a page. So you could use this functionality to grab and insert a footer on every page. I'm trying the same right now with a login form, which I need on every page.
Have you tried "ui-state-persist"?
<div data-role="navbar" data-iconpos="top">
<ul>
<li>Home</li>
<li>Favorite</li>
</ul>
</div>
I still dont know why but jqm moves the footer from page to page, eventhough I assign a new footer to each page.
Maybe because I set the same ID to all of them.
Anyhow, I used this workaround to solve the problem:
On the 'pagebeforeshow' event, I set the button I want active to all the footers in the documents. I've set a special data-name attribute to each navbar button, I give it the 'ui-btn-active' class after removing it from the rest of the items.
var $footers = $(document).find('div[data-role="footer"]');
$footers.find('a').removeClass("ui-btn-active");
$footers.find('a[data-name="' + page
.attr("data-active-footer") + '"]').addClass("ui-btn-active");

Resources