How to switch applying a property binding using ternary operator in Expression Binding? - data-binding

See, I have a table where duplicate entries are highlighted using the highlight property of sap.m.Table. Now im trying to implement a toggle button that lets the user decide whether he wants the duplicates highlighted or not.
In my controller, I created the toggle button function which toggles the model property "compare" of the client-side model "compareModel" which is bound to my table.
My default model is the model for the table items. The bound "dupe" property either contains "Success" or "Error".
This works:
<ColumnListItem highlight="{dupe}">
<Text text="{myItemText}" />
<!-- ... -->
</ColumnListItem>
Now for my problem:
I want to set the highlight property based on whether the toggle button is pressed or not. So far, my expression binding attempts looked something like this:
<ColumnListItem highlight="{= ${compareModel>/compare} ? ${dupe} : false }">
I tried putting quotation marks here and there but so far no luck.
Hoping somebody can help me out!

Try with highlight="{= ${compareModel>/compare} ? ${dupe} : undefined }.
Working sample:
globalThis.onUI5Init = () => sap.ui.require([
"sap/ui/core/mvc/XMLView",
"sap/ui/model/json/JSONModel", // sample model. Applies also to ODataModel
], async function (XMLView, JSONModel) {
"use strict";
const control = await XMLView.create({
definition: `<mvc:View xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
displayBlock="true"
height="100%"
>
<App>
<Page title="Toggle Highlight">
<headerContent>
<Switch
state="{compareModel>/compare}"
customTextOn=" "
customTextOff=" "
/>
</headerContent>
<List items="{/items}">
<StandardListItem
title="{myItemText}"
highlight="{= %{compareModel>/compare} ? %{dupe} : undefined }"
/>
</List>
</Page>
</App>
</mvc:View>`,
models: {
"compareModel": new JSONModel({ "compare": true }),
undefined: new JSONModel({
"items": [
{
"myItemKey": 1,
"myItemText": "A",
"dupe": "Error"
},
{
"myItemKey": 2,
"myItemText": "B",
"dupe": "Success"
},
{
"myItemKey": 3,
"myItemText": "A",
"dupe": "Error"
}
]
}),
},
});
control.placeAt("content");
});
<script id="sap-ui-bootstrap" src="https://sdk.openui5.org/resources/sap-ui-core.js"
data-sap-ui-libs="sap.ui.core,sap.m,sap.ui.layout,sap.ui.unified"
data-sap-ui-async="true"
data-sap-ui-oninit="onUI5Init"
data-sap-ui-theme="sap_horizon"
data-sap-ui-compatversion="edge"
data-sap-ui-excludejquerycompat="true"
data-sap-ui-xx-waitfortheme="init"
></script>
<body id="content" class="sapUiBody"></body>
The issue with highlight="{= ... ? ... : false }" is that false is not a valid value from the enum sap.ui.core.MessageType/.IndicationColor for the list item's highlight property. You should see a console error reporting a similar issue.
With undefined, however, the default value of highlight will be applied which is "None" for sap.m.ListBase controls.

Related

Vue-Select: How to save all entries as lower case items?

I am using the vue-select library. How can I force input entries to make all characters lower case? Right now, when I type the word "Baseball" into the input field, the tag is saved as "Baseball". I would like all tags to only keep a lower case version of the entry such as "baseball".
I have a sandbox here: https://stackblitz.com/edit/vue-ranqmt?file=src/App.vue
<template>
<div id="app">
<h3>Vue Select</h3>
<v-select
v-model="selected"
taggable
multiple
:options="options"
#input="setSelected"
></v-select>
<br /><br />
selected: <br />
<pre>{{ selected }}</pre>
</div>
</template>
<script>
import vSelect from 'vue-select';
import 'vue-select/dist/vue-select.css';
export default {
name: 'App',
components: {
'v-select': vSelect,
},
data() {
return {
selected: null,
options: [],
};
},
methods: {
setSelected(value) {
console.log(value);
},
},
};
</script>
a - You could add a computed property which returns the lowercased version to use in whichever part of your app you need it to be.
computed: {
selectedLowerCase() {
return this.selected.toLowerCase();
}
}
b - if you are using this for something like an API call, then you can turn the variable(s) into lowercase before submitting.
c - if you want the variable to appear as lowercase even in the input field you need to add the #input action to your input field and point it to a function to lowercase your input
methods: {
setSelected(value) {
this.selected = this.selected.toLowerCase();
console.log(value);
},
},

How to pass props to slot, Vue 3

Making a custom select box component. But having some trouble when I try to pass to slot.
<vb-select v-model="container"
title="bla bla"
multiple>
<vb-option v-for="(item, idx) in items" :key="idx" :value="item">{{item}}</vb-option>
</vb-select>
vb-option is slot and I am calling it in vb-select component.
vb-select
<ul v-if="state" class="vb-options">
<slot :state="state" :multiple="multiple"></slot>
</ul>
When I try to pass multiple to slot as a prop. I can't listen/watch it in the vb-option
vb-option
<li class="vb-option">
{{multiple}}
</li>
props:{
multiple:Boolean,
},
What is right way to achieve this? Watching props inside the slot for changes.
<slot :state="state" :multiple="multiple"></slot>
What you've done here is use scoped slots - but they don't quite work the way you're trying to use them.
Try this way:
<vb-select>
<template #default="scope"> <!-- you can also do #default="{ state, multiple }" -->
<vb-option :multiple="scope.multiple" />
</template>
</vb-select>
Slot scope isn't automatically applied to the props of the component you put inside the slot - you need to pass it explicitly as shown above.
So that's the "how slot scope works" part, but the second part is that you're trying to implement something called "compound components", and it's a little trickier than that in Vue. In React, to do this you'd use React Context, but in Vue you'd use provide/inject.
Here's a repo where I outline some usecases, including an select/option:
https://github.com/sethidden/vue-compound-components
If that's too crazy, there's really nothing wrong with doing something like this and handling option rendering in vb-select itself:
<vb-select :options="[{ value: '123', label: 'Hello' }, { value: '567', label: 'Helloooo' }]" />
Pass hasError to default slot.
<script setup>
const slots = useSlots():
const Child = defineComponent({
render: () => {
const data = slots.default?.()[0];
data!.props!.hasError = true;
return data;
},
});
</script>
<template>
<Child />
</template>

Gutenberg ToggleControl won't visually toggle

I had a ToggleControl on a block that appears in the InspectorControls area. It is by default on. When I try to set it using the type of boolean it would not save the state properly. When trying to toggle it, it would remain as "on" but the help text would change as if it were off. When saving changes and reloading, the toggle would be back to on.
const defaults: {
autoplay: {
type: 'boolean',
meta: 'autoplay',
default: true
}
}
....
<ToggleControl
label={__('Autoplay')}
help={attributes.autoplay ? __('Slideshow will start playing automatically', 'five') : __('User will have to cycle slideshow manually', 'five')}
checked={(attributes.autoplay || defaults.autoplay.default)}
onChange={() => {
setAttributes({
autoplay: !attributes.autoplay
});
}}
/>
However, if I change that input to be a string and do the boolean handling myself it toggles the input correctly and saves the state:
const defaults: {
autoplay: {
type: 'boolean',
meta: 'autoplay',
default: true
}
}
....
<ToggleControl
label={__('Autoplay')}
help={attributes.autoplay == 'true' ? __('Slideshow will start playing automatically', 'five') : __('User will have to cycle slideshow manually', 'five')}
checked={(attributes.autoplay || defaults.autoplay.default) == 'true'}
onChange={(nextValue) => {
setAttributes({
autoplay: nextValue ? 'true' : 'false'
});
}}
/>
This works, but requires extra characters when checking for truthy when rendering. I could probably make this a little easier by just using a number and using 0 and 1, but that's not the point.
Am I missing something with the boolean type, or is this a bug in Gutenberg?
WP Version: 5.1.1
Your condition inside checked attribute is wrong because it is always evaluating to true. While your help text don't have any of that condition, toggling of help text depends on attributes.autoplay which you are changing inside onChange attribute.
So the solution is to change your condition on checked attribute.
Attributes inside registerBlockType can define default values by using default key inside any attribute so the recommended way is to use that. (default attribute example from wordpress cover block)

Vue.js: How to change a class when a user has clicked on a link?

I am trying to add a class to an element depending on whether the user has clicked on a link. There is a similar question here but it is not working as I wanted it to be.
I created a component which has its own internal data object which has the property, isShownNavigation: false. So when a user clicks on the a I change isShownNavigation: true and expect my css class isClicked to be added. Alas that is not happening - isShownNavigation stays false in the component when I displayed it {{isShownNavigation}} but I can see in the console that my method is working when clicked.
I imported my header component to the App. Code is below.
Header Component
<template>
<header class="header">
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="showNavigation">
Click
</a>
</header>
</template>
<script>
export default {
name: 'header-component',
methods: {
showNavigation: () => {
this.isShowNavigation = !this.isShowNavigation
}
},
data: () => {
return {
isShowNavigation: false
}
}
}
</script>
Application
<template>
<div id="app">
<header-component></header-component>
</div>
</template>
<script>
import HeaderComponent from './components/Header.vue'
export default {
name: 'app',
components: {
'header-component': HeaderComponent
}
}
</script>
I am using the pwa template from https://github.com/vuejs-templates/pwa.
Thanks.
Don't use fat arrow functions to define your methods, data, computed, etc. When you do, this will not be bound to the Vue. Try
export default {
name: 'header-component',
methods: {
showNavigation(){
this.isShowNavigation = !this.isShowNavigation
}
},
data(){
return {
isShowNavigation: false
}
}
}
See VueJS: why is “this” undefined? In this case, you could also really just get rid of the showNavigation method and set that value directly in your template if you wanted to.
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="isShowNavigation = true">
Click
</a>
Finally, if/when you end up with more than one link in your header, you will want to have a clicked property associated with each link, or an active link property instead of one global clicked property.

Event handling dynamic created buttons in Vue.js v-for loop

I have a v-for loop where a button is created for each iteration. I'm trying to make a toggle handler where clicking the button will toggle the color of the button. But since the buttons are dynamically created, all of their colors are changing ....
<div class="pets" v-for="pet in pets" :key="pet.id">
<button class="favorite" v-on:click="toggle">
<i v-bind:class="[{ 'red' : favorited }, 'material-icons']">favorite</i>
</button>
</div>
The pets array is being filled with a http call. My script looks like this:
<script>
export default {
name: 'home',
data() {
return {
pets: [],
favorited: false
}
},
methods: {
toggle: function() {
this.favorited = !this.favorited;
}
},
}
The Style tag just changes the color
<style scoped>
.red {
color: red;
}
Essentially, I'm trying to created a favorite button where you can toggle favoriting an object from the array pets. I know why my method would activate all my buttons. Since favorited is not unique to a button and coming from the data. So when favorited = true, the class 'red' is bound to all my buttons. My question is how do I bind the class 'red' to just the button I click on? I'm relatively new to Vue and this is driving me nuts lol! Someone please tell me how I can fix this.
Add a favorited property to your pet objects in the pets array. Then use that property.
<div class="pets" v-for="pet in pets" :key="pet.id">
<button class="favorite" v-on:click="pet.favorited = !pet.favorited">
<i v-bind:class="[{ 'red' : pet.favorited }, 'material-icons']">favorite</i>
</button>
</div>
Example.
If you didn't want to modify the pet object, then here is an alternate way.

Resources