Proper way to express switch statement with Vue data bindings - javascript

I have a simple use case for a range input that changes a button's text whenever the range's value changes. I'm using Vue.js to bind data from an object that is simply storing the range value, and spitting it back out to the button (and the counter, for easier debugging).
In the following fiddle, when the range value is greater than 0, the text reads "Buy", or else it reads "Sell".
Fiddle: http://jsfiddle.net/svwa79pe/1/
What I want to do is show three button states depending on whether the range value is positive, negative, or zero. I can use Vue handlebar / mustache syntax to create a ternary expression, but I can't figure out how cover the third state. What I need is closer to a switch statement than ternary, but I can't seem to find an analog to that within Vue's documentation.
Does Vue contain a tool for this kind of logic that I don't know about?
Should I just handle this with a custom method that fires on the range change?
Am I just using the Vue template incorrectly? Is there a better way to do this with attributes or something?
HTML
<div id="app">
<span>
<button disabled="true">{{ item.delta }}</button>
</span>
<input type="range" v-model="item.delta" value="0" v-bind:max="item.stock" v-bind:min="0 - item.loot">
<span class="exchange">
<button>
{{ (item.delta > 0) ? "Buy" : "Sell" }}
</button>
</span>
</div>
JS
var stats = {
item : {
price : 10,
stock : 20,
loot : 5,
delta : 0
}
}
var app = new Vue({
el: '#app',
data: stats
});

Typically you want to remove complex logic from the template. In this case you want a value based on some other data so this is an ideal use case for a computed property.
computed:{
btnText(){
if (this.item.delta > 0) return "Buy"
if (this.item.delta < 0) return "Sell"
return "Nothing"
}
}
Here I'm just using simple if statements, but if you wanted to use a switch you certainly could. Used in your template like this:
<button>
{{ btnText }}
</button>
Here is a working example.
var stats = {
item : {
price : 10,
stock : 20,
loot : 5,
delta : 0
}
}
var app = new Vue({
el: '#app',
data: stats,
computed:{
btnText(){
if (this.item.delta > 0) return "Buy"
if (this.item.delta < 0) return "Sell"
return "Nothing"
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
<span>
<button disabled="true">{{ item.delta }}</button>
</span>
<input type="range" v-model="item.delta" value="0" v-bind:max="item.stock" v-bind:min="0 - item.loot">
<span class="exchange">
<button>
{{ btnText }}
</button>
</span>
</div>

Related

Vue.js v-for not rendering component

I have the following issue, for some reason, the v-for will not render at all. Please find the fiddle here https://jsfiddle.net/tadeyemi/k6s4gv85/ I have absolutely no idea why it isn't working. Someone care to shed some light?
<div id="app">
<h1>Finds</h1>
<div>
<input ref="option">
</div>
<button v-if #click="addFind">
New Find
</button>
<p v-for="(option,idx) in options.slice(1)">
<span #click="removeOption(idx+1)">Option{{idx+1}}: {{option}}</span>
</p>
</div>
and the JavaScript as follows:
new Vue({
el: '#app',
data: {
options: [],
count:0
},
methods: {
addFind: function () {
var msg = this.$refs.option.value;
console.log(this.options);
if( msg.trim() != "" ){
this.count++;
var i = this.count;
this.options[i]= this.$refs.option.value.trim();
}
},
removeOption:function(index){
this.options.splice(index,1);
this.count--;
}
}
});
There are some issues with your code, but the most prominent is that you break some reactivity rules explained here: https://v2.vuejs.org/v2/guide/reactivity.html#For-Arrays
Vue cannot detect the following changes to an array:
When you directly set an item with the index, e.g.
vm.items[indexOfItem] = newValue When you modify the length of the
array, e.g. vm.items.length = newLength
Basically: this.options.push(msg.trim()); would work, while this.options[i]= this.$refs.option.value.trim(); won't
I edited the fiddle a little to make it work: https://jsfiddle.net/63jyw7gz/

Conditional V-HTML rendering

Just wondering if there is something I'm missing here:
<span v-html="(shouldParseHTMLBool ? HTMLContent : '')">
{{ contentWithoutParsedHTML }}
</span>
doens't appear to work.
I would like to have this span interpreting HTML only if shouldParseHTMLBool is set to true.
Is it something possible, or should I use a v-if? In this trivial example it's not a big deal, but with my components I'll have to duplicate ~30 lines for each condition.
It's better to make if condition away from template as much as possible.
You should create a computed object instead.
[template]
<span v-html="computedContent"></span>
[script]
...
computed: {
computedContent: function () {
return this.shouldParseHTMLBool ? this.HTMLContent : ''
}
},
...
The v-html directive replaces the innerHTML of the element. In your case, the {{ contentWithoutParsedHTML }} will be replaced with the value of (shouldParseHTMLBool ? HTMLContent : '')
You can do something like
<template>
<span v-html="conditionalParse"></span>
</template>
<script>
methods: {
conditionalParse: function () {
return this.shouldParseHTMLBool ? this.HTMLContent : ''
}
</script>
try this
<span v-if="shouldParseHTMLBool" v-html="HTMLContentForIfCondition" >All</span>
<span v-else v-html="HTMLContentForElseCondition" ></span>
You can use two spans, One for v-if is true and other for v-else

Vue.js v-if inside v-for not actively listening to array change

I am trying do display an element for each array inside an array and display that element if its array contains a true boolean. The if function runs the first time but the element does not disappear when the value changes.
<li v-for="(value, index) in list">
<span> {{ value[0] }} </span>
<span v-if='value[1]'> {{ value[2] }} </span>
</li>
var List = new Vue({
el: "#List",
data: {
list: ['fizz',true,0],
},
methods: {
toggleItem: function(index) {
this.list[index][1] = !this.list[index][1];
},
}
})
I should be able to run
List.toggleItem(0)
If you are updating the array in Vue then use Vue.set(); so that Vue can track the changes and update the template
for example,
Vue.set(value, 1, false);
Note: simpley updating like this value[1] = false; this will not work
For more, https://v2.vuejs.org/v2/guide/list.html#Caveats

Angular 1.5 filter with ng-repeat not working by track by id

so I tried so many different ways to get this done. Followed so many StackOverflow and could not get this to work. All I am trying to do is to filter some list items based on the value of a boolean property. Below is the picture of my object data. The closest example I am following is this question Filtering an Angular 1.2 ng-repeat with "track by" by a boolean property. Still not working. does it have anything to do with an object literal and this type of filtering with property only works with array? I am new to javascript so not sure. Also using angular material, virtual repeat container and other material based things are not affecting the result, I can display the whole data, just the filtered by this specific property not working
loadAssets = () => {
var self = this;
self.infiniteAssets = {
numLoaded_: 0,
toLoad_: 0,
items: [],
pageNum:1,
virtualIndex:0,
getItemAtIndex: function (index) {
this.virtualIndex=index;
if (index > this.numLoaded_) {
this.fetchMoreItems_(index);
return null;
}
return this.items[index];
},
// Required.
getLength: function () {
if (this.virtualIndex > this.numLoaded_) {
return this.numLoaded_ ;
}else{
return this.numLoaded_ + 5 ;
}
},
fetchMoreItems_ : function (index) {
if (this.toLoad_ < index) {
self.loading = true;
this.toLoad_ += 20;
self.siAsset.getAssets(this.pageNum++,20)
.then(angular.bind(this, function (assets) {
//this.objLength = assets.length;
if(! assets.statusCode){
this.items = this.items.concat(assets);
this.toLoad_ = this.items.length;
this.numLoaded_ = this.toLoad_;
}
self.loading = false;
}))
}
}
};
console.log('++++++++++',self.infiniteAssets)
<md-virtual-repeat-container id="vertical-container" ng-show="$ctrl.infiniteAssets.getLength() > 0 && $ctrl.switch">
<md-list>
<md-list-item class="list-page" md-on-demand md-virtual-repeat="asset in $ctrl.infiniteAssets | filter: {disabled: true } track by asset.id" ng-click="$ctrl.loadDetail(asset)">
<span class="search-status" style="border-left-color:{{asset.statusColor}};"></span>
<p >{{asset.name}} </p>
<label hide-xs ng-if="asset.disabled" class="ng-animate-disabled">
<md-chips >
<md-chip >{{'LABELS.DISABLED' | translate}}</md-chip>
</md-chips>
</label>
<label ><i>{{asset.status || 'UNKNOWN'}}</i></label>
<md-button aria-label="Delete Asset" class="md-icon-button md-warn" layout-padding ng-click="$ctrl.deleteAsset(asset)">
<md-icon md-svg-icon="delete" class="modelTrashIcon"></md-icon>
</md-button>
<md-divider></md-divider>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
Are you certain md-virtual-repeat works with filters? AngularJS Materials virtual repeat is a custom implementation of ng-repeat, so you can't expect it to work exactly as the original. Here's from the documentation.
Virtual repeat is a limited substitute for ng-repeat that renders only
enough DOM nodes to fill the container and recycling them as the user
scrolls.
Arrays, but not objects are supported for iteration. Track by, as
alias, and (key, value) syntax are not supported.
I would move the filtering inside your controller instead and just make sure the filter is reapplied whenever the collection changes.
As per you said, "Angular 1.5 filter with ng-repeat not working by track by id"
I have created sample example using AngularJs 1.5, and used filter with track by on ng-repeat.
angular.module('controllerAsExample', [])
.controller('SettingsController1', SettingsController1);
function SettingsController1() {
this.infiniteAssets = [
{disabled :false, name:'test0',id:234 },
{disabled :true, name:'test1',id:123 },
{disabled :false, name:'test2',id:345 }
];
//console.log(this.infiniteAssets);
}
<!doctype html>
<html >
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"></script>
</head>
<body ng-app="controllerAsExample">
<div ng-controller="SettingsController1 as settings">
<p>ng-repeat with track by field example using angularjs 1.5.0:</p>
<ul>
<li ng-repeat="asset in settings.infiniteAssets | filter: {disabled: false } track by asset.id">
{{asset.name}}
</li>
</ul>
</div>
</body>
</html>

Setting initial value for computed property with vuejs

I'm trying out vuejs by following along with the laracasts series of webcasts on this. In https://laracasts.com/series/learning-vue-step-by-step/episodes/6, Jeffery Way has put the code in the http://jsfiddle.net/d4f27e5p/ .
The initial setting is shown in the screenshot, before any plan is selected. I would like to set a default value in the button of "please select " (instead of "Downgrade") The button code is:
<span v-else>
<button #click="setActivePlan">
{{ isUpgrade ? 'Upgrade' : 'Downgrade' }}
</button>
How can I do this?
How about adding a computed property for the button text that includes the additional logic? Something like this:
buttonText: function() {
if(!this.active.name)
return "Please Select";
return this.isUpgrade ? 'Upgrade' : 'Downgrade';
}
Then the button would use this:
<span v-else>
<button #click="setActivePlan">
{{ buttonText }}
</button>
</span>
Here's an updated jsfiddle.

Categories

Resources