Package: djangocms-spa-vue-js
This package prepares your django CMS and vue.js project to create a single-page application (SPA). Use it together
with the base packagedjangocms-spa.A template tag renders a list of all available routes that are used by vue-router. Contents of other pages are
requested asynchronously and delivered as JSON through a REST-API.Make sure you read the docs of djangocms-spa.QuickstartInstall djangocms-spa-vue-js:pip install djangocms-spa-vue-jsAdd it to yourINSTALLED_APPS:INSTALLED_APPS=(...'djangocms_spa','djangocms_spa_vue_js',...)Add the URL pattern form the API:urlpatterns=[...url(r'^api/',include('djangocms_spa.urls',namespace='api')),...]Render your vue.js router in your template:{% load router_tags %}
{% vue_js_router %}ApphooksYou need to consider a couple of things when using apphooks. Let’s assume you have an event model.classEvent(DjangocmsVueJsMixin):name=models.CharField(max_length=255,verbose_name=_('Name'))defget_frontend_list_data_dict(self,request,editable=False,placeholder_name=''):# Returns the data for your list view.data=super(Event,self).get_frontend_list_data_dict(request=request,editable=editable,placeholder_name=placeholder_name)data['content'].update({'name':self.name,})returndatadefget_frontend_detail_data_dict(self,request,editable=False):# Returns the data for your detail view.data=super(Event,self).get_frontend_detail_data_dict(request,editable)# Prepare the content of your model instance. We use the same structure like the placeholder data of a CMS page.content_container={'type':'generic','content':{'name':self.name}}# Add support for the CMS frontend editingifeditable:content_container.update(self.get_cms_placeholder_json(request=request,placeholder_name='cms-plugin-events-content'))# Put the data inside a container like any other CMS placeholder data.data['containers']['content']=content_containerreturndatadefget_absolute_url(self):# Return the URL of your detail view.returnreverse('event_detail',kwargs={'pk':self.pk})defget_api_detail_url(self):# Return the API URL of your detail view.returnreverse('event_detail_api',kwargs={'pk':self.pk})defget_detail_view_component(self):# Return the name of your vue component.return'cmp-event-detail'defget_detail_path_pattern(self):# Return the path pattern of your named vue route.return'events/:pk'defget_url_params(self):# Return the params that are needed to access your named vue route.return{'pk':self.pk}All of your views need to be attached to the menu, even if they are not actually rendered in your site navigation.
If the CMS page holding your apphook uses a custom view, you need this configuration:DJANGOCMS_SPA_VUE_JS_APPHOOKS_WITH_ROOT_URL=['<my_apphook_name>']Yourcms_menus.pymight looks like this:classEventMenu(CMSAttachMenu):name=_('Events')defget_nodes(self,request):nodes=[]counter=1is_draft=self.instance.publisher_is_draftis_edit=hasattr(request,'toolbar')andrequest.user.is_staffandrequest.toolbar.edit_mode# We don't want to parse the instance in live and draft mode. Depending on the request user we return the# corresponding version.if(notis_editandnotis_draft)or(is_editandis_draft):# Let's add the list viewnodes.append(NavigationNode(title='Event List',url=reverse('event_list'),id=1,attr={'component':'cmp-event-list','vue_js_router_name':'event-list','fetch_url':reverse('event_list_api'),'absolute_url':reverse('event_list'),'named_route_path_pattern':':pk',# Used to group routes (dynamic route matching)'login_required':True# Hide a navigation node for unauthorized users}))counter+=1foreventinEvent.objects.all():nodes.append(NavigationNode(title=event.name,url=event.get_absolute_url(),id=counter,attr=event.get_cms_menu_node_attributes(),parent_id=1))counter+=1returnnodesmenu_pool.register_menu(EventMenu)This is an example of a simple template view. Each view that you have needs an API view that returns the JSON data only.fromdjangocms_spa.viewsimportSpaApiViewfromdjangocms_spa_vue_js.viewsimportVueRouterViewclassContentMixin(object):template_name='index.html'defget_fetched_data(self):data={'containers':{'content':{'type':'generic','content':{'key':'value'}}}}returndataclassMyTemplateView(ContentMixin,VueRouterView):fetch_url=reverse_lazy('content_api')# URL of the API view.classMyTemplateApiView(ContentMixin,SpaApiView):passYour list view looks like this:fromdjangocms_spa.viewsimportSpaListApiViewfromdjangocms_spa_vue_js.viewsimportVueRouterListViewclassEventListView(VueRouterListView):fetch_url=reverse_lazy('event_list_api')model=Eventtemplate_name='event_list.html'classEventListAPIView(SpaListApiView):model=Eventtemplate_name='event_list.html'Your detail view looks like this:fromdjangocms_spa.viewsimportSpaDetailApiViewfromdjangocms_spa_vue_js.viewsimportVueRouterDetailViewclassEventDetailView(VueRouterDetailView):model=Eventtemplate_name='event_detail.html'defget_fetch_url(self):returnreverse('event_detail_api',kwargs={'pk':self.object.pk})classEventDetailAPIView(SpaDetailApiView):model=Eventtemplate_name='event_detail.html'The router objectThe server needs to prepare the routes for the frontend. The easiest way to do this is by iterating over the CMS
menu. In order to bring all available routes to the menu, you have to register all your custom URLs as a menu too.
A template tag renders a JS object like this.{"routes":[{"api":{"fetch":"/api/pages/","query":{"partials":["menu","footer"]}},"component":"index","name":"cms-page-1","path":"/"},{"api":{"fetched":{"partials":{"menu":{"type":"generic","content":{"menu":[{"path":"/","label":"Home","children":[{"path":"/about","label":"About","children":[{"path":"/contact","label":"Contact"}]}]}]}},"footer":{"type":"cmp-footer","plugins":[{"type":"cmp-footer-text","position":0,"content":{"text":"Lorem ipsum dolor sit amet, nam et modus tollit."}}]}},"data":{"meta":{"description":"","title":"Content-Plugins"},"containers":{"main":{"type":"cmp-main","plugins":[{"type":"cmp-text","position":0,"content":{"text":"Ex vim saperet habemus, et eum impetus mentitum, cum purto dolores similique ei."}}]}},"title":"About"}},"query":{"partials":["menu","footer"]}},"component":"content-with-section-navigation","name":"cms-page-2","path":"/about"},{"api":{"fetch":"/api/pages/about/contact","query":{"partials":["menu","meta","footer"]}},"component":"content-with-section-navigation","name":"cms-page-3","path":"/about/contact"}]}DebuggingIf you need to debug the router object, this middleware is probably pretty helpful:MIDDLEWARE+=('djangocms_spa_vue_js.middleware.RouterDebuggingMiddleware',)CreditsTools used in rendering this package:Cookiecuttercookiecutter-djangopackageHistory