Generating HTML from JSON in Angular - javascript

Im trying to generate HTML code to Angular component template from JSON and im looking for best practice.
Actually Im fetching JSON and use *NgFor and *NgIf to achieve that
const blocks = [
{
id: '1',
type: 'header',
fields: [{
content : 'H1 Header'
}]
},
{
id: '2',
type: 'image',
fields: [{
src : 'https://i.pinimg.com/originals/5e/f6/83/5ef68313994aaf68e87d190de943f104.jpg',
title: 'My Image'
}]
},
]
<mat-accordion multi="true" *ngIf="blocks.length > 0">
<mat-expansion-panel *ngFor="let block of blocks">
<mat-expansion-panel-header>
{{ block.type }}
</mat-expansion-panel-header>
<ul>
<div *ngFor="let field of block.fields">
<div *ngIf="block.type == 'header'">
<h1>{{ field.content }}</h1>
</div>
<div *ngIf="block.type == 'image'">
<img style="width:100%;" src={{field.src}} />
</div>
<div *ngIf="block.type == 'paragraph'">
<p>{{field.content}}</p>
</div>
</div>
</ul>
</mat-expansion-panel>
</mat-accordion>
Is there better way to do that?

Instead if using several ngif, I would use ngswitch

this is not a good practice, in angular you have a template and you should bind properties and events to make your app dynamic. Keep in mind that angular sanitizes your input so for example you cannot render scripts and style in the way you are tryng to do so you won't be able to rendere an entire page. If you want to change a title dynamically you can do something like this:
// in your component
title: string;
// in template
<h1>{{title}}</h1>
same for image
// in component
src: string;
//in template
<img [src]="src">
Angular is made to build apps, but apps have a structured page, if not what kind of app would you build?

Related

Interpolation in a Vue component that creates HTML

I am building a web app using Vue 2.6.x. In the prototype that was created we have a line like this repeated many times:
<label class="title">On Time</label><span class="score float-right">Score: {{ score.ap_ontime }}
The only part of this whole line that changes is the data within the {{ }}. I would like to turn this into a component that I can call many times. For example:
<MyLabel title="On Time" score="score.ap_ontime" />
...so that I don't have to type this score interpolation over and over in a long HTML file. I know how pass props to the component and add text to the template of the component:
<template>
...
<label class="title">{{title}}</label>
...
</template>
..but what I can't figure out is how to take a string (e.g.score.ap_ontime) and have the template inject something that will render that value from a score plain old JavaScript object that exists in the application and is imported into the template. The original code I show at the top of this post renders fine and reacts to changes in the JavaScript object I just can't figure out how to do this in a component that creates HTML and Vue template syntax based on props.
Your new component should look something like this:
<template>
<label class="title">{{ caption }}</label>
<span class="score float-right">Score: {{ onTime }}</span>
</template>
<script>
export default
{
name: 'MyNewComponent',
props:
{
caption:
{
type: String,
default: ''
},
onTime:
{
type: [String, Number],
default: 0
}
}
}
</script>
Then you can call your component in this way:
<my-new-component :caption="Big title" :on-time="score.ap_ontime" />

Angular *ngIf displays a div without content

I have an array with objects, each having a type.
I want to display only the objects with a specific type that i change by pressing some buttons.
For some reasons, for the "Food" type, it also renders the other two objects but without any content.
Here's the array and type
objects = [
{
title: 'Spartan Sandwich',
price: 4,
type: 'Food',
},
{
title: 'Math Lessons',
price: 10,
type: 'Necesities',
},
{
title: 'Ice Skating',
price: 10,
type: 'Misc',
},
];
type: string = 'Food';
Here's the HTML
<div class="objects">
<div class="object-container" *ngFor="let object of objects">
<app-object [object]="object" *ngIf="object.type === type"></app-object>
</div>
</div>
I know that it's normal for it to always render all the object-container divs, but how can i fix it so that they do not appear?
You can run the ngFor loop in ng-content and check the object.type in object-container div:
<div class="objects">
<ng-content *ngFor="let object of objects">
<div class="object-container" *ngIf="object.type === type">
<app-object [object]="object"></app-object>
</div>
</ng-content>
</div>
Note: ng-content doesn't render any HTML element.
Either you go with the code suggested by Shuvo or you manipulate the "objects" array using filter.
viewData = this.objects.filter(({ type }) => type === this.type);
The best way to do this is by putting the app-object component inside a virtual container like ng-container that way there will not be any need to use an extra div.
The code will look like this:
`
<div class="objects">
<div class="object-container" *ngFor="let object of objects">
<ng-container *ngIf="object.type === type">
<app-object [object]="object"></app-object>
</ng-container>
</div>
</div>
`

Vuejs warning: infinite update loop in a component render function

working through my first project in vue js.
Here, looping through different tabs and show correct content for each tab when clicked on.
https://codepen.io/anon/pen/vWPMGq?editors=1010
problem here is with line
this.cardData = $(".card-content").html(this.coinInfo[this.activeTabName]);
but I'm not sure how to fix this.
You shouldn't be mixing jquery and Vue if it's not 100% necessary.
Here a simple way to do it:
https://jsfiddle.net/gmmujLs4/2/
HTML
<div id="root">
<div class="navbar-start" v-for="tab in tabs">
<a class="navbar-item" href="#" #click="activeTabName = tab.name">{{tab.name}}</a>
</div>
<div class="card-content">
{{ coinInfo[activeTabName] }}
</div>
</div>
Vue instance
new Vue({
el: '#root',
data: {
activeTabName: 'Description',
tabs: [
{
name: 'Description',
},
{
name: 'Features',
},
{
name: 'Technology',
}
],
coinInfo: {
Description:'DescriptionContent',
Features:'FeaturesContent',
Technology:'TechnologyContent'
}
}
})
coinInfo could be passed by properties instead of beeing declared as data.

Vue accessing loop index within input name [duplicate]

My Vue.js component is like this:
<template>
<div>
<div class="panel-group" v-for="item in list">
...
<div class="panel-body">
<a role="button" data-toggle="collapse" href="#purchase-{{ item.id }}" class="pull-right" aria-expanded="false" aria-controls="collapseOne">
Show
</a>
</div>
<div id="purchase-{{ item.id }}" class="table-responsive panel-collapse collapse" role="tabpanel">
...
</div>
</div>
</div>
</template>
<script>
export default {
...
computed: {
list: function() {
return this.$store.state.transaction.list
},
...
}
}
</script>
When executed, there exists an error like this:
Vue template syntax error:
id="purchase-{{ item.id }}": Interpolation inside attributes has
been removed. Use v-bind or the colon shorthand instead.
How can I solve it?
Use JavaScript code inside v-bind (or shortcut ":"):
:href="'#purchase-' + item.id"
and
:id="'purchase-' + item.id"
Or if using ES6 or later:
:id="`purchase-${item.id}`"
Use v-bind or shortcut syntax ':' to bind the attribute.
Example:
<input v-bind:placeholder="title">
<input :placeholder="title">
Just use
:src="`img/profile/${item.photo}`"
If you're pulling data from an array of objects, you need to include require('assets/path/image.jpeg') in your object like I did below.
Working example:
people: [
{
name: "Name",
description: "Your Description.",
closeup: require("../assets/something/absolute-black/image.jpeg"),
},
Using require(objectName.propName.urlPath) in the v-img element did not work for me.
<v-img :src="require(people.closeup.urlPath)"></v-img>
The easiest way is too require the file address:
<img v-bind:src="require('../image-address/' + image_name)" />
The complete example below shows ../assets/logo.png:
<template>
<img v-bind:src="require('../assets/' + img)" />
</template>
<script>
export default {
name: "component_name",
data: function() {
return {
img: "logo.png"
};
}
};
</script>
The most elegant solution is save images outside Webpack. By default, Webpack compress images in Base64, so if you save images in your assets folder, that doesn't work because Webpack will compress images in base64, and that isn’t a reactive variable.
To solve your problem, you need to save your images in your public path. Usually the public path is in "public" folder or "statics".
Finally, you can do this:
data(){
return {
image: 1,
publicPath: process.env.BASE_URL
}
}
And your HTML you can do this:
<img :src="publicPath+'../statics/img/p'+image+'.png'" alt="HANGOUT PHOTO">
When to use the public folder
You need a file with a specific name in the build output
File depends on a reactive variable that can change in execution time
You have images and need to dynamically reference their paths
Some library may be incompatible with Webpack and you have no other option but to include it as a <script> tag.
More information: "HTML and Static Assets" in Vue.js documentation

Vue.js - Getting data from multiple keys in an object

I'm trying to set something up in my app where I can select an option from a list and change the background of the app depending on what's selected.
Let's say I have a list like:
<li v-for="item in items">
<label class="radio">
<input type="radio" value="{{ item.name }}" v-model="itemSelection">
{{ item.name }}
</label>
</li>
items is an array that's stored in my store.js:
items: [
{name: 'item1', img: 'placehold.it/200x200-1'}
{name: 'item2', img: 'placehold.it/200x200-2'}
{name: 'item3', img: 'placehold.it/200x200-3'}
],
So when you select item1 I want to not only pull the name from the selection (which gets passed up to the parent component in itemSelection to display there) but also the img link to place that in css to change the background of the body. I'm not entirely sure how to go about this, as I'm pretty new to vue and this is basically something I'm building to help me learn!
Thanks!
You can do this by several ways e.g:
watch : {
itemSelection: function(val) { ... }
}
There is some examples. Check this fiddle

Categories

Resources