I'm trying to dip my toes into the Aurelia framework. I'm trying to create a reusable pager component.
Is there a way to provide some default content, but allow it to be overridden if the user of the component so desires?
For instance, my pager.html looks like this:
<template>
<div class="pager-container">
<content select="pager-beginning"></content>
</div>
</template>
my pager-beginning.html looks like this:
<template>
<content>
<button type="button">|<</button>
</content>
</template>
I was thinking I should be able to do something like this:
<template>
<require from="components/pager/pager"></require>
<pager></pager>
</template>
and have the markup produced look like this:
<div class="pager-container">
<button type="button">|<</button>
</div>
or alternatively I should be able to do something like this:
<template>
<require from="components/pager/pager"></require>
<pager>
<pager-beginning><button type="button"><i class="glyphicon glyphicon-step-backward"></i></button></pager-beginning>
</pager>
</template>
and have the markup produced look like this:
<div class="pager-container">
<button type="button"><i class="glyphicon glyphicon-step-backward"></i></button>
</div>
The idea being that I could provide all of the functionality of a pager, all of the logic specific to a pager in my pager.js file and a default html rendering, but then allow the user of the component to override pieces of the html if they so desired.
What currently seems to be happening is that the markup inside of the pager-beginning.html <content></content> tag is always getting replaced. So I get markup rendered that looks like this:
<div class="pager-container"></div>
I can't figure out how to provide it with that 'default' content functionality.
Use the "template parts" feature. More info here (search for "template parts").
pager.html
<template>
<div class="pager-container">
<button type="button" click.delegate="gotoBeginning()">
<template replaceable part="goto-beginning-button-content">|<</template>
</button>
</div>
</template>
app.html
<template>
<require from="./components/pager/pager"></require>
<pager>
<template replace-part="goto-beginning-button-content">
<i class="glyphicon glyphicon-step-backward"></i>
</template>
</pager>
</template>
Related
I am trying to create a dummy vue page, were the different components used in the page like b-field ,b-table etc should be injected into the page in order to make things more dynamic. Currently the fields and components are defined in the template section of the .vue page.
<template>
<div class="container is-fluid">
<b-loading :is-full-page="true" :active.sync="this.isLoading"></b-loading>
<p class="subtitle">Business Unit</p>
<b-field label="Business Unit">
<b-input
required
:disabled="this.newRecord ? false : true"
:value="this.objectData.id"
#input="(newValue)=>{updateValue(newValue,'id')}"
></b-input>
</b-field>
<b-field label="Description">
<b-input
:value="this.objectData.description"
#input="(newValue)=>{updateValue(newValue,'description')}"
></b-input>
</b-field>
<b-field label="Short Description">
<b-input
:value="this.objectData.shortdescription"
#input="(newValue)=>{updateValue(newValue,'shortdescription')}"
></b-input>
</b-field>
<b-field label="Status">
<b-autocomplete
:value="this.objectData.status"
:open-on-focus="true"
:keep-first="true"
:data="['Active','InActive']"
#input="(newValue)=>{updateValue(newValue,'status')}"
></b-autocomplete>
</b-field>
<section>
<p class="is-size-7 has-text-danger">{{submitError}}</p>
<b-button #click="submitForm" class="is-radiusless">Submit</b-button>
<b-button type="is-text" #click="$router.go(-1)" class="is-radiusless">Return</b-button>
</section>
</div>
</template>
<script>
import { viewMixin } from "../viewMixin.js";
const ViewName = "BusinessUnitDetail";
export default {
name: "BusinessUnitDetail",
mixins: [viewMixin(ViewName)],
};
</script>
But the components mentioned in the template section should actually be stored as a string and going forward in the future this string will be retrieved from the database instead. But for the time being and as a starting point , this string can be hardcoded in the script section itself. Now i need a solution or guidance on how to achieve this and make the page to actually work..
Note: Please note that in vue js, i know that we could show or hide components based on the application state using Vue conditional structures such as v-if and v-else. But this is not what iam talking about. Instead i want the components (b-field,b-table etc) to be dynamically injected into the DOM. So in future if there will be an extra b-field or any other component, i can simply append the component tag to the string and that new component will be rendered in the frontend successfully.Plz help?
I guess the solution you look for is vue dynamic components.
So you can do this:
<template v-for="(item, index) of components" :key="index">
<component :is="item.name" :any-prop="item.anyProp">
</template>
...
<script>
// ...
data() {
return {
components: [
{ name: 'b-field', anyProp: 'status' }
]
}
}
</script>
In my project, I came across the following code:
Parent component - <ParameterModal>:
<template>
<modal-wrapper props="...">
<!-- ... other templates similar to this... -->
<template v-else-if="modalTemplate === 'bitmask_set'">
<template slot="header">
<h4 class="center-text">{{title}}</h4>
</template>
<div v-if="errorMessage" class="error-message">
{{errorMessage}}
</div>
<ModalBitmaskSet
v-bind="modalMeta"
:setErrorMessage="setErrorMessage"
:clearErrorMessage="clearErrorMessage"
/>
</template>
<!-- ... -->
<div v-else>
Warning: unmapped modal template!
</div>
<!-- ... -->
</modal-wrapper>
</template>
Ok, cool, this is using a regular slot and named slot to display a component called <ModalBitmaskSet>. So I look inside modal-wrapper to find the outlets...
Child component - <modal-wrapper>
<template>
<!-- some container and wrapper elements and then... -->
<div class="modal-header">
<slot name="header" />
</div>
<div
:class="['modal-body', 'display-flex', 'flex-direction-column', modalTemplate]"
>
<div
id="escape_message"
style="text-align: center; display: none; padding-bottom: 10px;"
>
You have unsaved changes.<br />Please click Save or Cancel to proceed.
</div>
<md-content v-if="modalContent">{{modalContent}}</md-content>
<slot />
</div>
<!-- end containers and wrappers -->
</template>
Also cool, there is where the slots are coming out... but how are props being passed to <ModalBitmaskSet>? When I look in Vue DevTools, I can see that props are somehow being passed to this component that don't exist in the parent. On top of this, when I add new components to <ParameterModal>, they sometimes don't get passed props that other components seem to be getting! This is very weird!
As you can see from the photo, this component is somehow getting passed props that aren't listed in the code! Specifically, the props colIndex, fieldSet, indexOffset, methodIndex and rowIndex in this case, although other components on this <ParameterModal> component appear to get different props.
Am I missing something? Where could these ghostly props be coming from?
This line seems the likely cause, though without seeing the code for modalMeta it's difficult to be sure:
v-bind="modalMeta"
This is using the object v-bind syntax, so whatever properties exist in the modalMeta object will be passed as props to the component.
See:
https://v2.vuejs.org/v2/guide/components-props.html#Passing-the-Properties-of-an-Object
https://v2.vuejs.org/v2/api/#v-bind
When using Named Slots with Vue (utilizing the older, more verbose API for component slots), if I have a reusable component defined with a template like this:
<template>
<div v-for="field in formFields">
<slot name="`${field.key}_PREPEND`">
<span hidden></span>
</slot>
<slot name="`${field.key}_FIELD`">
<slot name="`${field.key}_LABEL`">{{ field.label }}</slot>
<slot name="`${field.key}_CONTROL`">
<input v-if="field.type === 'text'" v-model="model[field.key]"></input>
<input type="checkbox" v-else-if="field.type === 'checkbox'" v-model="model[field.key]"></input>
</slot>
</slot>
<slot name="`${field.key}_APPEND`">
<span hidden></span>
</slot>
</div>
</template>
(this is essentially a hollowed out version of an auto-form generating component I have)
I can then reuse this component like so:
<auto-form
:fields="someArray"
:model="someObject"
>
<template slot="Name_PREPEND"> This goes before the name field </template>
<template slot="Name_FIELD"> For some reason this isn't being rendered, the default slot markup is</template>
<template slot="Name_APPEND"> This goes after the name field </template>
</auto-form>
For some reason, using the above markup (<auto-form>), the slot "${field.key}_FIELD" is ignored.
If I change the inner markup of the _PREPEND field like so
<slot name="`${field.key}_PREPEND`">
<span hidden>
<slot name="`${field.key}_CONTENT`"></slot>
</span>
</slot>
I similarly cannot override the _PREPEND slot (but can override _CONTENT)
Is this simply a limitation of Vue component slots? i.e. Are nested component slots not allowed?
In this particular case, the limitation would prevent a developer using this AutoForm component from say, overriding both the control and label at once via the _FIELD slot (for my uses I wanted to add logic that made a particular field conditional based on the value of other fields in the form)
In case anyone else runs into this issue, it's a bit of a sneaky one.
It looks like if you do conditional rendering on markup that is supposed to override a slot, the default slot will render in its place when it is not conditionally rendered.
So, the simple solution is to use v-show instead of v-if when you try to override the component slot.
(Has nothing to do with nested component slots as originally suspected)
I'm trying to create a table with a complex header (multi-line header) like this:
using the angular-2-data-table library found here:
https://github.com/swimlane/angular2-data-table
They have something called "Expressive cell templates" in which you can define columns like this:
<datatable
class="material"
[rows]="rows"
[options]="options">
<datatable-column name="Name">
<template let-column="column">
Hi + {{column.name}}
</template>
<template let-value="value">
Hi: <strong>{{value}}</strong>
</template>
</datatable-column>
<datatable-column name="Gender">
<template let-row="row" let-value="value">
My name is: <i [innerHTML]="row['name']"></i> and <i>{{value}}</i>
<div>{{joke}}</div>
</template>
</datatable-column>
</datatable>
plnkr here
But it seems like the column name is always defined as text, and it's unclear how you would have nested column names. Any ideas if complex headers are possible in angular2-data-table? It's possible in regular datatables (https://datatables.net/examples/advanced_init/complex_header.html).
Thanks in advance!
Say I have a common piece of markup that will be repeated throughout my website with only a few small differences.
For example, I have a bunch of <div> elements that all have a title.
Elements with class: section-title are all styled the same way, but their text content is different.
I would like to make a template to represent a title, sort of like this:
<template name="section-title">
<div class="section-title">
<span> Profile </span>
</div>
</div>
Except, I need "Profile" to be interchangeable, because I have many different sections: "profile", "contact information", etc.
What is the best way to do this in Blaze?
You can use either template arguments or set the template current data context to the appropriate list of arguments.
HTML
<template name="sectionTitle">
<section class="section-title">
<span>{{title}}</span>
</section>
</template>
{{> sectionTitle title="Profile"}}
<template name="parent">
{{> sectionTitle args}}
</template>
JS
Template.parent.helpers({
args: function(){
return {
title: "Profile"
};
}
});