Vue-js and different layouts - vue.js

how would one layout the router-views, if you have, lets say 3 different layout to use for you app (e.g. layout for customers, layout for employer and admin-interface).
At the moment i implemented the customer "view" like:
<template>
<div id="app">
<HeaderBar/>
<Navigation></Navigation>
<router-view></router-view>
</div>
</template>
inside the App.vue file. I could use something like:
<div id="app">
<router-view name="header">
<HeaderBar/>
<Navigation></Navigation>
</router-view>
<router-view></router-view>
</div>
</template>
and load different headers for those "subroutes" but this seem's to be odd.
Also what, if i'd like to use an other index.html?
I'm using webpack for this app.
Or would you suggest to create different apps for this?
Many thanks
rene

You could try using dynamic components.
Basically you change which component is being rendered depending on your route.
So it would probably be something like this:
<component v-bind:is="headerComponent"></component>
and then your app can contain heeaderComponent property in data object that has a default value and gets changed when you click on a route that should use different header. Rinse and repeat for the footer.
As for the last question, I think you should only use one app. As it is possible to do and everything is still connected. I'm not sure but I don't know how would you, if need be, communicate between different instances of Vue.

Related

Vue: communicating parameters from app to component and back, using computed properties

Sorry for the beginner question (I'm fairly sure this will be a duplicate, but I actually can't figure out what terms to use in order to find it). I just started with Vue.
I am just getting started with Vue, and following this course (https://www.vuemastery.com/courses/intro-to-vue-js/communicating-events). In this problem, there is (and here I don't know the term, so I'm going to go with...) app-level data parameter called cart. cart is an array which holds the id of each item a user has added to cart.
The problem tells us to add a button to remove items from the cart.
I ran into problems trying to create a computed property, which would allow me to hide the "remove" button in the event the selected item is not in the cart (eg. `
Communicating data from the app-level (the cart array), to the component-level (to a computed property in the product component), so that I could use something like :hidden="!inCart" on the "Remove from Cart" button, which is itself defined in the component. inCart would be a computed value here.
Communicating the selected product from the component to the app-level, computing inCart at the app level, then using the computed value at the component-level.
Either way, I can't seem to figure out how to do this in the way I would want to, which would look something like how v-bind operates. Namely, I think I may be able to hack together a solution using methods (which I believe have to be triggered by certain events), but I don't understand how I might go about this using built-in functionality such that the value of inCart is dynamically auto-computed.
Maybe there would be an answer to this in the next few courses, but I don't see us covering that in the intro material. Sorry for the neophyte question. Thank you in advance.
In Vue the way you communicate "state" from higher-level objects to lower-level objects is through props.
So, assuming your app looks something like...
<MyApp>
<MyShoppingPageWithItems>
<MyItem></MyItem>
<MyItem></MyItem>
<MyShoppingPageWithItems>
</MyApp>
You need to pass the cart object down as a prop.
So in your MyShoppingPageWithItems template, you'll have something like...
<template>
<div>
<MyItem v-for="item in items" :item=item :cart="cart"?
</MyItem>
</div>
</template>
And in your item template...
<template>
<div>
<div>
{{item.name}}
</div>
<div v-if="cart.includes(item.id)">
Remove button or whatever
</div>
</div>
</template>
Not that the .includes() method is a native JavaScript method, which you can read more about here.
Edit
To reference a prop in a computed property (or anywhere else in a Vue component), just refer to this.propName, as demonstrated here in the Vue docs.
So, if you want to create a computed property, you can do the following:
<template>
<div>
<div>
{{item.name}}
</div>
<div v-if="isInCart">
Remove button or whatever
</div>
</div>
</template>
<script>
export default {
props: ['cart', 'item'],
computed: {
isInCart() {
return this.cart.includes(this.item.id)
}
}
}
</script>
Note that the formula is the exact same as above, but just includes this. for cart and item. In templates, the this. is implied when referring to props, data, and computed properties.

Modal window in nuxt architecture

Before I was working with Vue2JS and I used to creating modal as just component within App.vue root component for example:
<template>
<div>
<app-navbar></app-navbar>
<router-view></router-view>
<app-footer></app-footer>
<my-modal v-if="someBoolean"></my-modal>
</div>
</template>
Now basing on some custom events or Vuex storage I was able to change someBoolean and trigger when I want modal to be visible.
Since in Nuxt we don't have such thing as root App.vue component I'm wondering how to achieve same as above but with Nuxt.
Of course I could use some package as bootstrap-vue but I don't really want to inject this big package just for that one purpose.
You can write code in layouts/default.vue file and this file works on your defaults, work the code at where you used as a layout of your pages(generally almost everywhere.)
Different approach is use portalvue to render components whereever you want. Nice article here but in Turkish.

How to open multiple pages in the same app?

I know that Aurelia is a framework for an SPA but I need to open multiple pages at the same time inside the browser.
I want to host each page (view/viewmodel) within a draggable div and have more than one open at the same time. They will be selected by the User from a menu.
I have looked at viewPorts but cannot see how they will help in this problem.
Is there another way to do this?
Yes, there is a way to do this.
You say pages, but basically they are components. Every component has a HTML (view) and JS (view-model) file. They can be included as custom-elements.
So if you want multiple possible components on one page, all you need to do is create one component that has a placeholder for those views.
Probably, you would want something looking like this:
running Gist example
<template>
<require from="./page1"></require>
<require from="./page2"></require>
<page1></page1>
<hr>
<page2></page2>
</template>
You can create loop for all elements you want to display, if you need multiple instances of the same page, and put a repeat.for on the elements:
HTML:
<template>
<require from="./page1"></require>
<require from="./page2"></require>
<page1 repeat.for="i of page1instances"></page1>
<hr>
<page2 repeat.for="i of page2instances"></page2>
</template>
JS:
export class App {
const page1Instances = 4;
}
Making them drag&droppable works like every other element, there are many solutions for that to be found. Just make the <page1> and <page2> draggable.

Aurelia: if binding and content selectors

I have found content selectors don't seem to work when an if binding is used on a parent element. For example:
some-element.html
<template>
<div if.bind="true">
This will appear
<content></content>
</div>
</template>
app.html
<template>
<some-element>This will not appear</some-element>
</template>
This works fine if I don't use the if binding, but will not render <content> when I do use the if binding. Is there something I'm doing wrong here or is there a way to get this to work as expected?
I'll be adding this to our documentation soon, but for now, here is #EisenbergEffect's explanation from https://github.com/aurelia/framework/issues/375
"No. That is a characteristic of the shadow dom. The content selection points have to be static. They cannot be added ore removed dynamically. If you want to hid them, then consider using show.bind instead."

Good way to integrate query-layout with reactive templates + async data sources?

I'm working on a Meteor app that gets data from Facebook & I would like to use jquery-layout for presentation. I suspected that there might be some "subtleties" when trying to use jquery to modify HTML in reactive templates, so I set up a relatively simple test case that goes something like this (paraphrased for brevity)...
<body>
{{> mainTemplate}}
</body>
<template name="mainTemplate">
{{#with userInfo}}
{{> partialNorth}}
{{> partialWest}}
{{> partialCenter}}
{{> partialEast}}
{{/with}}
{{layItOut}}
</template>
Template.mainTemplate.userInfo returns contents of a Session variable that starts with a default value and asynchronously get updated with info from Facebook.
Template.mainTemplate.layItOut sets up a call to Meteor.defer with a callback fcn that actually executes the 5 lines of jquery-layout code.
And that seems to work pretty well...
the initial display is as expected/intended (although there's a brief period where the page is not laid out)
any updates to the reactive context cause re-execution of the layout (again, w/brief-but-visible re-layout)
So, why am I whining? Mostly I would like to find a cleaner approach that does away with the noticeable re-layout activity.
I could make the reactive contexts more granular, but I'm not sure that this would really help.
Alternatively, I suppose I could experiment with directly controlling rendering (e.g., via Meteor.ui.render() , but that sounds like a lot of work ;-)
I think what I'd really like is either
a) a way to hook into Meteor render events
or better still
b) a cleaner way to connect query-layout to templates
Thoughts?
So I managed to educate myself enough to answer my own question in case anyone else finds it useful. Bottom line is that I was wrong on several levels; making the reactive contexts more granular is the answer (or at least an answer).
In the example I gave, I made the whole page reactive by placing all of the rendering within the #each construct. What I now do is try to make the reactive contexts as small as possible so that only a (relatively) small part of the page is re-rendered on any reactive change and all of the reactive elements are contained below (or way below) the level of the jquery ui elements.
I.e., something like this:
<body>
{{> mainTemplate}}
</body>
<template name="mainTemplate">
{{#with userInfo}}
{{> partialNorth}}
{{> partialWest}}
...
{{/with}}
{{layItOut}}
</template>
<template name="partialNorth">
<div class="ui-layout-north"> <-- definition for jquery-layout north panel
<h1>This is the north pane</h1>
<p>The user data go next:</p><br />
{{> templateUserData}}
</div>
</template>
<template name="templateUserData">
<div>
{{#with theUserData}} <-- Assumes a helper function 'theUserData' for this template
<p>First name: {{first_name}}</p>
<p>Last name: {{last_name}}</p>
...
{{/with}}
</div>
</template>
Once you have the reactive elements below the jquery ui elements (I have tried it with panels, tabs, accordions, buttons and pop-ups so far) the whole thing works just like it said it would in the shiny brochure! ;-)