angular repeat with template? - javascript

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.

Related

How an i change the pagination-control numbers(1 2 3) to other values like 'Anything1 Anything2 ...' using ngFor* on array in ngx-pagination?

Is it possible to replace ngx-pagination's 'pagination-control'? By defaults, page numbers are showing like '1 2 3'. And I want to replace them with a string value in the array.
<pagination-controls (pageChange)="pageChange($event)" class="my-pagination"></pagination-controls>
The above code will display pagination numbers like
I want to replace the number with values from an array [Anything1, Anything2, Anything3].
Here my data is fixed and I know my page number count and its details.
You need to have look at custom template of ngx-pagination. In customization part you may try to add your Anything before {{page.label}}.
Custom Template example can be found here. http://michaelbromley.github.io/ngx-pagination/#/custom-template
<pagination-template #p="paginationApi"
[id]="config.id"
(pageChange)="config.currentPage = $event">
<div class="custom-pagination">
<div class="pagination-previous" [class.disabled]="p.isFirstPage()">
<a *ngIf="!p.isFirstPage()" (click)="p.previous()"> < </a>
</div>
<div *ngFor="let page of p.pages" [class.current]="p.getCurrent() === page.value">
<a (click)="p.setCurrent(page.value)" *ngIf="p.getCurrent() !== page.value">
<span>Anything{{ page.label }}</span>
</a>
<div *ngIf="p.getCurrent() === page.value">
<span>Anything{{ page.label }}</span>
</div>
</div>
<div class="pagination-next" [class.disabled]="p.isLastPage()">
<a *ngIf="!p.isLastPage()" (click)="p.next()"> > </a>
</div>
</div>
</pagination-template>

How to dynamically add items and attributes to HTML with javascript or jQuery referencing items from a csv or json file

I am trying to make the following bit of code easier to maintain. I am not a web developer so bear with me. I think the following approach is appropriate.
I would like to dynamically add content and attributes to an html file using either javascript or jQuery. The items could reside in a .csv or .json (or something else?) file.
Given content like this
<div class="filtr-container">
<div class="col-12 col-sm-6 col-md-4 card filtr-item" data-category="cat-1" data-date="2018-02-09">
<div class="card-inner-border box-shadow">
<a href="address-1.html">
<img class="card-img-top rounded-top" src="./images/image-1.jpg" alt="img-2-alt">
</a>
<div class="card-body">
<h5 class="card-title">Title-1</h5>
<p class="card-text card-desc">
This is a description for title-1 content.
</p>
<a href="address-1.html">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
</a>
<p class="card-text">
<small class="text-muted">Last updated February 2, 2018</small>
</p>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 card filtr-item" data-category="cat-2, cat-3" data-date="2018-02-14">
<div class="card-inner-border box-shadow">
<a href="address-2.html">
<img class="card-img-top rounded-top" src="./images/image-2.jpg" alt="img-2-alt">
</a>
<div class="card-body">
<h5 class="card-title">Title-2</h5>
<p class="card-text card-desc">
Here is a long description for title-2 content.
</p>
<a href="address-2.html">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
</a>
<p class="card-text">
<small class="text-muted">Last updated February 14, 2018</small>
</p>
</div>
</div>
</div>
<!-- MANY MORE CARDS / ITEMS ... -->
</div> <!-- End of filtr-container -->
I think we could abstract the details into something like this (.csv)
item-id,title,description,categories,address,image,image-alt,update
1,Title-1,This is a description for title-1 content.,cat-1,address-1.html,image-1.jpg,img-1-alt,2018-02-09
2,Title-2,Here is a long description for title-2 content.,"cat-2, cat-2",address-2.html,image-2.jpg,img-2-alt,2018-02-14
What's a nice approach of attack for using `javascript` or `jQuery` to add this content from the `.csv` or `.json` file?
A few concerns:
The headers of the .csv will not match verbatim (e.g. <p class="card-desc"> aligns with the .csv header of description)
There could be embedded comma-separated items (e.g. item 2 has categories cat-2, cat-3 so it gets quotes " in the .csv -- maybe .json would better (?) or perhaps its a non-issue)
If possible can we reuse the date item for both data-date= and the final piece of text <small class="text-muted"> which converts the date into Last updated month-name-long, dd, yyyy instead of yyyy-mm-dd.
Some attributes are partial references (e.g. the src for an image is just the final part of the path; stated as image-1.jpg in the .csv not ./images/image-jpg).
To hopefully help make this feel less complicated, here's a picture with the highlighted elements that could be "referenced" from the .csv file.
To me this feels like:
Read in the .csv file.
For each item in the .csv file, append objects to $(".filtr-container") with the shell layout...
But I'm lost when it comes to the particulars or if that's an appropriate approach.
You seem to be searching for template parsing. You can find many libraries that will ease this burden. In its simplest form, template parses carry out the steps in the following code. If you don't need the flexibility, power, features, etc. from a template parser library or full framework, you should consider not including the thousands of lines of code if all you want to accomplish is what is shown below.
Since you mentioned both JSON and CSV I've included the code to parse both. I'll leave the AJAX and date formatting magic to you. I don't think I populate the ID either, but this shows that more data than template attributes will work fine.
let template = document.getElementById('card-template').innerHTML;
let container = document.querySelector('.filtr-container');
// Do some ajax magic to get csv file
let csv = `item-id,title,description,categories,address,image,image-alt,update
1,Title-1,This is a description for title-1 content.,cat-1,address-1.html,https://via.placeholder.com/75,img-1-alt,2018-02-09
2,Title-2,Here is a long description for title-2 content.,cat-2 cat-2,address-2.html,https://via.placeholder.com/75,img-2-alt,2018-02-14`;
let csvLines = csv.split("\n");
let csvHeaders = csvLines.shift().split(',');
csvLines.forEach(line => {
let parsed = template;
let props = line.split(',');
props.forEach((prop, idx) => {
parsed = parsed.replace('{{' + csvHeaders[idx] + '}}', props[idx]);
});
container.innerHTML = container.innerHTML + parsed;
});
let json = `[{
"item-id": "1",
"title": "Title-1",
"description": "This is a description for title-1 content.",
"categories": "cat-1",
"address": "address-1.html",
"image": "https://via.placeholder.com/75",
"image-alt": "img-1-alt",
"update": "2018-02-09"
}, {
"item-id": "2",
"title": "Title-2",
"description": "Here is a long description for title-2 content.",
"categories": "cat-2 cat-2",
"address": "address-2.html",
"image": "https://via.placeholder.com/75",
"image-alt": "img-2-alt",
"update": "2018-02-14"
}]`;
let data = JSON.parse(json);
data.forEach(col => {
let jParsed = template;
for (prop in col) {
jParsed = jParsed.replace('{{' + prop + '}}', col[prop]);
}
container.innerHTML = container.innerHTML + jParsed;
});
<div class="filtr-container">
<script type="template" id="card-template">
<div class="col-12 col-sm-6 col-md-4 card filtr-item" data-category="{{categories}}" data-date="{{date}}">
<div class="card-inner-border box-shadow">
<a href="{{address}}">
<img class="card-img-top rounded-top" src="{{image}}" alt="{{image-alt}}">
</a>
<div class="card-body">
<h5 class="card-title">{{title}}</h5>
<p class="card-text card-desc">
{{description}}
</p>
<a href="{{address}}">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
</a>
<p class="card-text">
<small class="text-muted">Last updated {{update}}</small>
</p>
</div>
</div>
</div>
</script>
This post might help you parse your CSV document. If your data lives in a JSON, you can use JSON.parse
Once you properly retrieved and parsed your data, it's a matter or rendering it to the DOM.
You can do it using the standard javascript library, JQuery or frameworks such as React or VueJS

Rendering ng-content as html - Angular 2

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>

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
});

How to hide "div" from js, which use information from spring?

In my website, on UI I show information from DB, use "spring". I want to make, if items quantity equals 0, then I hide this "div". If items quantity more than 0, then I show "div"
In html I recive from spring:
<div th:each="viewAvailableWhisky : ${viewAvailableWhisky}">
<div class="tilt pic" id="ShowHide">
<a th:href="#{~/buySelectedWhisky(nameBuyWhiskey=${viewAvailableWhisky.id})}" >
<img th:attr="src=${viewAvailableWhisky.photo}" id="photoId" width="150" height="250"/>
<div>
<b> <span th:text="${viewAvailableWhisky.nameWhisky}">Name</span></b>
</div>
<div>
<b> Quantity: <span th:text="${viewAvailableWhisky.quantityWhisky}"
>quantityWhisky</span>piece</b>
<input type="hidden" id="quantity" th:value="${viewAvailableWhisky.quantityWhisky}"/>
</div>
<div>
<b> $ <span th:text="${viewAvailableWhisky.price}">Price</span></b>
</div>
</a>
</div>
</div>
I think, it's better to make the with use a JS. But I don't understand how, I try use this code. This code can't catch
quantity of goods of each item from a DB
$(document).ready ( function(){
var quantityItems = $("#quantity").val();
console.log(quantityItems);
if(quantityItems>0){
$("#ShowHide").show();
}
else {
$("#ShowHide").hide();
}
});
The th:if="${condition}" attribute is used to display a section of the view if the condition is met.
<span th:if="${student.gender} == 'M'" th:text="Male" />
Furthemore, thymeleaf offers option to display certain section if given condition is NOT met using th:unless
<span th:unless="${student.gender} == 'M'" th:text="Female" />
(For further info see This tutorial or this intro to Thymeleaf conditionals)
To display certain content only if user has specific role, use spring security's integration with thymeleaf.
<div sec:authorize="hasRole('ROLE_ADMIN')">
This content is only shown to administrators.
</div>
More info about Spring Security integration here.

Categories

Resources