In my project I'm binding a list of 'paper-fab' elements from a JSON array.
I want to give only the name of the icon instead of the full path to bind with 'src' property of 'paper-fab'. How can I achieve this? Is it possible to do it with computed property? Thanks in advance.
The code snippet and JSON format is given below.
<ul id="actionButtons">
<template is="dom-repeat" items="[[plugins]]">
<li>
<span>
<paper-fab mini src="[[item.iconSrc]]" id="[[item.id]]" name="[[item.name]]" class$="[[item.cssClass]]"> </paper-fab>
</span>
</li>
</template> </ul>
JSON structure is given below
plugins:
[
{
"id": 1,
"name": "Image_1",
"iconSrc": "app/images/icons/pacs_pull.png",
"cssClass": "white"
},
{
"id": 2,
"name": "Image_2",
"iconSrc": "app/images/icons/pacs_push.png",
"cssClass": "grey"
},
{
"id": 3,
"name": "Image_3",
"iconSrc": "app/images/icons/fileBrowser.png",
"cssClass": "white",
}
]
Check out Computed bindings
<template>
My name is <span>{{computeFullName(first, last)}}</span>
</template>
<script>
Polymer({
is: 'x-custom',
properties: {
first: String,
last: String
},
computeFullName: function(first, last) {
return first + ' ' + last;
}
...
});
</script>
</dom-module>
You should be able to pass item.iconSrc to you compute to append the path to the icon
Related
I have imported the following json file:
[
{
"case_id": "1234",
"thread": [
{
"t_id": "1111",
"text": "test"
},
{
"t_id": "2222",
"text": "test"
}
]
},
{
"case_id": "5678",
"thread": [
{
"t_id": "9999",
"text": "test"
},
{
"t_id": "8888",
"text": "test"
},
{
"t_id": "777",
"text": "test"
}
]
}
]
using the following:
import cases from '../cases.json'
The whole json dataset is available in cases variable and can be used in the template with the support of v-if and v-for.
How can I create a separate dataset (thecase) that contains only threads for a given case_id? In the template I would only like to use v-for to display all threads for a given case_id.
Below is my export default section:
export default {
name: "details",
props: {
case_id: {
required: true,
type: String
}
},
data () {
return {
cases,
thecase: ### THIS IS THE PART I CANNOT FIGURE OUT ###
}
}
};
You can remove thecase from data options and use a computed property instead for thecase. Inside the computed property, we will need to use array .find() method to find the case where case_id is same as the case_id passed in the prop:
data: {
cases,
},
computed: {
thecase: function() {
return this.cases.find(c => c.case_id === (this.case_id || ''))
}
}
and then you can use v-for on thecase.thread just like you would do for a data option like:
<li v-for="item in thecase.thread" :key="item.t_id">
{{ item.text }}
</li>
You can further modify it and use v-if & v-else to show a text like No cases were found with give case id in case there is no match found.
I have a UI where initially a User has to check some checkboxes. The checkboxes have sequential IDs. The JSON Structure for it is as follows:
{
"categories": [{
"name": "Product",
"labels": [{
"id": 1,
"name": "I work on an asset (capital good).",
"checked": false
}, {
"id": 2,
"name": "I work on a consumer product.",
"checked": false
}, {
"id": 3,
"name": "I am not sure what type of product I work on.",
"checked": false
}
]
}, {
"name": "Goal",
"labels": [{
"id": 4,
"name": "I want to improve the product's reliability.",
"checked": false
}, {
"id": 5,
"name": "I need information to identify root causes.",
"checked": false
}, {
"id": 6,
"name": "I need information about the product's environment.",
"checked": false
}, {
"id": 7,
"name": "I need information about customer requirements.",
"checked": false
}, {
"id": 8,
"name": "I need quantified information.",
"checked": false
}, {
"id": 9,
"name": "I am not sure what I need.",
"checked": false
}
]
}
]
}
I render it Angular using the following Code:
component.html
<div class="row mt-lg-auto" *ngFor="let filter of filters['categories']">
<div class="col-lg-12">
<h4>
{{filter['name']}}
</h4>
<div *ngFor="let label of filter['labels']">
<div class="form-check">
<input class="form-check-input"
type="checkbox"
value="{{label['id']}}"
id="{{label['id']}}"
[(ngModel)]="label['checked']"
(change)="changeCheck(label['id'], $event)"
>
<label class="form-check-label" for="{{label['id']}}">
{{label['name']}}
</label>
</div>
</div>
</div>
</div>
component.ts
I directly import the JSON file from src/assets/ folder and save the id to a Set in order to avoid duplicate values when the user selects a checkbox.
import { Component, OnInit } from '#angular/core';
import * as FilterFunc from 'src/assets/FilterFunction.json';
const Filters: any = FilterFunc;
#Component({
selector: 'explore-step1',
templateUrl: './explore-step1.component.html',
styleUrls: ['./explore-step1.component.css']
})
export class ExploreStep1Component implements OnInit {
filters = Filters.default;
selections = new Set();
constructor() {
}
ngOnInit(): void {
}
changeCheck(id: number, event: any) {
(event.target.checked) ?
this.selections.add(id):
this.selections.delete(id);
console.log(this.selections);
}
}
I am using ngx-treeview to render a tree view for a fixed JSON file that has the following structure:
GitHub Gist of the Complete Recursive JSON
Here on the children in the most depth have the following key-value pair:
"value": {
"label_ids": [relevant ids from the first json],
"description": "some text to render"
}
else the "value" is null.
I wish to compare the Set values to the above mentioned recursive JSON's label_ids and if one or more than one values in the label_ids match with the Set then change the checked value to true
How does one accomplish this in Typescript/Angular?
I solved it by creating a Recursion Parsing Function which takes in the JSON Structure.
Within the ngOnInit() I call the service and pass it to the parseTree function
I recursively parse it and compare the values with the Set
I add additional information like a Font-Awesome class text within the value structure to render it
pass the updated JSON to the respective items variable
component.ts:
parseTree(factors: TreeviewItem[]) {
factors.forEach(factor => {
// console.log(factor);
if (!isNil(factor.value)) {
let labels: number[] = factor.value['label_ids'];
labels.forEach(label => {
if(this.selected.findIndex(l => l == label) > -1) {
factor.value['highlighted'] = true;
factor.value['class'] = 'fas fa-flag';
}
});
}
if (!isNil(factor.children)) {
this.parseTree(factor.children);
}
});
this.items = factors;
}
here selections is a Set and within ngOnInit() I set it a fixed value:
ngOnInit() {
this.selections.add(15);
this.items = this.parseTree(this.service.getDataQualityFactors());
}
Based on ngx-treeview example I use the itemTemplate in the code and add the Font-Awesome fonts next to the selections as follows:
component.html
<label class="form-check-label" *ngIf="item.value !== null && item.value['highlighted']">
<i class="{{item.value['class']}}" aria-hidden="true" title="Highlighted" [ngClass]="{'marked': item.checked}"></i>
</label>
and use the CSS classes to manipulate the color change of the font:
.fa-flag {
color: red;
}
.fa-flag.marked {
color: green;
}
StackBlitz Example Code
I want to access a property, defined in ready() of my Polymer element (as seen in the following code):
Polymer({
is: 'my-list',
ready: function() {
this.tasks = [{
"task": {
"name": "OTS",
"rules": [{"name": "rule 1", "id": "1"}]
}
}];
this.parseJson();
},
parseJson: function() {
this.taskname = JSON.parse(this.tasks.task.name); // errors here
}
});
But I am getting the following error:
Uncaught TypeError: Cannot read property 'name' of undefined
for this line:
JSON.parse(this.tasks.task.name);
How do I fix this?
this.tasks is an array of objects, but parseJson() is not using the correct syntax to access array elements.
parseJson() should be using this.tasks[0].task.name, assuming your actual code can have more than one task and that you're only interested in the first one. Also, you don't need to use JSON.parse(), since the task name is not a JSON string.
Here's a working demo:
Polymer({
is: 'my-list',
ready: function() {
this.tasks = [{
"task": {
"name": "Task1",
"rules": [{
"name": "rule 1",
"id": "1",
}]
}
}];
this.parseJson();
},
parseJson: function() {
this.taskname = this.tasks[0].task.name;
console.log(this.taskname);
}
});
<head>
<base href="https://polygit.org/polymer+:master/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<my-list></my-list>
</body>
I was using polymer example and they used core-ajax to call the api.I want to display text from the openweathermap api.When i call the api it displays no data.I'm not able to display any data and when i placed console.log(this.post) in the post-list element it gives me undefined.I'm practically a noob when it comes to polymer.
Below is the Api Calling Method
<polymer-element name="post-service" attributes="posts">
<template>
<style>
:host {
display: none;
}
</style>
<core-ajax id="ajax"
auto
url="http://api.openweathermap.org/data/2.5/weather?q=hyderabad"
on-core-response="{{postsLoaded}}"
handleAs="json">
</core-ajax>
</template>
<script>
Polymer('post-service', {
created: function() {
this.posts = [];
},
postsLoaded: function() {
// Make a copy of the loaded data
this.posts = this.$.ajax.response;
},
/**
* Update the service with the current favorite value.
* (Two-way data binding updates the favorite value
* stored locally.) If this was a real service, this
* method would do something useful.
*
* #method setFavorite
* #param uid {Number} Unique ID for post.
* #param isFavorite {Boolean} True if the user marked this post as a favorite.
*/
setFavorite: function(uid, isFavorite) {
// no service backend, just log the change
console.log('Favorite changed: ' + uid + ", now: " + isFavorite);
}
});
</script>
</polymer-element>
This is element is used to display
<polymer-element name="post-list" attributes="show">
<template>
<style>
:host {
display: block;
width: 100%;
}
post-card {
margin-bottom: 30px;
}
</style>
<post-service id="service" posts="{{posts}}">
</post-service>
<div layout vertical center>
<template>
<post-card>
<h2>{{post.weather.main}}</h2>
<p>{{post.weather.description}}</p>
</post-card>
</template>
</div>
</template>
<script>
Polymer({});
console.log(this.post);
</script>
</polymer-element>
Example json
[
{
"uid": 1,
"text" : "Have you heard about the Web Components revolution?",
"username" : "Eric",
"avatar" : "../images/avatar-01.svg",
"favorite": false
},
{
"uid": 2,
"text" : "Loving this Polymer thing.",
"username" : "Rob",
"avatar" : "../images/avatar-02.svg",
"favorite": false
}
]
My api response(json)
{
"coord": {
"lon": 78.47,
"lat": 17.38
},
"weather": [
{
"id": 802,
"main": "Clouds",
"description": "scattered clouds",
"icon": "03d"
}
],
"base": "cmc stations",
"main": {
"temp": 303.15,
"pressure": 1010,
"humidity": 62,
"temp_min": 303.15,
"temp_max": 303.15
},
"wind": {
"speed": 7.7,
"deg": 280
},
"clouds": {
"all": 40
},
"dt": 1436677800,
"sys": {
"type": 1,
"id": 7830,
"message": 0.0124,
"country": "IN",
"sunrise": 1436660330,
"sunset": 1436707470
},
"id": 1269843,
"name": "Hyderabad",
"cod": 200
}
The core-ajax element sends an event when the data are received. To use them you have to manipulate the fired event.
postsLoaded: function(event, detail) {
// Event contains lot of informations
console.log(event);
// Detail would be the received data
console.log(detail);
// Make a copy of the loaded data
this.posts = detail; // or this.posts = event.detail;
}
It would be easier to see what happens if you add a listener on the posts attributes in your element post-list. See the doc section Observing properties.
<script>
Polymer({
postsChanged: function(oldValue, newValue) {
console.log(newValue);
}
});
</script>
Moreover, I think you have a typo in your code. In the template you are using post and no posts :
<template>
<post-card>
<h2>{{post.weather.main}}</h2>
<p>{{post.weather.description}}</p>
</post-card>
</template>
Finally, if you are starting with Polymer, I suggest you to start with the version 1.0.
I'm trying to use Knockout's mapping plugin on a nested JSON object with variable data inside. However, I'm not sure how to get it to display in my HTML. How do I correctly map all the nested JSON objects and display it as, say, a simple string? Here is my code:
JS
var ListModel = function(jsonData) {
var self = this;
self.master = ko.mapping.fromJS(jsonData);
}
var listModel = new ListModel(jsonData);
ko.applyBindings(listModel);
HTML
<!-- ko foreach: master -->
<div data-bind="text: $data"></div>
<!-- /ko -->
Sample JSON
{"Level 1a":"Hi","Level 1b":{
"Level 2a":"Hello","Level 2b":{
"Level 3":"Bye"}
}
}
Sample Output
Hi
Hello
Bye
The main thing I'm trying to do here is to print out the values from all nested levels. The key values and number of nested levels are entirely variable (most of the nested JSON examples I found on SO and online were for fixed keys). Is this possible?
Update: I found the jQuery equivalent, but I still need the Knockout implementation for observables.
Since your JSON object has variable keys, you must transform it into a fixed, predictable structure first or nested template mapping will not work (knockout is declarative, so you need to know key names beforehand).
Consider the following custom mapping code (no knockout mapping plugin needed):
var ListModel = function(jsonData) {
var self = this;
self.master = ko.observableArray([]);
function nestedMapping(data, level) {
var key, value, type;
for (key in data) {
if (data.hasOwnProperty(key)) {
if (data[key] instanceof Object) {
type = "array";
value = ko.observableArray([]);
nestedMapping(data[key], value());
} else {
type = "simple";
value = ko.observable(data[key]);
}
level.push({key: key, type: type, value: value});
}
}
}
nestedMapping(jsonData, self.master());
}
the function nestedMapping() turns your data structure:
{
"Level 1a": "Hi",
"Level 1b": {
"Level 2a": "Hello",
"Level 2b": {
"Level 3": "Bye"
}
}
}
into:
[
{
"key": "Level 1a",
"type": "simple",
"value": "Hi"
},
{
"key": "Level 1b",
"type": "array",
"value": [
{
"key": "Level 2a",
"type": "simple",
"value": "Hello"
},
{
"key": "Level 2b",
"type": "array",
"value": [
{
"key": "Level 3",
"type": "simple",
"value": "Bye"
}
]
}
]
}
]
Now you can create a template like this one:
<script type="text/html" id="nestedTemplate">
<!-- ko if: type == 'simple' -->
<div class="name" data-bind="text: value, attr: {title: key}"></div>
<!-- /ko -->
<!-- ko if: type == 'array' -->
<div class="container" data-bind="
template: {
name: 'nestedTemplate',
foreach: value
}
"></div>
<!-- /ko -->
</script>
See it working: http://jsfiddle.net/nwdhJ/2/
Note a subtle but important point about nestedMapping(). It creates nested observables/observableArrays. But it works with the native array instances (by passing self.master() and value() into the recursion).
This way you avoid needless delay during object construction. Every time you push values to an observableArray it triggers knockout change tracking, but we don't need that. Working with the native array will be considerably faster.
Change your JSON data to this (note that the arrays!):
[
{
"Text": "Hi",
"Children": [
{
"Text": "Hello",
"Children": [
{
"Text": "Bye"
}
]
}
]
}
]
and use a self-referential template:
<script type="text/html" id="nestedTemplate">
<div class="name" data-bind="text: Text"></div>
<div class="container" data-bind="
template: {
name: 'nestedTemplate',
foreach: Children
}
"></div>
</script>
that you call like this:
<div class="container" data-bind="
template: {
name: 'nestedTemplate',
foreach: master
}
"></div>
You can then use CSS to manage indent:
/* indent from second level only */
div.container div.container {
margin-left: 10px;
}
See it on jsFiddle: http://jsfiddle.net/nwdhJ/1/