In Ember can I pass markup to a component? - javascript

I'm trying to create a dropdown type component that can have some markup for the title, and then upon hovering reveal more markup. Something like this:
{{#dropdown-menu}}
{{#dropdown-header}}
<span>My Custom Title markup</span>
{{/dropdown-header}}
{{#dropdown-body}}
list of menu items
{{/dropdown-body}}
{{/dropdown-menu}}
The body should only show while some property like isExpanded is true. But if the body is clicked, isExpanded would become false.
I can make a working component that accepts a title property (string), but I can't figure out how to make the title include custom markup.

You can put the yield in your component in an if block. see this jsbin
component:
App.TestShowComponent = Ember.Component.extend({
layoutName: "components/test-show",
expanded: false,
actions: {
toggle: function () {
this.set('expanded', !this.get('expanded'));
}
}
});
index template:
{{#test-show}}
inner stuff
{{/test-show}}
component template:
<button {{action 'toggle'}}>toggle</button>
{{#if expanded}}
{{yield}}
{{/if}}

Related

How can I modify a dropdown item in bootstrap-vue to open a modal?

How can i modify the below drop down to include a modal in bootstrap vue:
I want some of my dropdown items to open a modal, and some to go to be href. I am ok with the href. But how do i go about making some of these drop down items open a modal?
<template>
<b-dropdown-item v-bind="$props" :href="to ? undefined : href || '#'">
<feather-icon size="1x" :icon="icon"/>
{{name}}
</b-dropdown-item>
</template>
<script>
import FeatherIcon from '#/components/FeatherIcon';
export default {
name: 'feather-dd-item',
props: ['icon', 'name', 'href', 'to'],
components: { FeatherIcon }
};
</script>
It looks like you're spreading your component's $props onto a <b-dropdown-item>.
You can't really add v-b-modal to your components' props because that would mean setting v-b-modal on the wrapper, which would make the wrapper open the modal, instead of the dropdown item. So we need a substitute prop to transport the id of the modal. I suggest modalTarget.
Inside the markup, just map modalTarget (containing the modal id) to v-b-modal directive:
<b-dropdown-item v-b-modal='modalTarget' />.
Obviously, you have to add modalTarget to the props array. Final markup should look like this:
<template>
<b-dropdown-item v-b-modal="modalTarget"
v-bind="$props"
:href="to ? undefined : href || '#'">
<feather-icon size="1x" :icon="icon"/>
{{name}}
</b-dropdown-item>
</template>
<script>
import FeatherIcon from '#/components/FeatherIcon';
export default {
name: 'feather-dd-item',
props: ['icon', 'name', 'href', 'to', 'modalTarget'],
components: { FeatherIcon }
};
</script>
Should work. Now you can use
<feather-dd-item modalTarget="my-modal" />
, which will open the modal with id="my-modal", if it finds one.

vue.js and slot in attribute

I'm trying to build a vue.js template that implements following:
<MyComponent></MyComponent> generates <div class="a"></div>
<MyComponent>b</MyComponent> generates <div class="a" data-text="b"></div>.
Is such a thing possible?
EDIT
Here is the best I can reach:
props: {
text: {
type: [Boolean, String],
default: false
}
},
and template
<template>
<div :class="classes()" :data-text="text">
<slot v-bind:text="text"></slot>
</div>
</template>
but the binding does not work, text always contains false.
You can use the mounted() method to get text using $slot.default property of the component to get the enclosing text. Create a text field in data and update inside mounted() method like this :
Vue.component('mycomponent', {
data: () => ({
text: ""
}),
template: '<div class="a" :data-text=text></div>',
mounted(){
let slot = this.$slots.default[0];
this.text=slot.text;
}
});
Note: It will only work for text, not for Html tags or components.
You're mixing slots and properties here. You'll have to pass whatever you want to end up as your data-text attribute as a prop to your component.
<MyComponent text="'b'"></MyComponent>
And in your template you can remove the slot
<template>
<div :class="classes()" :data-text="text"></div>
</template>
Another thing: it looks like your binding your classes via a method. This could be done via computed properties, take a look if you're not familiar.
You can try this.
<template>
<div :class="classes()">
<slot name="body" v-bind:text="text" v-if="hasDefaultSlot">
</slot>
</div>
</template>
computed: {
hasDefaultSlot() {
console.log(this)
return this.$scopedSlots.hasOwnProperty("body");
},
}
Calling
<MyComponent>
<template v-slot:body="props">
b
</template>
</MyComponent>

VueJS Show element on child component one at a time

So I have several components <server-header>. It has the following HTML:
<span #click="$parent.changeOrder(column, $event)">
<slot></slot>
<i v-show="sortActive" class="order-arrow" :class="sort"></i>
</span>
These components are inserted in another component <server-list>. These will be the headers, that when clicked, will order some lists. My objective is to only show one arrow icon at a time.
E.g.: If I click on the first header, the arrow appears on that one. If I click on the second header, the arrow from the first header hides and the one on the second header appears.
This would be simple to do with jQuery for example, but I'm kind of lost on how to do this with VueJS.
Don't call parent functions directly. That is an anti pattern. Instead use 2-way data binding with sync and this is much easier. Rough example below:
// server-list.vue
data() {
return {
selected: undefined
}
}
<server-header v-for="(header, idx) in headers" :header="header" :selected.sync="selected" :idx="idx"></server-header
Then in the child, we drop #click="$parent.changeOrder(column, $event)" in favor of:
#click="$emit('update:selected', idx)"
Make sure server-header has this prop expected:
props: ['idx', 'header', 'selected']
Then make sure we compare the idx to our header index:
<i v-show="selected === index"
I assume you're rendering the <server-header> components with a v-for directive, looping a server_headers property in your <server-list> component data property.
The way i go with this is adding a prop to the <server-header> component like selected, then I add a selected property to each of the server_headers objects. Then i render the icon (<i>) with v-show if the correspondent selected property is true.
Like this:
server-list component
Vue.component('server-list', {
data: () => ({
server_headers: [{key: value, selected: true}, {key:value, selected:false}]
}),
template: `
<div>
<server-header v-for="header of server_headers" :v-key="somekey" :selected="header.selected"></server_header>
</div>
`
}
Vue.component('server-header', {
props: ['selected'],
template: `
<span #click="$parent.changeOrder(column, $event)">
<slot></slot>
<i v-show="selected" class="order-arrow" :class="sort"></i>
</span>
`
})
Then, if i click on the arrow I would unset all the selected properties, and set the one i clicked. I hope this helps!!!!

How can I add Multi class binding in Vue2

I faced one problem when I put multi-class binding in Vue2.
<div class="list-item clearfix" v-on:click="selectItem(trail)" :class="popupMode ? 'popup' : ''">
Here popupMode is props and I'd like to add one more class binding when I select the Item(click function:selectItem()).
For example, similar to selected.
This class is defined. How can I manage this problem?
Update HTML class bind like:
v-bind:class="{ popup: popupMode, selected: isSelected }"
where isSelected is a new prop like popupMode. On element click when selectItem method is called, set the bool isSelected prop to true and you will be able to see selected class after that.
JS:
data: {
popupMode: true,
isSelected: false
},
methods: {
selectItem() {
this.isSelected= true;
}
}
For more info check this: Binding HTML Classes
You can try this method:
{ active: isActive, 'text-danger': hasError }
Don't forget place isActive and hasError in data (in this case).
I hope this help's.

Parent onChange sets child property to true

Form (parent) component:
export default {
template: `
<form name="form" class="c form" v-on:change="onChange">
<slot></slot>
</form>`,
methods: {
onChange:function(){
console.log("something changed");
}
}
};
and a c-tool-bar component (child) that is (if existing) inside the c-form's slot
export default {
template: `
<div class="c tool bar m006">
<div v-if="changed">
{{ changedHint }}
</div>
<!-- some other irrelevant stuff -->
</div>`,
props: {
changed: {
type: Boolean,
default: false,
},
changedHint: String
}
};
What I want to achieve is, that when onChange of c-form gets fired the function
checks if the child c-tool-bar is present && it has a changedHint text declared (from backend) it should change the c-tool-bar's prop "changed" to true and the c-bar-tool updates itself so the v-if="changed" actually displays the changedHint. I read the vue.js docs but I don't really know where to start and what the right approach would be.
You can use the two components like this:
<parent-component></parent-component>
You will be passing in the :changed prop a variable defined in the parent component in data(). Note the :, we are using dynamic props, so once the prop value is changed in the parent it will update the child as well.
To change the display of the hint, change the value of the variable being passed in as prop to the child component:
export default {
template: `
<form name="form" class="c form" v-on:change="onChange">
<child-component :changed="shouldDisplayHint"></child-component>
</form>`,
data() {
return {
shouldDisplayHint: false,
};
},
methods: {
onChange:function(){
this.shouldDisplayHint = true; // change the value accordingly
console.log("something changed");
}
}
};
One simple way is to use $refs.
When you will create your child component inside your parent component, you should have something like: <c-tool-bar ref="child"> and then you can use it in your parent component code:
methods: {
onChange:function(){
this.$refs.child.(do whatever you want)
}
}
Hope it helps but in case of any more questions: https://v2.vuejs.org/v2/guide/components.html#Child-Component-Refs

Categories

Resources