I have a data store in a variable requestData which I want to use in populating the notification component view
requestData = {
"bookId": "5e5bb8ac59441513cca2e64c",
"bookTitle": "Take Great Photographs",
"request": [
{
"_id": "5e21d286b3ee501ebc39d899",
"firstname": "Ibaji",
"lastname": "Offor"
},
{
"_id": "5e2c5a20aff8434278639ff5",
"firstname": "Cheksmate",
"lastname": "Fidelis"
}
]
}
the view is supposed to look like the image below after the view has been populated with the above data
this is the code for the notification.component.html
<section class="main profile " my-books>
<div class="profile-header">
<h1>Notifications</h1>
</div>
<div class="book-list-header">
<h2>Showing Recent Notifications</h2>
<!-- <span>(Confirm by click the recieved button once book is returned)</span> -->
</div>
<div history *ngFor="let item of requestData">
<a [routerLink]="['details']">
<h3>9:15 AM | Mon, Feb 11, 2019</h3>
<div class="first">
<span>
<span class="image-cropper" style=" width: 36px; height: 36px;">
<img src="assets/images/avatar.jpg" alt="profile image" avatar>
</span>
<h4>{{item.request.firstname}} {{item.request.lastname}}</h4>
</span>
<span>
<h3>Book Title</h3>
<h4>{{item.bookTitle}}</h4>
</span>
</div>
</a>
</div>
</section>
Any attempt to help will be really appreciated
From the above mentioned requestData, the data is an object, and you are trying to use *ngFor="let item of requestData" on object which is invalid and it should throw the error.
Reason is array's are only the iterables in *ngFor.. So change your above code with the following,
<div history *ngFor="let item of requestData.request">
<a [routerLink]="['details']">
<h3>9:15 AM | Mon, Feb 11, 2019</h3>
<div class="first">
<span>
<span class="image-cropper" style=" width: 36px; height: 36px;">
<img src="https://www.w3schools.com/howto/img_avatar.png" style="width:50px; border: 2px solid #fff; border-radius:50%" alt="profile image" avatar>
</span>
<h4>{{item.firstname}} {{item.lastname}}</h4>
</span>
<span>
<h3>Book Title</h3>
<h4>{{requestData.bookTitle}}</h4>
</span>
</div>
</a>
</div>
Here requestData.request is an array so you need to iterate only this array using *ngFor and replace, {{item.request.firstname}} with {{item.firstname}} and so on for all related data.
To assign the value of bookTitle, you need to use like this directly from the object,
{{requestData.bookTitle}}
Working stackblitz example here..
Related
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.
Came across this weird behaviour when developing a dynamic FAQ-page where the answer is displayed after the question is clicked on.
When the boolean that controls the opening/closing of the answer is stored in an object the view is updated instantly and the answer is displayed or hidden instantly. However, when it is stored in a list, the answer isn't displayed or hidden until the view is updated in some other way.
Here's a fiddle displaying the problem https://jsfiddle.net/masterofginseng/ffqt9n4y/6/
The HTML
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<div id="faq">
<div>This responds immediately</div>
<hr>
<div v-for="questionObj in questions1">
<div style="cursor: pointer;" #click="questionObj.open = !questionObj.open">
{{ questionObj.question }}
</div>
<div style="color: blue;" v-show="questionObj.open">{{ questionObj.answer }}</div>
</div>
<br>
<br>
<div>This doesn't respond until the view is updated in some other way (ex. by clicking on one of the questions above)</div>
<hr>
<div v-for="questionarray in questions2">
<div style="cursor: pointer;" #click="questionarray[2] = !questionarray[2]">
{{ questionarray[0] }}
</div>
<div style="color: blue;" v-show="questionarray[2]">{{ questionarray[1] }}</div>
</div>
</div>
And the javascript:
new Vue({
el: "#faq",
data: {
questions1: [{
question: "How big is it?",
answer: "very big",
open: false
}, {
question: "How small is it?",
answer: "very small",
open: false
}],
questions2: [
["How big is it?", "very big", false],
["How small is it?", "very small", false]
]
}
});
Due to limitations in Javascript, Vue can't detect changes of values in items with this syntax:
questionarray[2] = !questionarray[2]
See here: https://v2.vuejs.org/v2/guide/list.html#Array-Change-Detection
As explained in the link above, you have to use splice() instead:
questionarray.splice(2, 1, !questionarray[2])
This is because of list-rendering caveats
Try like this:
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<div id="faq">
<div>This responds immediately</div>
<hr>
<div v-for="questionObj in questions1">
<div style="cursor: pointer;" #click="questionObj.open = !questionObj.open">
{{ questionObj.question }}
</div>
<div style="color: blue;" v-show="questionObj.open">{{ questionObj.answer }}</div>
</div>
<br>
<br>
<div>This doesn't respond until the view is updated in some other way (ex. by clicking on one of the questions above)</div>
<hr>
<div v-for="questionarray in questions2">
<div style="cursor: pointer;" #click="myMethod(questionarray)">
{{ questionarray[0] }}
</div>
<div style="color: blue;" v-show="questionarray[2]">{{ questionarray[1] }}</div>
</div>
</div>
Script
new Vue({
el: "#faq",
data: {
questions1: [{
question: "How big is it?",
answer: "very big",
open: false
}, {
question: "How small is it?",
answer: "very small",
open: false
}],
questions2: [
["How big is it?", "very big", false],
["How small is it?", "very small", false]
]
},
methods:{
myMethod(questionArray){
this.$set(questionArray, 2, !questionArray[2]);
}
}
});
Here is the jsFiddle
Or a less verbose version as Linus borg answered:
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<div id="faq">
<div>This responds immediately</div>
<hr>
<div v-for="questionObj in questions1">
<div style="cursor: pointer;" #click="questionObj.open = !questionObj.open">
{{ questionObj.question }}
</div>
<div style="color: blue;" v-show="questionObj.open">{{ questionObj.answer }}</div>
</div>
<br>
<br>
<div>This doesn't respond until the view is updated in some other way (ex. by clicking on one of the questions above)</div>
<hr>
<div v-for="questionarray in questions2">
<div style="cursor: pointer;" #click="questionarray.splice(2, 1, !questionarray[2])">
{{ questionarray[0] }}
</div>
<div style="color: blue;" v-show="questionarray[2]">{{ questionarray[1] }}</div>
</div>
</div>
From Json API, data are read and displayed in the HTML.
User can also add custom articles which are stored as staticCards object inside script tag.
Now i wanted to find the HTML which is rendering the staticCards and add a class only to those articles.
How can i differentiate ? staticCards & Dynamiccards (directly rendered from JSON API)
var staticCards = [{
"cardLayout": "comms-double-image",
"cardLabel": "STORY",
"cardTitle": "households receive their share of $3.4 billion",
"cardDate": "05 April 2017",
"fileReference": "/content/dam/caas/newsroom/images/2016-09-15-newsroom-habib-spoons-banner-image.png",
"imageFocus": "image-centre-focus",
"cardLinkURL": "#",
"cardLinkDTM": "data-tracker-id=\"cards_1_1\" data-tracker-type=\"button\" data-tracker_ei=\"cards_1_1\" "
}]
<script type="text/x-handlebars-template" id="comms-double-image">
<article class="card card-double cvp comms image" data-tags="">
<a href="{{link}}" class="link" data-tracker-id="{{trackerId}}" data-tracker-type="{{trackerType}}" data-tracker_ei="{{trackerEI}}">
<div class="mobile-wrapper">
<div class="background-container" style="background-image : url({{image}});">
<h4 class="eta font-low-impact">{{label}}</h4>
</div>
<div class="content">
<time datetime="{{date-time}}">{{date-time}}</time>
{{#xif "this.title > 45"}}
<h3>{{title}}</h3>
{{else}}
<h3 class="delta">{{truncate title 55}}</h3>
{{/xif}}
<p class="cta">{{link-desc}}</p>
</div>
</div>
</a>
</article>
</script>
<div ng-app="appPage" ng-controller="appController">
<div class="nav">
<h1 class="logo">Todlio</h1>
<i class="icon setting" style="color:#fff;font-size:1.8em;;position:absolute;top:11px;right:12px;"/></i>
</div>
<div class="todo">
<div class="todo_column">
<div style="font-weight700;text-align:center; margin:20px;">
<a href="#/add" ng-click="addTodo()" class="ui basic button">
<i class="add square icon"></i>
Add
</a>
</div>
<ul>
<a href="#/"><li ng-href="#/" ng-click="detail($index)" ng-repeat="todo in todos">
<h3 ng-model="title">{{ todo.title }}</h3>
<h6>{{ todo.note_date }}</h6>
</li></a>
</ul>
</div>
<div class="todo_full">
<div class="todo_title" ng-scope="todo in todos">
<span><h1>{{ title }}</h1></span>
<span class="check">
<i style="font-size:2em;" class="square outline icon"></i>
<i class="write icon" style="font-size:1.8em;"></i>
</span>
</div>
<h4>Note:</h4>
<p class="todo_note">{{ note }}
</p>
</div>
</div>
</div>
Controller
app.controller("appController", function ($scope) {
$scope.todos = [
{title: "Call Vaibhav", note: "", note_date: ""},
{title: "Grocery", note: "Lemons, Apple and Coffee", note_date: ""},
{title: "Website design for Stallioners", note: "UI/UX on xyz#mail.com", note_date: ""},
{title: "Fill MCA form", note: "First search for all the colleges", note_date: "" }
];
$scope.detail = function(x){
$scope.todos.title = $scope.title;
$scope.todos.note = $scope.note;
};
I want to get the clicked list item title and the note attached to it to the different div below
Can anybody please help. Its a todo app the left half has the list to todos and the right half has a note attached to it or anything checked or not.
There are a few ways to do this. One easy way is as follows:
Define a $scope variable with a name like $scope.currentTodo.
In the repeat loop, the ng-click would set $scope.currentTodo=todo
This current variable will hold the todo object so you can use {{ $scope.currentTodo.title }} in place of {{title}}
Ditch the ng-scope
I want to display the json in a certain format with ng-repeat, but have had no success.
This is the structure:
<div ng-repeat="book in books">
{{book}}
<div ng-repeat="name in book.name" >{{name}} </div>
</div>
THis is the structure how I want it to display on the page:
Genre:
Sci-fic
Bookname1
Bookname2
Non sci-fic
Bookname3
Bookname4
This is the json sample:
[{"Genre":Sci-fic,"Name":"Bookname1"},
{"Genre":Sci-fic,"Name":"Bookname2"},
{"Genre":Non sci-fic,"Name":"Bookname3"},
{"Genre":Non sci-fic,"Name":"Bookname4"}]
http://jsfiddle.net/HB7LU/4053/
Should/Need to fix/restructure your JSON
$scope.books = [
{
"genre": "Sci-fic",
"titles" : ['Bookname1', 'Bookname2']
},
{
"genre": "Non sci-fic",
"titles" : ['Bookname3', 'Bookname4']
},
];
<div ng-repeat="book in books">
{{book.genre}}
<div ng-repeat="title in book.titles" >{{title}}</div>
</div>
Wouldn't it be easier if you format the json object that you receive like this?:
[{
"name": "Sci-Fic",
"names":["book 1","book 2"]
},
{
"name": "Another Genre",
"names":["book 1","book 2"]
}
]
Why don't u use ul and li? just another approach.
<ul>
<li ng-repeat="genre in books">
{{genre.name}}
<ul>
<li ng-repeat="name in genre.names">{{name}}</li>
</ul>
</li>
</ul>
Hope it helps :D
---Edit---
Sry, didn't see #Christopher Marshall answer while i was writing, its the same thing.
If the data MUST be in that format you can do this:
<div ng-app="app">
<div ng-controller="ParentCtrl">
<div data-ng-repeat="genre in data | orderBy:'Genre'">
<span data-ng-if="(data | orderBy:'Genre')[$index - 1].Genre != genre.Genre">
{{ genre.Genre }}
</span>
<div style="padding-left: 30px;">{{ genre.Name }}</div>
</div>
</div>
</div>
Here's a jsFiddle of it: http://jsfiddle.net/wgLnH/