Multiple Single File Components and Vue Webpack Template - vue.js

I have used https://github.com/vuejs-templates/webpack to set up a simple Vue site. This template comes with one single file component (App.vue). I am trying to figure out how would I build a site with multiple single file components?
For example, let's say I have a webpage that has four single file components on it: a table component that can sort and paginate the table data, a filter component that filters the table data, a button component that has a number of buttons like new/edit/delete, and a header component that has a drop down with options that when selected swap out the available filters and the data in the table.
I assume I would create four .vue pages (Table.vue, Filter.vue, Button.vue, and Header.vue) but I'm not really certain how to include them all on the same page. I assume I would use App.vue as the container for the other four single file components, but I'm not sure how to include the other four .vue pages into the App single file component. Should there be references to them in main.js?

Import them into whichever parent component definition you are using them in and register them in the components property of the parent component:
import MyTable from 'path/to/Table'
import MyFilter from 'path/to/Filter'
import MyButton from 'path/to/Button'
import MyHeader from 'path/to/Header'
export default {
components: { MyTable, MyFilter, MyButton, MyHeader },
}
Then, you can use their tags in the parent component's template:
<my-table></my-table>
<my-filter></my-filter>
<my-button></my-button>
<my-header></my-header>
Alternatively, you could register them globally in main.js:
import Table from 'path/to/Table'
Vue.component('my-table', Table);
This way you could use the <my-table> tag in any component without having to register it to that component.

Related

Create component in template from data variable

I have multiple components that take same props. I want to use those components in template in such a way that I don't use multiple if-else statements in my template. I created an object in my data and paired my components with string keys. Is there a way to call those components in template with that object ? My data object looks something like this:
componentMap:{
"testComponent1":TestComponent1,
"testComponent2":TestComponent2,
},
For example, if I give "testComponent1" as key, then in template it should use TestComponent.
Use the component tag.
<component :is="componentMap['testComponent1']"></component>
See the Docs

media queries and styled components

I like to have all the media queries at one place, usually in the App.css file because when I want to change something depending on the size I see all the components involved at once.
I am looking for a nice way to do so with styled components. There styles are usually attached to the file where the styled components are defined. I don't want to use wrappers to refer to them with className.
Does someone has a good way to handle this?
After a discussion following solution came up:
const reducer = (accumulated, [condition, css]) =>
accumulated +
`
#media(${condition}) {
${css[componentName]}
}
`
const addMedia = componentName => Object.entries(theme.media).reduce(reducer, "")
Now you have all the media queries at one place and you need just to add them within the styled component via:
${addMedia("componenName")}

Vue child component not receiving prop from parent

I have an Order form component which uses a OrderTasklistBuilder component. Note: there are nested components here. The hierarchy is as follows: OrderSingle > CreateOrderForm > OrderTasklistBuilder
The initial order is fetched when the OrderSingle is mounted and then passed down through child components.
The tasklist builder component is used like this:
<order-tasklist-builder v-if="form.tasks.length" :initial-tasks="form.tasks" #taskAdded="handleTaskAdded" class="mb-2" />
This works fine, but if there aren't any tasks in the listbuilder, the component will not display. This is a problem because someone might remove all of the tasks.
Now, if I remove the length check on form tasks the tasklist builder component will not display any tasks even after adding new tasks.
<order-tasklist-builder :initial-tasks="form.tasks" #taskAdded="handleTaskAdded" class="mb-2" />
The components are fairly large ( > 300 lines ) so if I can elaborate on something specific, let me know.
Solved by moving logic previously in mounted function inside of CreateOrderForm component to created function.

Conditionally set main color in Vue

I wrote a reusable app in Vue and I compiled it as a library. I set the global variable $brand-color in SCSS file which is a main color of the app (buttons, borders, font colors). I use this variable in other SCSS component files.
I've put my app to my client's website and everything is working fine. Right now I have another client who is willing to use my app. BUT... new client wants to have my app in different $brand-color than my old one. What would be the best way to approach this problem?
One thing which comes to my mind is to set store variable with value of $brand-color which depends on location.host and bind styles of all "branded" elements.
switch (location.host) {
case 'client1.com':
context.commit('setMainColor', '#ff0000');
...
case 'client2.com':
context.commit('setMainColor', '#16c100');
...
}
But this will be very painful. I would need to apply a lot of changes in all my components. Is there any better solution that style-binding all components?
By the way, I can't use CSS variables because code needs to be IE friendly.
You can have 2 files, each file definne scss variable for different customer:
customer1_variables.scss
$brand-color: green
customer2_variables.scss
$brand-color: red
And you can import it in javascript file
main.js
switch (location.host) {
case 'client1.com':
import './customer1_variables.scss'
...
case 'client2.com':
import './customer2_variables.scss'
...
}
Another solution is using vue-style-component. You can check this article for more detail
So finally I set store value depending on my client and prepared set of SCSS classess specified for each client, i.e.:
.btn-client1 {
background: red;
}
.btn-client2 {
background: blue;
}
...
and I binded classes for specific elements:
:class="`btn-${client}`"`

VueJS Component Input Sync

I want to create components which have input which two-way bind to the local scope of the component.
Without a component, I would create a new Vue instance and then set my data to what I need. Then using v-model, bind an input to that data and it can be manipulated from the input.
However, converting the same code to a component, I cannot for the life of me get any input in a component to bind to its data. I have tried props, :data.sync, data attributes but no matter what I have tried, the input within a component does nothing.
I have created a JSFiddle to illustrate this:
https://fiddle.jshell.net/f0pdmLhy/2/
What I would like to happen is the input in the component to two way bind to the err variable, just like the non component version underneath.
How would I accomplish this?
I basically want to create components that I can instansiate with ajax data and then populate the inputs. The inputs could then update the data and I can use a save method to send the data to the server. Can this even be done using components?
So there are a couple of things:
The external resource you were using was somehow faulty. I've used
jsfiddle default Vue instance and it works fine.
When you declare a component, you should not define the data as an object, but as a function returning an object. Read here: https://vuejs.org/guide/components.html#Component-Option-Caveats
A working example here: https://fiddle.jshell.net/by4csn1b/1/
Yes, with components, the reactivity can be accomplished just like with an instance.
One catch with components, is that data must be a function that returns an object.
Also, to maintain the two way binding, use v-model in your input.
Vue.component('ii', {
template: '<span>{{err}}</span><input type="text" v-model="err"><hr>',
data: function () {
return {
err: 123
}
}
})
Fiddle: https://fiddle.jshell.net/f0pdmLhy/25/