How to convert string to expression ( value ) in aurelia repeat for? - javascript

Array used in repeat for loop
let loopArr = ["item.name + ' /'+ item.DisplayName? item.DisplayName: item.otherDisplayName",
"item.description + ' /'+ item.anotherDescription"]
Template
<div repeat.for = item of data">
<div repeat.for = "row of loopArr">
<span textcontent.bind="renderRow(row, item)></span>
</div>
</div>
Component method
renderRow(row, item){
return eval(row)
}
Actually I wanted to display like below in template
<div repeat.for = item of data">
<div repeat.for = "row of loopArr">
<span>${item.name + ' /'+ item.DisplayName? item.DisplayName: item.otherDisplayName} </span>
<span>${item.description + ' /'+ item.anotherDescription} </span>
</div>
</div>
Since I wanted to loop through dynamic loopArr, instead of using eval to convert from string to value, is there any better way to compute the value from string? Also, eval doesnt work for multiline statements, is there any other approach/way to handle the above problem?
How to convert string to value and display in aurelia template?
Any help would be appreciated!

I'm not sure why you're adding the logic in string format and using eval. You could directly add it to the template and display it:
<div repeat.for="item of data">
<span>${item.name + '/' + (item.DisplayName ? item.DisplayName: item.otherDisplayName)}</span>
<span>${item.description + ' / '+ item.anotherDescription} </span>
</div>
Let's assume you have a list of custom string formats and you are importing them from another file. You could create an array of functions instead of array of strings. This is much better way of deferring the string creation than running eval
displayTemplates = [
item => item.name + '/' + (item.DisplayName ? item.DisplayName: item.otherDisplayName),
item => item.description + '/'+ item.anotherDescription
]
and then in the template:
<div repeat.for="item of data">
<template repeat.for="func of displayTemplates">
<span>${ func(item) }</span> <!-- call each func on item object -->
</template>
</div>
Also, there is a logical error in your string format. + operator has higher precedence compared to the ternary operator.
So,
item.name + '/' + item.DisplayName ? item.DisplayName : item.otherDisplayName
is actually evaluated as
(item.name + '/' + item.DisplayName) ? item.DisplayName : item.otherDisplayName
So, this expression will always evaluate to item.DisplayName because item.name + '/' + item.DisplayName will never be falsy.
You need to add () around the ternary operation:
item.name + '/' + (item.DisplayName ? item.DisplayName: item.otherDisplayName)
// OR
item.name + '/' + (item.DisplayName ?? item.otherDisplayName)

Related

Use if else at Javascript

I have a section on the site where used items parsing from Steam API. Every item has an attribute market_hash_name, classid, and i have virtual items on the site that don’t have these attributes, that have items from Steam.
The problem is this: items from the Steam service are displayed normally, and virtual items are not displayed, an error appears in the site console VM13384:1 Uncaught SyntaxError: Unexpected token u in JSON at position 0, these json requests have the variables + info.classid + and + info.name +, which my virtual items don't have and therefore are not displayed and I get an error. How i can fix this error?
My JS code:
html_items = '';
json1.forEach(function(info){
html_items += '<li class="fast-game-trade-item" style="transform: translate3d(0px, 0px, 0px); background-image: url(https://steamcommunity-a.akamaihd.net/economy/image/' + info.classid + '/23fx23f);"><div class="item_info darkBlueBg">\
<div class="item_info_img_wrap">\
<div class="item_info_img">\
<img src="https://steamcommunity-a.akamaihd.net/economy/image/' + info.classid + '/110fx100f" alt="' + info.market_hash_name + '">\
</div>\
</div>\
<div class="item_info_description">\
<div class="item_info_name">' + info.market_hash_name + '</div>\
<div class="item_info_price">' + info.price + '₽</div>\
</div>\
<div class="item_owner">\
<div class="item_owner_img">\
<img src="' + data1.user.avatar + '">\
</div>\
</div>\
</div></li>';
});
$('#game_'+data1.game_id+' #block_items_1').html(html_items);
In site templates I solve this problem as follows:
#if(!isset($i->img))
here code for Steam items
#else
here code for virtual items
#endif
How can I use #if #else in my case in Javascript?
EDIT add code with JSON parse:
data1 = JSON.parse(data);
var string = JSON.stringify(data1.items);
var json = JSON.parse(string);
var json1 = JSON.parse(json);
$('#bank').text(data1.gamePrice);
if (data1.number == 1) {
if(window.location.pathname == '/fastgame') {
$('title').text(Math.round(data.gamePrice,2) + '');
}
$('#game_'+data1.game_id+' #chance_1').toggleClass('chance_'+data1.user.id);
$('#game_'+data1.game_id+' #block_1 .player.first-player').css('background-image', 'url(' + data1.user.avatar + ')');
$('#game_'+data1.game_id+' #block_1').removeClass('empty');
$('#game_'+data1.game_id+' #block_1 .players-roulette-container').attr('id','bet_1_'+ data1.betId);
html_items = '';
json1.forEach(function(info){
html_items += '<li class="fast-game-trade-item" style="transform: translate3d(0px, 0px, 0px); background-image: url(https://steamcommunity-a.akamaihd.net/economy/image/' + info.classid + '/23fx23f);"><div class="item_info darkBlueBg">\
<div class="item_info_img_wrap">\
<div class="item_info_img">\
<img src="https://steamcommunity-a.akamaihd.net/economy/image/' + info.classid + '/110fx100f" alt="' + info.market_hash_name + '">\
</div>\
</div>\
<div class="item_info_description">\
<div class="item_info_name">' + info.market_hash_name + '</div>\
<div class="item_info_price">' + info.price + '₽</div>\
</div>\
<div class="item_owner">\
<div class="item_owner_img">\
<img src="' + data1.user.avatar + '">\
</div>\
</div>\
</div></li>';
});
$('#game_'+data1.game_id+' #block_items_1').html(html_items);
$('#game_'+data1.game_id+' #price_1').text(data1.price).countTo({
from: 0,
to: data1.price,
speed: 600,
formatter: function(value, options) {
return value.toFixed(2);
}
});
}
Solution to issue "JSON parsing error"
The reason you are getting Uncaught SyntaxError: Unexpected token u in JSON at position 0 is that you are somewhere stuffing undefined into JSON.parse. This is because JSON.parse will convert whatever you passed into it to a string and attempt to parse it, but undefined is not valid JSON, and you are getting an error at the first character already, which is u.
I don't see JSON.parse in your code, so this error must happen in a place you did not copy here.
Everything I'm going to explain below is not going to work unless you fix this error first. So please check where you are parsing your JSON and don't attempt to parse it if the text you are parsing is undefined.
A tip on the side: Open your browser console before the error happens (or refresh the page if you opened it too late). Then you will get more useful error output and most importantly also better info about where the error happened, especially when you have source maps. Even better, you will usually break into the code at the moment the error happens, so you can inspect the variables and see for example why you had undefined there. Also, when you click that file and line number at the right (VM13384:1 in your case), it will show you the line of code where it happened. If the code is minified (which I assume since here it shows line 1), you can click the {} button to auto-format it.
Answer to question "how to embed conditionals in a string"
You can use the ternary conditional operator ?:.
The syntax is condition ? valueIfTrue : valueIfFalse, and it evaluates to valueIfTrue when condition was truthy, otherwise it evaluates to valueIfFalse.
For example:
console.log('Is one greater than two? ' + (1 > 2 ? 'Yes' : 'No') + '!')
// Output: Is one greater than two? No!
Further notes
On a side note, the code will become much neater by using template strings (they use `backticks` and you can embed ${values} in them, and they can contain newlines):
html_items += `
<li class="fast-game-trade-item" style="transform: translate3d(0px, 0px, 0px); background-image: url(https://steamcommunity-a.akamaihd.net/economy/image/${info.classId}/23fx23f);"><div class="item_info darkBlueBg">
<div class="item_info_img_wrap">
<div class="item_info_img">
<img src="https://steamcommunity-a.akamaihd.net/economy/image/${info.classId}/110fx100f" alt="' + info.market_hash_name + '">
</div>
</div>
<div class="item_info_description">
<div class="item_info_name">${info.market_hash_name}</div>
<div class="item_info_price">${info.price}₽</div>
</div>
<div class="item_owner">
<div class="item_owner_img">
<img src="${data1.user.avatar}">
</div>
</div>
</li>
`;
Of course you could nest them, together with conditionals:
`
<div class="list">
${items.length > 0 ? `
<ul>
${items.map(item => `
<li>${item}</li>
`).join('')}
</ul>
`, `
<strong>No items!</strong>
`}
</div>
`
However, with all of this, there is still one issue: You are not escaping your text as HTML entities, so that HTML can sneak in, which can break formatting at best or cause XSS attack vulnerabilities at worst.
You should wrap all the text values in an escaping function. Unfortunately, browsers still don't have a straight-forward function to do that, but it's easy to create one yourself:
function esc (s) {
const i = document.createElement('i')
i.innerText = s
return i.innerHTML
}
This way, you can escape your text values like so:
`
<div class="item_info_name">${esc(info.market_hash_name)}</div>
`

Concatenate/Join Template Elements with a "+"

Is there a way to concatenate template elements? I have this template, which is a loop, but I can't figure out how I can concatenate/join multiple items. If I place the + inside the <kbd> tag, I get an extra + at the end and it is within the kbd tag (which I would like outside of the tag).
<template>
<kbd v-for="(v, i) in item" :key="i">{{ v }}</kbd>
</template>
The data I am using looks something like this (I am actually using a store):
data: () {
return {
item: [ 'a', 'b', 'c' ]
}
}
The result I am looking for is:
<kbd>a</kbd> + <kbd>b</kbd> + <kbd>c</kbd>
Easy enough. Conditional rendering based on index. Something similar to this should work:
<template v-for="(v, i) in item">
{{i > 0 ? ' + ' : ''}}<kbd :key="i">{{ v }}</kbd>
</template>

How can I use the variable in router-link?

How can I use the variable in router-link ?
<Col
span="8"
v-for="(item,index) in this.data"
>
<router-link to='/home/workpanel/true/ + item.name'>
there it can not convert the item.name to the value, it is just the string:
'/home/workpanel/true/ + item.name'
how can I use the variable in the router-link's to ?
if I click the link, the browser shows:
http://localhost:8080/home/workpanel/true/%20+%20item.name
Just add : before to attribute. In this way you can use javascript syntax.
<router-link :to="'/home/workpanel/true/' + item.name">

How to display dynamic HTML in uib-popover inject into uib-grid

I want to display a dynamic generated HTML in uib-popover, and it's shown when a <i> is clicked in each column header of uib-grid. The ng directives and variables also need to be active, such as ng-click, $scope etc.
I tried to use the headerCellTemplate in each column definition of uib-grid for displaying the<i>tag, and I passed a dynamic generated HtML to each column definition, so that I can get it in the headerCellTemplate with col.displayName.popoverTemplate, It works fine. But I can't pass any variables in to popover use $scope, and ng-click don't work at all. Someone said I need to compile the HTML or use the uib-popover-template, how do I pass params to uib-popover-html or there is another way.
THANKS!
columnDefs
columnDefs.push({
name:'columnViewData._name',
displayName:{//use this file to pass params into uib-grid headers
displayName:'columnName',
popoverTemplate:generatePopoverTemplate(),//template should be shown in popover for this column header
customerScope:$scope//current scope, I hope popover can access it
},
width:100,
enableColumnMenu:false,
pinnedLeft:true,
headerCellTemplate:generateUIGridHeaderTemplate('name',0)
});
Column header template:
var generateUIGridHeaderTemplate = function(columnName,type){
return "\
<div role=\"columnheader\" ng-class=\"{ 'sortable': sortable }\" ui-grid-one-bind-aria-labelledby-grid=\"col.uid + '-header-text ' + col.uid + '-sortdir-text'\" aria-sort=\"{{col.sort.direction == asc ? 'ascending' : ( col.sort.direction == desc ? 'descending' : (!col.sort.direction ? 'none' : 'other'))}}\">\
<div style=\"margin-top:0.3em;\">\
<div style=\"display:inline;\" role=\"button\" tabindex=\"0\" class=\"ui-grid-cell-contents ui-grid-header-cell-primary-focus\" col-index=\"renderIndex\" title=\"TOOLTIP\">\
<span class=\"ui-grid-header-cell-label\" ui-grid-one-bind-id-grid=\"col.uid + '-header-text'\">\
{{ col.displayName.displayName CUSTOM_FILTERS }}\
</span> \
<span ui-grid-one-bind-id-grid=\"col.uid + '-sortdir-text'\" ui-grid-visible=\"col.sort.direction\" aria-label=\"{{getSortDirectionAriaLabel()}}\">\
<i ng-class=\"{ 'ui-grid-icon-up-dir': col.sort.direction == asc, 'ui-grid-icon-down-dir': col.sort.direction == desc, 'ui-grid-icon-blank': !col.sort.direction }\" title=\"{{isSortPriorityVisible() ? i18n.headerCell.priority + ' ' + ( col.sort.priority + 1 ) : null}}\" aria-hidden=\"true\">\
</i> \
<sub ui-grid-visible=\"isSortPriorityVisible()\" class=\"ui-grid-sort-priority-number\">\
{{col.sort.priority + 1}}\
</sub>\
</span>\
</div>\
<i style=\"margin-left:-1em;\" class=\"glyphicon glyphicon-filter\" popover-placement='bottom' uib-popover-html=\"col.displayName.popoverTemplate\" popover-title=\"Filter\" popover-append-to-body=\"true\">\
</i>\
<!-- above line is the popover togger shown in the column header-->\
</div>\
<div role=\"button\" tabindex=\"0\" ui-grid-one-bind-id-grid=\"col.uid + '-menu-button'\" class=\"ui-grid-column-menu-button\" ng-if=\"grid.options.enableColumnMenus && !col.isRowHeader && col.colDef.enableColumnMenu !== false\" ng-click=\"toggleMenu($event)\" ng-class=\"{'ui-grid-column-menu-button-last-col': isLastCol}\" ui-grid-one-bind-aria-label=\"i18n.headerCell.aria.columnMenuButtonLabel\" aria-haspopup=\"true\">\
<i class=\"ui-grid-icon-angle-down\" aria-hidden=\"true\">\
\
</i>\
</div>\
<div ui-grid-filter>\
</div>\
</div>"}
Popover template
var generatePopoverTemplate = function(){
var t = $sce.trustAsHtml('<a class="btn" ng-click="alert(\'asd\')">asd</a>');
$compile(t)($scope);
return t;
}
I solved my question :)
Generate the popover template and put it in to $compileCache with an Id then uib-popover-template property can find it and ng works fine.

Angularjs dynamic ng-if data binding in span

I have this Angular expression in my view:
<span ng-if="{{list.StoreList ? (list.StoreList.length ' Products)' : '(0 Products)'}}"> </span>
So if I have any items in the StoreList then I'll display the count, otherwise I'll just show 0 products.
I'm getting unexpected expecting error from Angularjs.
How can I solve this?
Try with:
<span ng-if="list.StoreList">{{'(' + list.StoreList.length + ' Products)'}}</span>
<span ng-if="!list.StoreList">(0 Products)</span>
This is not formatted correctly
<span ng-if="{{list.StoreList ? (list.StoreList.length ' Products)' : '(0 Products)'}}"> </span>
should probably be
<span ng-if="{{ list.StoreList ? '(' + list.StoreList.length + ' Products)' : '(0 Products)'}}"> </span>

Categories

Resources