Rendering ng-content as html - Angular 2 - javascript

My API is returning HTML content (I want it to - it has to). I want to render this content in Angular 2/7 as HTML.
Right now, I have
<div class="card-container" [ngClass]="type === 'bot' ? 'white' : 'blue'">
<div class="header" >
<b>{{name}}</b>
{{date}}
</div>
<ng-content></ng-content>
</div>
The goal here is to have ng-content rendered as HTML.
For context, ng-content is something like this
Test Date: Mon, Nov 19
And this component is called chat-card.component
And it's a parent is called chat-card-area.component
<div class="right-child">
<app-chat-card type={{type}}>
<ng-content></ng-content>
</app-chat-card>
</div>
And the parent if chat-card-area is simply chat.component, which has
<div *ngFor="let message of messages">
<app-chat-card-area type={{message.type}}>
{{message.text}} // I want this to be rendered as html
</app-chat-card-area>
</div>
Any idea of how to do this?

Use innerHTML binding here as:-
<div *ngFor="let message of messages">
<app-chat-card-area type={{message.type}}>
<div [innerHTML]="message.text"></div> // Will render as HTML
</app-chat-card-area>
</div>

Related

Is it possible to assign the hash(#) automatically in angular?

Is there any way where I can assign the hash(#) automatically to the elements inside an ngfor?
<div *ngFor="let note of notes; index as i">
<h3 #[note][i]>
{{ note }}
</h3>
</div>
The result I would expect would be something like this:
<div>
<h3 #note11>
note1
</h3>
</div>
<div>
<h3 #note122>
note12
</h3>
</div>
<div>
<h3 #note153>
note15
</h3>
</div>
You can use the index variable to automatically assign a unique id to each element in the ngFor loop. Here's an example:
<div *ngFor="let note of notes; index as i">
<h3 id="note{{i}}">
{{ note }}
</h3>
</div>
This will give each element an id of "note0", "note1", "note2", etc.
Regarding using #, it is not possible to use it in such a way. The # symbol is used to create a template reference variable, you can use it like <h3 #myNote>{{ note }} and you can access the element using myNote in your component.
Try this:
<div *ngFor="let note of notes; index as i">
<h3 #{{note}}{{i}}>
{{ note }}
</h3>
</div>
Note:
This would do your work but # is used to create template reference.

angular repeat with template?

I'm currently trying to figure out how to use templates in angular. At present, I'm playing with ui.router (angular-ui-router) but I can't find good documentation on how the templating language is used to embed a sub-template view, especially as relates to a repeating element for different model instances.
BACKGROUND:
I am basically converting a static-local-filesystem image uploader/manager to work with amazon S3. The background essentials are already worked out, now I'm trying to improve the UI itself by converting it from 10 year old javascript to angular.js. I have it 'working' for an all-in-one html page but would prefer to modularize it to make it more dynamic.
CONTEXT:
I get a list of objects under a given prefix back from a listObjectsV2() call to the AWS sdk via the s3 client. I parse the results to break it into a pseudo-directory tree then display one directory at a time starting at the [virtual] root dir just after the prefix. (FYI the prefix is a userid)
I built a UserDir object that uses a PseudoDir sub object to define a virtual directory with array properties for 'subdirs' (more PseudoDir objects representing virtual sub-directories) and 'images' (S3 objects that are image files of one type or another).
What I want to display for any given 'current' directory (e.g. "" or the user root) is first a list of folder icons for each the curDir.subdirs, then a thumbnail icon for each of the curDir.images.
QUESTION:
I already have this working from a single html file and even managed to figured out how to use ui.router to create a for the main page. Now I want to modularize it so that a different controller will handle folder icon/info behavior, and another for image icons/behaviors.
i.e. I have already started building a 'FolderController' and a 'ImageController' and would like the ngRepeat for 'image in curDir.images' for example, to invoke a state with it's own template but I can't seem to find an example on how to do that.
Here is the current all-in-one template code. But I would like to move each sub-block into a state for FolderController with a templates/folder.html template and one for ImageController with a templates/image.html but can't seem to find an example of how to write the syntax:
<!-- folders -->
<div ng-repeat="(folder, pDir) in subdirs" ng-controller="FolderController" ng-init="folderName=folder;awsObject=pDir">
<div id="{{folderName}}" class="Item">
<div class="Icon ui-draggable ui-draggable-handle">
<img id="{{folderName}}Icon" src="../../images/folder.png">
</div>
<div id="{{folderName}}Desc" class="Description">
<span id="{{folderName}}Name" class="filename" title="{{folderName}}/">{{folderName}}/</span>
</div>
</div>
</div>
<!-- images -->
<div ng-repeat="(filename, uImage) in images" ng-controller="ImageController" ng-init="uImage=uImage">
<div id="image{{uImage.hash}}" class="Item">
<div class="Icon ui-draggable ui-draggable-handle">
<img id="icon{{uImage.hash}}" ng-src="{{ uImage.thumbSrc }}"></div>
<div id="desc{{uImage.hash}}" class="Description">
<span id="name{{uImage.hash}}" class="filename" title="{{filename}}">{{filename}}</span>
<img id="thumb{{uImage.hash}}" src="../../images/tick_image-services.png" class="Check Right" ng-show="uImage.usedInLayout" title="Used in layout"><br />
<span id="date{{uImage.hash}}" ng-show="(uImage.mtime > 0)">Date uploaded: {{ uImage.mtime | date: 'EEE MMM dd yyyy' }}</span><br />
<span id="size{{uImage.hash}}" ng-show="(uImage.size > 0)">Size: {{ uImage.size | humanizeBytes }}</span><br />
<span id="dims{{uImage.hash}}" ng-show="((uImage.width > 0) && (uImage.height > 0))">Dimensions: {{ uImage.width }} x {{ uImage.height }} pixels</span><br />
<span id="aspect{{uImage.hash}}" ng-show="(uImage.aspectRatio)">Aspect Ratio: <span class="AspectRatio">{{ uImage.aspectRatio }}</span></span><br />
</div>
</div>
</div>
You can create two components, one for folders and one for images, see here for official docs.
A rough draft would look like:
angular.module('myApp').component('images', {
templateUrl: 'imageList.html',
bindings: {
images: '='
}
});
imageList.html:
<div ng-repeat="(filename, uImage) in images" ng-controller="ImageController" ng-init="uImage=uImage">
<div id="image{{uImage.hash}}" class="Item">
<div class="Icon ui-draggable ui-draggable-handle">
<img id="icon{{uImage.hash}}" ng-src="{{ uImage.thumbSrc }}"></div>
<div id="desc{{uImage.hash}}" class="Description">
<span id="name{{uImage.hash}}" class="filename" title="{{filename}}">{{filename}}</span>
<img id="thumb{{uImage.hash}}" src="../../images/tick_image-services.png" class="Check Right" ng-show="uImage.usedInLayout" title="Used in layout"><br />
<span id="date{{uImage.hash}}" ng-show="(uImage.mtime > 0)">Date uploaded: {{ uImage.mtime | date: 'EEE MMM dd yyyy' }}</span><br />
<span id="size{{uImage.hash}}" ng-show="(uImage.size > 0)">Size: {{ uImage.size | humanizeBytes }}</span><br />
<span id="dims{{uImage.hash}}" ng-show="((uImage.width > 0) && (uImage.height > 0))">Dimensions: {{ uImage.width }} x {{ uImage.height }} pixels</span><br />
<span id="aspect{{uImage.hash}}" ng-show="(uImage.aspectRatio)">Aspect Ratio: <span class="AspectRatio">{{ uImage.aspectRatio }}</span></span><br />
</div>
</div>
and your original html would look like:
<!-- folders -->
<div ng-repeat="(folder, pDir) in subdirs" ng-controller="FolderController" ng-init="folderName=folder;awsObject=pDir">
<div id="{{folderName}}" class="Item">
<div class="Icon ui-draggable ui-draggable-handle">
<img id="{{folderName}}Icon" src="../../images/folder.png">
</div>
<div id="{{folderName}}Desc" class="Description">
<span id="{{folderName}}Name" class="filename" title="{{folderName}}/">{{folderName}}/</span>
</div>
</div>
</div>
<image-list [images]="images" ></image-list>
EDIT
Here is an example plunker which shows a simple implementation of a component, with data binding and ng-repeat in it, no need for ui-router for what you asked for. Please note that the html I wrote above is a botched copy paste of what you wrote - so the double ng-repeat was a mistake, updated the html.

how to add :key in v-for with multiple divs

I want to make v-for loop without any html element so I decided to use <template as parent. I don't know to assign :key for this loop. I can't assign it to template and to every div inside loop. Any ideas?
<template
v-for="{ id, text, option, percentage, value } in reports"
>
<div class="table-row__index">
{{ id }}
</div>
<div class="table-row__title">
<p>{{ text }} - <strong>{{ option }}</strong></p>
</div>
<div class="table-row__info">
{{ percentage }}%
</div>
<div class="table-row__info">
{{ value }}
</div>
</template>
As a good practice we should always have a parent element inside. But due to your constraints, it's okay to use for loop on template as given in official docs
https://v2.vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt
In this case, any keys have to be added to child elements/components and this is what officially recommended.
See this example and please add keys to your div's inside template.
new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template v-for="n in 5">
<span :key="'number' + n">{{ n }}</span>
<span :key="'dot' + n">. </span>
</template>
</div>
you just can't have the for loop in your template. The for loop directive is only allowed to be inside the first child component ( or root component) inside your template.
Here is an example of how you can render your loop:
<template>
<div class="cant-have-for-loop-this-is-the-root-component">
<div v-for="{ id, text, option, percentage, value } in reports" :key="{id}">
<div class="table-row__index">
{{ id }}
</div>
<div class="table-row__title">
<p> {{ text }} - <strong>{{ option }}</strong></p>
</div>
<div class="table-row__info">
{{ percentage }}%
</div>
<div class="table-row__info">
{{ value }}
</div>
</div>
</div>
</template>
This runs soomthly, and with no hesitations. And renders with no styling as this screenshots depicts:
Hope this answers what you want to achieve.

AngularJS- Items within ng-repeat not updating on variable update

I have an array called altSegments, and based on $scope.firstSeg or $scope.lastSeg I'd like to display different parts of that same array. In most cases I change the altSegments array alltogether and it updates fine, but when I go from the same altSegments array to the same array but change the $scope.firstSeg and $scope.lastSeg it doesn't update properly.
I suspect it has something to do with altSegments not having changed and therefore AngularJS deciding that it's not worth it to go over the code again and re-display. How would I get around this?
<li ng-repeat="altseg in altSegments">
<!-- For multiflight home to first -->
<div ng-show="{{firstSeg}}" ng-repeat="flights in altseg.segment_details_1.leg_details">
<p class="small dark">
<strong>Flight:</strong> {{flights.Carrier}} {{ flights.FlightNumber}}
</p>
<p class="small dark">
<strong>Departure:</strong> {{flights.OriginName}} | {{flights.DepartureTime | splitDT }}
</p>
<p class="small dark">
<strong>Arrival:</strong> {{flights.DestinationName}} | {{flights.ArrivalTime | splitDT }}
</p>
</div>
<!-- For multiflight last to home -->
<div ng-show="{{lastSeg}}" ng-repeat="flights in altseg.segment_details_2.leg_details">
<p class="small dark">
<strong>Flight:</strong> {{flights.Carrier}} {{ flights.FlightNumber}}
</p>
<p class="small dark">
<strong>Departure:</strong> {{flights.OriginName}} | {{flights.DepartureTime | splitDT }}
</p>
<p class="small dark">
<strong>Arrival:</strong> {{flights.DestinationName}} | {{flights.ArrivalTime | splitDT }}
</p>
</div>
ng-show is an angular directive and evaluates angular code;
Therefore; you do not need : ng-show="{{firstSeg}}"
Remplace with : ng-show="firstSeg"
See full documentation of ng-show here: https://docs.angularjs.org/api/ng/directive/ngShow
it looks like you are using ng-show="{{firstSeg}}" this should be ng-show="firstSeg" ..
If still doesn't work,
Try to update the data from controller side in $scope.apply() ...
e.g :-
$scope.apply(function(){
list = updated_list; // put your updation of list here
});

Assign value to dynamically created scope variables

I'm trying to create a means to toggle dynamically created rows of information. I've tried using ng-init, and then passing it to a function, but I'm screwing up somewhere and I can't seem to wrap my head around how or if this is possible. The gap, I believe, is in getting the concatenated scope variable to be referenced elsewhere. I'm using Bootstrap 3 and AngularJS 1.5.
The HTML:
<div class="row" data-ng-repeat="equipment in task.equipment">
<div class="col-md-12">
<h4 class="green-text">
{{ equipment.equipId }}
<small class="green-text">
<i class="glyphicon"
data-ng-class="{'glyphicon-triangle-bottom': field{{ $index }}, 'glyphicon-triangle-right': !field{{ $index }}}"
data-ng-init="equipment['field' + $index] = true"
data-ng-click="toggleTaskEquip('field{{ $index }}')">
field{{ $index }}: I WANT THIS TO WORK</i>
</small>
</h4>
</div>
<div data-ng-show="field{{ $index }}">
...stuff here...
</div>
</div>
The JS:
$scope.toggleTaskEquip = function(toggleBool)
{
if (toggleBool === true)
$scope.isTaskEquipOpen = false;
else if (toggleBool === false)
$scope.isTaskEquipOpen = true;
};
If I understand the problem correctly, you want to be able to toggle the boolean created in the ng-init with a click.
I think you need this:
<div class="container-fluid">
<div ng-controller="MyCtrl">
<div class="row" data-ng-repeat="equipment in task.equipment">
<div class="col-md-12">
<h4 class="green-text">
{{equipment.equipId}}
<small class="green-text">
<i class="glyphicon"
data-ng-class="{'glyphicon-triangle-bottom': isVisible, 'glyphicon-triangle-right': !isVisible}"
data-ng-init="isVisible = true"
data-ng-click="isVisible = !isVisible">I WANT THIS TO WORK</i>
</small>
</h4>
</div>
<div data-ng-show="isVisible">
...stuff here...
</div>
</div>
</div>
</div>
You don't even need the function toggleTaskEquip on the $scope.
JSFiddle here.
ng-repeat creates a new scope for each template instance, so you can just create a separate isVisible for each equipment with isVisible = true in the ng-init.

Categories

Resources