I try to develop a simple Ember.js app.
I think that these screens describe the desired scenario good enough.
Buttons on the top are #link-tos. Note that I want the first button to be highlighted on the 3rd screen.
It's easy to find examples where the 3rd template lives in the outlet in the 2nd one, but I need some kind of template replacement in the main outlet.
Please help me to achieve this behavior. Hope that my description is clear enough.
You can make list and detail routes same level, so they both are rendered to the same outlet, one at time. Like that:
App.Router.map(function() {
this.resource("movies", function() {
this.route("list");
this.route("view", {path:"view/:movie_id"});
});
});
App.MoviesIndexRoute = Em.Route.extend({
redirect : function() {
this.transitionTo('movies.list');
}
});
All the rest is done as allways
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.
A question regarding transclude within an angular 1.5.8 component, and it's uses.
Here is an example of some code;
var app = angular.module('app', [])
function AccordionController () {
var self = this;
// add panel
self.addPanel = function(panel) {
// code to add panel
}
self.selectPanel = function() {
//code to select panel
}
}
// register the accordion component
app.component('accordion', {
template: '<!---accordion-template-->',
controller: AccordionController
}
function AccordionPanelController () {
// use parents methods here
var self = this;
// add panel
self.parent.addPanel(self);
// select panel
self.parent.selectPanel(self);
}
// register the accordion-panel component
app.component('accordionPanel', {
// require the parent component
// In this case parent is an instance of accordion component
require: {
'parent': '^accordion',
template: '<!---accrodion-panel-template-->',
controller: AccordionController
}
My question is would it be better to nest all the according panels within the parent using transclude or alternatively pass in a data array to the parent which this loops out the required number of panels based on the array passed inside using a binding.
Thanks
// added
Many thanks for your reply, an example I have of transclude possibly being necessary is in the following bit of code
<modal modal-id="editCompany" title="Edit Company"> <company-form company="$ctrl.company"></company-form> </modal>
Here we have a modal component which may have a variety of other components used within it, on the example above I am adding the company form, but this could we be an contact form. is there an alternative way?
I've worked with angular pretty extensively. Two enterprise tools managing and displaying large amounts of data, dozens of interactive widget modules, all that.
Never, once, have I had anything to do with transclude. At work we are explicitly told not to use it (link functions too). I thought this was a good thing, and the way Angular 2 turned out it seemed that thinking wasn't totally without reason.
I would go with the iteration to lay out the required number of items. At work this wouldn't be a choice because transclude wouldn't be an option.
The thing with using transclude in a component architecture is that it visually breaks the idea of single responsibility and messes with the architecture.
<html>
<navigation></navigation>
<accordion>
<accordion-panel></accordion-panel>
</accordion>
<footer></footer>
</html>
In this example you know your page has a navigation menu, an accordion and a footer. But at the index level (or root component) you don't want to know / see what the accordion contains.
So the accordion-panel component should only appear in its direct parent component.
As for your other question, through the use of require or attributes you pass an array of panels that you iterate using ng-repeat inside the accordion component.
app.component('accordion', {
template: `<accordion-panel ng-repeat="..."></accordion-panel>`,
controller: AccordionController
}
I'm new to ember and am struggeling with the typical "how would one do that"-Problem. What I've got is fairly simple and I know how to do it, but my way is so complicated that I do not think it's correct.
The case:
<ul>
<li>{{link-to top-level}}</li>
<li>{{link-to another-top-level</li>
<ul class="submenu">
<li>{{link-to submenu</li>
</ul>
</ul>
What should happen is:
When a route is clicked, the corresponding list element should become active.
When a submenu is clicked the corresponding upper ul-element should get the class open
It's a fairly simple case with jQuery, but I understand that this is not scalable and abstracted and stuff.
Therefore I started with this approach:
Create a controller / template construct for the entire navigation to handle it's state (there are some other things I need to check as well, so it came in handy).
since ember adds the active class to the anchor tag I created a component to observe that:
Like:
export default Ember.Component.extend({
tagName: 'li',
classNameBindings: ['active'],
active: function() {
return this.get('childViews').anyBy('active');
}.property('childViews.#each.active')
});
Replacing the li elements with {{linked-list}} does indeed work.
But what next? Do I need to add another component to watch the component to watch the build in behaviour of active links? Do I have to write dedicated MVC-Classes for all the DOM Elements?
There has to be a simpler way, I think. I already created a whole lotta files for such a simple behaviour that I'm thinking I'm totally on the wrong track.
My gut feeling is: That is view logic and the view should just observe a few states in the template and that's it.
What's the leanest approach to the problem?
I don't know if I understand your question right, but why you want to add the class open to the corresponding upper element? It automatically get active assigned. And with correct CSS it should work as expected.
I have created a small example demonstrating what I mean. Please have a look and let me know, if that's the solution for you or what's your problem with this solution.
http://emberjs.jsbin.com/wifusosadega/7/edit
EDIT
Here is a Bootstrap flavored version: http://emberjs.jsbin.com/wifusosadega/9/edit .
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!