How to access object property in angular using string interpolation? - javascript

I have this object in my component ts file that looks like this:
someData = {
someValue: null,
unit: "centimeters"
},
In my template (html file) I have to verify the value of "someValue" property. If its value is null then display the unit, else display the value of someValue and unit together. I have the below code but it doesn't seem to work. Can anyone point me in the right direction? Thanks in advance!
<div>{{ someData?.someValue === null ? someData?.someValue.unit : someData?.someValue someData?.unit }}</div >

You can use ngif to check the condition and show the data:
<div *ngIf="someData?.someValue === null">{{someData.unit}}</div>
<div *ngIf="someData?.someValue!= null">{{someData.someValue}} {{someData.unit}}</div>

Your two values at the end aren't going to render right, because it's executable javascript, not HTML when inside the double brackets.
I would suggest splitting this into two lines of HTML.
<div *ngIf="someData?.someValue; else unit_only">{{someData.someValue}} {{someData.unit}}</div>
<ng-template #unit_only>{{someData?.unit}}</ng-template>
Or you could try sticking with your original approach...
<div>{{ someData?.someValue === null ? someData?.unit : someData?.someValue + ' ' + someData?.unit }}</div>

Related

Javascript / Vue : set background-color on specific parts of string, based on matched values in array

I'm struggling with making a script which sets background-color on specific parts of a string (or input in this case, but doesn't have to be...).
The solution is made with VUE, but i assume this has to be sorted out with core javascripting.
HTML :
<div class="col-sm-7">
<input v-model.lazy="item.text" class="form-control" style="width:98%">
</div>
VUE Computed :
nluData() {
return orderby(this.$store.getters.nlujson.filter(item => {
return item.intent.toLowerCase() === this.selectedIntent
}), ['intent', 'text'], ['asc', 'asc'])
},
Screenshot of JSON structure :
Screenshot of desired result (where arrows point, the word-blocks should be background-colored :
You can use v-html and style your text as desired.
<span v-html="text"></span>
And in you component you can use the computed value as you want:
computed: {
text () {
// Code logic of your convenience
// Be careful of XSS attack.
return '<b>' + this.someText + '</b>';
}
}

How to define a temporary variable in Vue.js template

Here is my current template:
<a-droppable v-for="n in curSize" :key="n - 1" :style="{width: `${99.99 / rowLenMap[orderList[n - 1]]}%`, order: orderList[n - 1]}">
<a-draggable :class="{thin: rowLenMap[orderList[n - 1]] > 10}">
<some-inner-element>{{rowLenMap[orderList[n - 1]]}}</some-inner-element>
</a-draggable>
</a-droppable>
The problem is that i have to write rowLenMap[orderList[n - 1]] multiple times, and i'm afraid vue.js engine will also calculate it multiple times.
What i want is something like this:
<a-droppable v-for="n in curSize" :key="n - 1" v-define="rowLenMap[orderList[n - 1]] as rowLen" :style="{width: `${99.99 / rowLen}%`, order: orderList[n - 1]}">
<a-draggable :class="{thin: rowLen > 10}">
<some-inner-element>{{rowLen}}</some-inner-element>
</a-draggable>
</a-droppable>
I think it's not difficult to implement technically because it can be clumsily solved by using something like v-for="rowLen in [rowLenMap[orderList[n - 1]]]". So is there any concise and official solution?
I found a very simple (almost magical) way to achieve that,
All it does is define an inline (local) variable with the value you want to use multiple times:
<li v-for="id in users" :key="id" :set="user = getUser(id)">
<img :src="user.avatar" />
{{ user.name }}
{{ user.homepage }}
</li>
Note : set is not a special prop in Vuejs, it's just used as a placeholder for our variable definition.
Source: https://dev.to/pbastowski/comment/7fc9
CodePen: https://codepen.io/mmghv/pen/dBqGjM
Update : Based on comments from #vir us
This doesn't work with events, for example #click="showUser(user)" will not pass the correct user, rather it will always be the last evaluated user, that's because the user temp variable will get re-used and replaced on every circle of the loop.
So this solution is only perfect for template rendering because if component needs re-render, it will re-evaluate the variable again.
But if you really need to use it with events (although not advisable), you need to define an outer array to hold multiple variables at the same time :
<ul :set="tmpUsers = []">
<li v-for="(id, i) in users" :key="id" :set="tmpUsers[i] = getUser(id)" #click="showUser(tmpUsers[i])">
<img :src="tmpUsers[i].avatar" />
{{ tmpUsers[i].name }}
{{ tmpUsers[i].homepage }}
</li>
</ul>
https://codepen.io/mmghv/pen/zYvbPKv
credits : #vir us
Although it doesn't make sense here to basically duplicate the users array, this could be handy in other situations where you need to call expensive functions to get the data, but I would argue you're better off using computed property to build the array then.
Judging by your template, you're probably best off with a computed property, as suggested in the accepted answer.
However, since the question title is a bit broader (and comes up pretty high on Google for "variables in Vue templates"), I'll try to provide a more generic answer.
Especially if you don't need every item of an array transformed, a computed property can be kind of a waste. A child component may also be overkill, in particular if it's really small (which would make it 20% template, 20% logic and 60% props definition boilerplate).
A pretty straightforward approach I like to use is a small helper component (let's call it <Pass>):
const Pass = {
render() {
return this.$scopedSlots.default(this.$attrs)
}
}
Now we can write your component like this:
<Pass v-for="n in curSize" :key="n - 1" :rowLen="rowLenMap[orderList[n - 1]]" v-slot="{ rowLen }">
<a-droppable :style="{width: `${99.99 / rowLen}%`, order: orderList[n - 1]}">
<a-draggable :class="{thin: rowLen > 10}">
<some-inner-element>{{rowLen}}</some-inner-element>
</a-draggable>
</a-droppable>
</Pass>
<Pass> works by creating a scoped slot. Read more about scoped slots on the Vue.js documentation or about the approach above in the dev.to article I wrote on the topic.
Appendix: Vue 3
Vue 3 has a slightly different approach to slots. First, the <Pass> component source code needs to be adjusted like this:
const Pass = {
render() {
return this.$slots.default(this.$attrs)
}
}
Today I needed this and used <template> tag and v-for like this
I took this code and
<ul>
<li v-for="key in keys"
v-if="complexComputation(key) && complexComputation(key).isAuthorized">
{{complexComputation(key).name}}
</li>
</ul>
Changed it to this
<ul>
<template v-for="key in keys">
<li v-for="complexObject in [complexComputation(key)]"
v-if="complexObject && complexObject.isAuthorized">
{{complexObject.name}}
</li>
</template>
</ul>
And it worked and I was pleasantly surprised because I didn't know this was possible
This seems like the perfect use case of a child component. You can simply pass your complex computed value(s) as a property to the component.
https://v2.vuejs.org/v2/guide/components.html#Passing-Data-to-Child-Components-with-Props
How about this:
<div id="app">
<div
v-for="( id, index, user=getUser(id) ) in users"
:key="id"
>
{{ user.name }}, {{ user.age }} years old
<span #click="show(user)">| Click to Show {{user.name}} |</span>
</div>
</div>
CodePen: https://codepen.io/Vladimir-Miloevi/pen/xxJZKKx
<template>
<div>
<div v-for="item in itemsList" :key="item.id">
{{ item.name }}
<input v-model="item.description" type="text" />
<button type="button" #click="exampleClick(item.id, item.description)">
Click
</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{
id: 1,
name: 'Name1',
},
{
id: 2,
name: 'Name2',
},
],
}
},
computed: {
itemsList() {
return this.items.map((item) => {
return Object.assign(item, { description: '' })
})
},
},
methods: {
exampleClick(id, description) {
alert(JSON.stringify({ id, description }))
},
},
}
</script>
Just tested using vue3 and works, i think it works universally
{{ (somevariable = 'asdf', null) }}
<span v-if="somevariable=='asdf'">Yey</span>
<span v-else>Ney</span>
It outputs nothing while setting your variable.
mandatory:
opening "("
set your variable
closing ", null)"
curSize is an array. Your temporary values comprise a corresponding implied array sizedOrderList = curSize.map(n => orderList[n-1]). If you define that as a computed, your HTML becomes
<a-droppable v-for="n, index in sizedOrderList" :key="curSize[index]" :style="{width: `${99.99 / rowLenMap[n]}%`, order: n}">
<a-draggable :class="{thin: rowLenMap[n] > 10}">
<some-inner-element>{{rowLenMap[n]}}</some-inner-element>
</a-draggable>
</a-droppable>

Dynamic binding in Angular 2

Basically I am looking for shorthand version of this:
<div class="">
{{listObject.firstHeaderKey.type === 'value' ? listObject.firstHeaderKey.key: item[listObject.firstHeaderKey.key}}
</div>
My data could either be a string or an expression i.e.
<div *ngFor="let mix of mixList">
<div>
{{mix}}
</div>
</div>
mix can either be " 'text' " or "obj[text]" and accordingly the template should evaluate.
According to best practisies you should do this in your typescript file
mix: any;
this.mix === 'value' ? listObject.firstHeaderKey.key :
item[listObject.firstHeaderKey.key]
And template should be something like this:
<div>
{{mix}}
</div>

how to display name of a particular code from a javascript object in angular js and html5

I have a angular controller that has the following array which has 3 values.
controller.medicalInstitues;
I want to take the 4th object in the array with code "AUS". Then further, I want to display the name of only 2 medical institutes with the code as "SYD" and "MEL" within that choosen bject from the parent array.
something like below:
var country = select controller.medicalInstitues.countryCode = "AUS"
var institues = select controller.medicalInstitues.name from country.code="SYD" and "MEL";
now I need to bind these in the UI (Angular).
<span class="code"> {{ controller.institutes.name }}</span>
Suppose you have got your list of values in medicalInstitues, then in the angularjs controller you can do
$scope.institues = medicalInstitues.filter(function(value){
return value.countryCode == "AUS" && (value.code == "SYD" || value.code == "MEL");
});
In HTML you can use ng-repeat:
<div controller="controller">
....
<span ng-repeat="institue in institues">{{institue.name}}</span>
....
</div>
In your controller, to bind the 4th object in your array you can set:
$scope.AUS = yourArray[3]
Then in the html you can show the object properties:
{{ AUS."object path to name" }}
Well, if I understood your question well, you can do the following:
// If you have an array that you already know that will come 4 items with the same country code, you can simply do:
$scope.medicalInstitues = array[3];
// Otherwise:
$scope.medicalInstitues = array.filter(function (value) {
return value.countryCode == "AUS";
})[3];
// To get all 'institues' with the countryCode equals to 'SYD' or 'MEL':
$scope.institues = array.filter(function (value) {
return value.countryCode == "SYD" || value.countryCode == "MEL";
});

VueJs: Working with v-class

I am trying to assign a css class to a span element, whenver it push the EDIT button.
This is my working example in jsfiddle:
http://jsfiddle.net/r3nepL7u/
BUT it only works, because I check if the title property of the edited object is equal to the title property of the todo object, instead I'd rather check if the two objects are equal.
Unfortunately this breaks my code, whenever I have the same property (e.g. titles) but different objects.
<td>
<span v-class="
completed: todo.completed,
editing: editedTodo.title == todo.title">
{{ todo.title }}
</span>
</td>
Instead I would like to do something like this, where I check todo == editedTodo
<span v-class="
completed: todo.completed,
editing: editedTodo == todo">
{{ todo.title }}
</span>
Non Working Jsfiddle:
http://jsfiddle.net/r3nepL7u/1/
How do I check if todo is equal with editedTodo. AND is there a better way, to use the v-class directive, instead of using inline expressions, meaning for more complicated calculations?
It seems to work fine in the todomvc example here:
Line 23: https://github.com/yyx990803/vue/blob/dev/examples/todomvc/index.html
Conditional class names in Vue.js
Here is how you can do it based on the documentation
1st way
as boolean variable
You define a boolean variable in your js file and based on that it will set the class
js file
data: {
isActive: true,
hasError: false
}
html file
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
as object
you can also define an object with class names
js file
data: {
classObject: {
active: true,
'text-danger': false
}
}
html file
<div v-bind:class="classObject"></div>
2nd way
You define the "name of the class" variable in your js file and based on the nameOfTheClass it will set the class
js file
data: {
nameOfTheClass: 'this-is-the-name-of-the-class'
}
html file
<div v-bind:class="nameOfTheClass"></div>
3rd way
You can set the name of the class in js and then evaluate with if statement in html file
js file
data: {
nameOfTheClass: 'this-is-the-name-of-class'
}
html file
<div v-bind:class="{ active: nameOfTheClass === 'this-is-the-name-of-class'}">
Add a method to your View Model that does a deep comparison. For instance, create a method called todoIsEqual and then have it use LoDash to do the comparison:
[...]
methods: {
todoIsEqual: function (todo_a, todo_b) {
return _.isEqual(todo_a, todo_b);
}
[...]
and use it like this:
<span v-class="
completed: todo.completed,
editing: todoIsEqual(editedTodo, todo)">
{{ todo.title }}
</span>
Actually the reason it didn't work was pretty simple:
I falsely assinged just two properties and made an if statement to to see if the two objects are equal. I did this:
editTask: function (that) {
this.editedTodo = {
body: that.todo.body,
completed: that.todo.completed
};
},
Instead of asigning the actual object to the editedTodo, like this:
editTask: function (that) {
this.editedTodo = that.todo;
},
Problem solved.

Categories