Spine.js - How to set up nested stacks - spine.js

I'm trying to set up nested stacks in Spine.js.
Although I copy-pasted the code that seems to work for others (https://gist.github.com/MikeSilvis/2839845) and adjusted the controller & model names it doesn't work.
Both stacks get shown perfectly right, there are no errors in the console either. But - if I don't fully misunderstand the usage of nested stacks - they are not nested within a root stack. Is there something that I have to add in the views?
index.coffee:
class App extends Spine.Controller
constructor: ->
super
new Spine.SubStack
Spine.Route.setup()
#append(#groups = new App.Groups)
#append(#people = new App.People)
class App.Root extends Spine.Stack
$.fn.item = ->
elementID = $(#).data('id')
elementID or= $(#).parents('[data-id]').data('id')
Person.find(elementID)
controllers:
groups: App.Groups
people: App.People
routes:
'/groups' : 'groups'
'/people' : 'people'
default: 'people'
className: 'stack root'
class Spine.SubStack extends Spine.Stack
constructor: ->
for key,value of #routes
console.log [key, value].join(" | ")
do (key,value) =>
#routes[key] = =>
#active()
#[value].active(arguments...)
super
window.App = App
in groups.coffee:
class App.Groups extends Spine.SubStack
controllers:
index: Index
edit: Edit
show: Show
new: New
routes:
'/groups/new': 'new'
'/groups/:id/edit': 'edit'
'/groups/:id': 'show'
'/groups': 'index'
default: 'index'
className: 'stack groups'
in people.coffee:
class App.People extends Spine.SubStack
controllers:
index: Index
edit: Edit
show: Show
new: New
routes:
'/people/new': 'new'
'/people/:id/edit': 'edit'
'/people/:id': 'show'
'/people': 'index'
default: 'index'
className: 'stack people'
All routes are still working after adding this code, but there is no root stack.
Hopefully someone with little more experience than me can help me out of this problem!

Without doing too much research, it seems like you should have a new App.Root or extends App.Root somewhere. That could explain why your routes are working, but there is no root stack.

Related

Vuex state structure and fetching when using same type of data with different values on different routes

I'm creating a portfolio with vue, vuex and vue-router that will show images.
On the homepage i will show images with 'show_home: true'.
Then there is "tag" pages (portfolio/:tagSlug) that will show images based on a slug, eg. 'weddings' with infinite scroll (auto populate pagination).
An image object will look something like:
{
id: 1,
title: 'Lorem..',
path: '..',
show_home: true
tags: [ {id: 1, slug: 'weddings'} ]
},
{
id: 2,
title: 'Lorem2..',
path: '..',
show_home: false
tags: [ {id: 1, slug: 'weddings'}, {id: 2, slug: 'water'} ]
}
Endpoints examples:
Homepage: GET: ../images/homepage?p=1
Tag page: GET: ../images/:slug?p=1
I can't figure out how I should structure this in vuex and handle the fetching..
Should i just create i single 'images: []' state and populate it with ALL the images after fetching them from the api in each route, then filter them with getters? How can i get the pagination in there in that case? Or do you have a better solution?
Thanks in advance
My preferred approach is to "flatten" the relationships and pull them as needed. This also allows you to only pull what you need from the server or related modules.
tags vuex module:
all: {
1: { <-- indexed by tag id
name: "weddings"
images: [1,2,3,4] <-- image ids
}
}
active: false <-- When there is an active tag, this becomes the id of the tag.
The vuex images module would follow this same pattern:
all: {
1: { <-- indexed by image id
title: 'Lorem..',
path: '..',
show_home: true
tags: [1,2,3,4] <-- tag ids
}
}
active: false <-- When there is an active image, this becomes the id of the image.
Then use a getter to hydrate the images or tags from the respective vuex module.
There is a great write up on this approach on this blog: https://medium.com/js-dojo/structuring-vuex-modules-for-relationships-speed-and-durability-de25f7403643
With this approach you will have fewer and smaller api calls, pagination is manageable and you don't need to worry about stale data in your relationships.
EDITED -- API info:
Two approaches come to mind.
1) always load the images with the tag.
Tag index request would not load any images, just the basic info for each tag.
When the user clicks on a tag, this inits an API call for the tag details:
Tag show request (tags/1 or tags/weddings) would return the tag with loaded relationships:
public function show($id)
{
$tag = Tag::where('id', $id)->with('images')->firstOrFail();
return new TagResource($tag); <-- will have all related images loaded.
}
2) set up a nested REST endpoint if needed
You can use the the resource controllers to shortcut the boilerplate like this:
api.php
Route::apiResource('tags.images', 'tags\TagImageController');
This route will watch your api calls and determine if it is index/store/show/delete. From your front end you can make a call like https://backendsite.com/tags/1/images (If wedding tag has an id of 1)
Then in the TagImageController you would have something like this:
public function index(Request $request, $id)
{
$tag = MemTag::find($id);
$images = $tag->images()->get();
$images->load(Image::allowedIncludes); <- or you can manually list relationships you want to load
return ImageResource::collection($images);
}

Routing / URL generation issues

I'm currently building an FAQ system which has the following URL structure:
Parent category: /faqs/parent-category/ (eg /faqs/products/)
Child article within a parent category: /faqs/parent-category/child-article/ (eg /faqs/products/how-to-build-my-product/)
I have the following routes set up:
routes.MapRoute(
name: "Faq.Main",
template: "{controller}/{parentSlug}/",
defaults: new { controller = "Faqs", action = "Index" }
);
routes.MapRoute(
name: "Faq.Article",
template: "{controller}/{parentSlug}/{slug}/",
defaults: new { controller = "Faqs", action = "View" }
);
The URL's generate fine for the parent, however I'm having an issue with the child articles. An example generated URL is as follows: /faqs/how-to-build-my-product/view/?parentSlug=products
The code in the View to create these links is as follows:
<a asp-controller="Faqs" asp-action="View" asp-route-parentSlug="#faq.ParentSlug" asp-route-slug="#faq.Slug">#faq.Title</a>
As you can see the URL generated for the child article isn't formatted correctly. It needs to be /faqs/products/how-to-build-my-product/
Any ideas how I can rectify this?
Thanks in advance.

Dynamic parameter in href option in Aurelia Routing config.map

This seems like a really simple issue, but it's driving me crazy...
Does anyone know how I can specify a dynamic :id parameter in the href routing configuration option?
The following unfortunately doesn't work:
config.map([
// ... default parameterless routing here
{
route:[':id/request'],
moduleId:'processes/bdd/request/request',
name:'Request', title:'Request', href:`#/bdd/request/${id}/request`, settings:{type:'Request', icon:''}, nav:true,
},
{
route:[':id/requestAuth'],
moduleId:'processes/bdd/request/requestauthorization',
name:'RequestAuthorization', title:'Request Authorization', href:`#/bdd/request/${id}/requestAuth`, settings:{type:'Request', icon:''}, nav:true,
},
// ... some additional mappings here
]);
The href property is static. If you want to generate a route for a link using this route, you could use the route-href custom attribute like this:
route-href="route: request; params.bind: { id: someProp }"
Note that I changed the route name to be camelCase (all lowercase since it is one word here) to match the route naming convention.
I had a similar use case and I was able to get this to work by adding a pipeline step to the router that alters the config on the fly.
My use case may be a little different in that I only want the item to appear in the nav bar when the route is active -- say I have routes /abc, /def/:id, and /ghi -- when the active route is ABC or GHI, only those items will appear in the nav bar, but when the active route is DEF, it should appear in the nav bar, and clicking it should lead to the same DEF ID you're currently looking at. So my route configuration includes a setting that indicates the route should only appear in the nav bar when it's the active route.
Here are the interesting parts of my actual configureRouter function:
configureRouter(config, router) {
config.addPreActivateStep(Preactivate); // explained below
config.map([
// some routes
{ route: 'patients/:patientId', name: 'patient-master',
moduleId: 'patients-and-cases/patient-master/patient-master',
title: 'Patient', nav: true, settings: { renderOnlyWhenActive: true },
href: '#' // it doesn't matter what this is
},
// more routes
]);
}
And here is the Preactivate class that sets the href on preactivate:
class Preactivate {
run(routingContext, next) {
routingContext.config.href = routingContext.fragment;
return next();
}
}
If, unlike me, you want this to display in a nav bar all the time, this will still work -- the href will simply remain set to the last thing it was set to when the route was active. In that case you'd probably want to initialize it to some default value that makes sense.

Partial view works different inside areas and outside of them

i have two controllers outside the areas and a few areas for my application
my Layout page contains a partial view rendering code:
#Html.Action(actionName: "LoginBar", controllerName: "Login")
and it is registered in RouteConfig
routes.MapRoute(
name: "LoginBar",
url: "loginbar",
defaults: new
{
controller = "Login",
action = "LoginBar"
});
when i browsing page in HomeController: /home it works well
when i'm trying to browse pages inside areas: /contentedit/articles the exception throws on a line #Html.Action:
InvalidOperationException: No route in the route table matches the supplied values.
any ideas why this exception throws and what code modifications I need?
Inside #Html.Action(actionName: "LoginBar", controllerName: "Login") you can give area as object route like this way :
#Html.Action("LoginBar","Login",new { area = "//your area name//" })

How to make parent stack active when nested stack responds to route

Question
I'm developing a fairly large app and I've run into a problem that seems common. But I haven't been able to find any solutions to it on the spine groups or here at SO.
So the question is, how do I make sure, that parent stacks becomes active when nested stacks responds to a route. How is this solved properly?
I managed to solve this by simply using #active within the parent controller for the route. This is doing the same as this.active. Here is an example of how i've done it…
Spine = require('spine')
$ = Spine.$
# Controllers
Main = require('controllers/posts/posts.main')
Nav = require('controllers/navigation/navigation')
class Posts extends Spine.Controller
className: 'posts top-controller'
constructor: ->
super
#nav = new Nav
#main = new Main
#routes
'/posts/new': ->
#active()
#nav.post.active()
#main.new.active()
'/posts/suggestion': ->
#active()
#nav.normal.active(title: "Groups near by")
#main.matches.active()
'/posts/:id': (params) ->
#active()
#nav.chat.active()
#main.show.active(params)
#append #nav, #main
module.exports = Posts