I'm using vue.js to build a PWA. Despite the fact that this isn't a desktop browser app, I'm thinking of ways to better utilise the otherwise wasted real-estate by combining multiple views into a single larger view.
For example, on mobile, there are 2 views with distinct routes - Select Outlet and Order Items. Is there any way to combine both these views only for the desktop with minimal modification? Thank you!
Abstract
I have a proposition of solution. Not sure if it will satisfy you but maybe it will give you a new perspective at least.
Example
Router setup:
const routes = [
{
path: '/combined-components',
name: 'CombinedComponents',
components: {
default: WrapperComponent,
'firs-component': SelectOutlet,
'second-component': OrderItems
},
}]
Template setup:
<div class="wrapper-component">
<div class="container">
<div class="first-component">
<router-view name="first-component"></router-view>
</div>
<div v-if="isMobile" class="second-component">
<router-view name="second-component"></router-view>
</div>
</div>
<div>
Explanation
By using the name attribute for <router-view> we can decide which component will be displayed depending on the current route. So we have one parent component in this case WrapperComponent and inside it's template we can freely place another <router-view> and in routes.js (or just routes const that you pass to configure router) you can specify which component you want to be placed in the slot of first-component and second-component. That allows you to combine 2 of them and just proceed further routes without leaving WrapperComponent. Wha is left is to define if you should display the second component or not. Depending on isMobile that you can define yourself (I would assume based on screen width).
Docs
Read more here: Vue Router Docs
Summary
In this approach, it works from the other side. You prepare it for desktop and just restrict it for mobile.
Related
I was hoping to get some additional information about the "Nested Layouts" in InertiaJS. The documentation is very scarce on it and I can barely find any sort of examples showing how it works, and the documentation on the site isn't very descriptive about how it works or what the code is doing. (https://inertiajs.com/pages#persistent-layouts)
Basically I want to achieve functionality similar to in this tweet here;
https://twitter.com/klaasgeldof/status/1181198792995037184
Hopefully someone can provide some extra information because I've been having a lot of trouble getting this working correctly.
There are many approaches to this and I'd say they are more related to what your frontend stack is. Since this question is tagged with vue.js I'll answer based on that.
Approach #1
The inertia part here is just creating a layout - which is basically just a vue component. That layout then contains the side and top navigation and a slot to fill the body content. You can then create a dashboard page component which utilize that layout by adding layout: myDashboardLayout in the export.
To get the same effect, you basically route to different views, which pass different slots to the layout and their respective data.
// Your DashboardLayout.vue
<template>
<div>
<my-sidebar :data="foo" />
<my-topbar :data="bar" />
<slot name="dashboardContent" />
</div>
</template>
// Your Dashboard/Index.vue
<template>
<main slot="dashboardContent" :data="myData" />
</template>
<script>
import DashboardLayout from './DashboardLayout'
export default {
// Using the shorthand
layout: DashboardLayout,
}
</script>
//web.php
Route::get('/dashboard', function(){
return Inertia::render('Dashboard/Index', [data]);
});
Route::get('/dashboard/foo', function(){
return Inertia::render('Dashboard/Index', [fooData]);
});
Route::get('/dashboard/bar', function(){
return Inertia::render('Dashboard/Index', [barData]);
});
You then either visit domain.com/dashboard/foo or domain.com/dashboard/bar
Approach #2
Having the same layout view, but passing a "tab-view" component. You'd not switch the routes at all and provide that one tab-view component with props to render the three different tabs.
Depending on the data, this approach could be slower as you fetch everything up front.
With #1 you fetch the data on demand and since it's an SPA, the user would't notice a difference.
Another benefit of #1 is, the user can actually actively visit that one specific tab and bookmark its URL for frequent access. You could do the same with anchors/hashes but you're using an SPA after all.
I could go on, but choose either option or perhaps that was enough to give you some inspiration.
Add a <slot /> inside tab
Now the trick is to put two Layouts on the nested page.
https://inertiajs.com/pages
<script>
import SiteLayout from './SiteLayout'
import NestedLayout from './NestedLayout'
export default {
// Using a render function
layout: (h, page) => {
return h(SiteLayout, [
h(NestedLayout, [page]),
])
},
// Using the shorthand
layout: [SiteLayout, NestedLayout],
props: {
user: Object,
},
}
</script>
layout: [SiteLayout, NestedLayout] this is where the trick is.
I'm using React Router and I'm trying to link to a sub-component in another route by id. Basically what would usually be done using the <a href="www.url.com/profile/#profile-header-id">.
I'm not sure if there's a built in way for react router to do this, but if not perhaps I can manually trigger the link at a later point when I know the element has been rendered.
The issue isn't linking to another route which of course is done with the Link from react router. The issue is linking to an element which is found in the rendered HTML of the linked component.
Less Abstract Code Example:
So let's say my router is
<Route path"/A" component={A}>
<Route path"/B" component={B}>
component A has the following render:
render(){
<div>
// A looooot of text and other HTML elements
<div id="relevant-to-B">
// relevant stuff for component B
</div>
</div>
}
Now in component B, I want a Link that not only takes me to the Route "/A", but also scrolls to the element of id #relevant-to-B, thereby skipping all the irrelevant stuff.
Before anyone press eagerly the close button, I already have looked the following question: ReactJS Two components communicating. My problem is exactly the third scenario developped in the current accepted answer.
I am using ReactJS to build something with two components. For HTML reasons (and presentation), i want my two components to be at two different places of the page.
For the moment, I have the following pattern, corresponding to scenario #2:
FooForm = React.createClass({
...
});
FooList = React.createClass({
...
});
FooManager = React.createClass({
...
render: function () {
return (
<div>
<FooForm ref="form" manager={this} />
<FooList ref="list" />
</div>
);
}
});
React.render(
<FooManager someProp={value} />,
document.getElementById('foo')
);
This gives something like:
<div id="foo">
<form>Form generated with the render of FooForm</form>
<ul>List generated with the render of FooList</ul>
</div>
However, i would like to have something like this:
<div id="fooform">
<form>Form generated with the render of FooForm</form>
</div>
<!-- Some HTML + other controls. Whatever I want in fact -->
<div>...</div>
<div id="foolist">
<ul>List generated with the render of FooList</ul>
</div>
The problem here is: how can I keep a reference in each component? Or at least the link Form -> List?
I tried to create the FooList before and pass the reference to the current manager, but I get the following warning/error:
Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.
The documentation says you can attach events to link two components which do not have a parent-child relation. But I don't see how. Can someone give me some pointers?
The Less Simple Communication lesson from react-training has a good example of how you can move actions & state sideways to avoid having to create an explicit link between related components.
You don't need to jump into a full Flux implementation to get the benefit of this approach, but it's a good example to lead you up to Flux, should you eventually need it or something like it.
Note that this requires you to model the relationship between the components based on changing state rather than explicitly passing a reference to a component instance (as you're doing above) or a callback bound to the component managing the state.
This would be the perfect use-case for a Flux type architecture.
What you want is someone FooManager to be able to trigger state changes in both components. Or, in fact, having the different components trigger, through Actions, state changes in each other.
The Flux Todo-App Tutorial illustrates your use-case perfectly!
After this, then you'd have the choices of using Facebooks implementation of Flux or the other gazillion ones.
My personal favorite is Reflux
Routing in Ember.js is troubling me, and I can't seem to find the "correct" way of doing what I want to do.
I have a route, let's call it #/map, which contains a series of top-level and containers of child views.
Hierarchically, I have a top map_view, which contains 4 additional views: A topbar (which has topbar menu item triggers within it), a sidebar (which has sidebar menu item triggers in it), and two containerViews (a sidebar menu containerView and a topbar menu containerView), which will contain one or more nested views that are programatically inserted on clicking a menu item trigger.
My issue is that while this works, and I can embed all of these views into their various templates, none of them are linking with controllers, and the controller they are picking up is the map_controller (which makes sense as that is the linked outlet controller for the top level view). Currently I am using a method described on Ember's github here, but it seems a little...hacky?
Here is a JSFiddle showing the problem. Notice that the controller for level-one-entry and level-two-entry is the index_controller: http://jsfiddle.net/fishbowl/Z94ZY/3/
Here are some code snippets for what I am doing to get around it:
map.hbs:
<section id='map'>
{{view App.SidebarView}}
{{view App.TopbarView}}
<div id='map-canvas'></div>
</section>
topbar_view.js:
var TopbarView = Em.View.extend({
templateName: 'topbar',
classNames: ['topbar-container'],
init: function() {
var content = this.get('content'),
controller = App.TopbarController.create({
view: this
});
this.set('controller', controller);
this._super();
}
});
module.exports = TopbarView;
topbar_controller.js
var TopbarController = App.ApplicationController.extend({
content: Ember.computed.alias('view.content'),
trigger: null,
start_date: null,
end_date: null,
travelling: null,
word: 'topbar'
});
module.exports = TopbarController;
I'm not doing anything special in the router other than declaring this.route('map'). A further problem i'm having is that whenever I declare needs: ['some_other_controller'], I get an error
<App.TopbarController:ember413> specifies 'needs', but does not have a container. Please ensure this controller was instantiated with a container.
Am I missing something blindingly obvious about how to go about linking these together. I'm guessing that i'm using routing incorrectly. I don't want to change what the URL is, as i'm technically not moving pages, just opening and closing menus on the page, but I don't really understand how else i'm supposed to use the router to achieve this.
EDIT 2: i've mocked up another jsfiddle of what I could do with outlets and link-to's, but i'm not sure that I want the URL changing (as you'd probably be able to do odd things with the back button etc): jsfiddle - The alternative to this is to set location: 'none' in the Router, but I don't really like that option either...
I have the below route structure.
App.Router.map(function() {
this.resource('projects', function() {
this.resource('listings', {path: '/:project_id/listings'}, function() {
this.route('listing', {path: '/:property_code'});
});
});
});
I replicated this structure and created a fiddle.
Fiddle: http://jsfiddle.net/aqHnt/6/
I hope the router is self-explanatory. I have a bunch of projects which each have a bunch of listings and each listing will have some additional details. Because these are nested resources, each child resource renders in to the {{outlet}} of it's parent template.
What I need is to entirely overwrite the parent template and as per a suggestion in a different post, I'm using the resources index route to achieve this.
So If you click on a project, the entire projects template will be replaced with the listings template. It's all good up to this point but I can't seem to achieve the same with listings. When I click on a listing, I want the entire listings template to be replaced by listing details. Can someone point out what am I doing wrong here.
Here is a possible solution: jsfiddle.
You were missing the listings template besides the listings/index one, as well as the route ListingsIndex. This way you replicate the same pattern you use at application level.
You could also consider using renderTemplate to specify in which outlet you need to render a given template.
Hope it helps!