Binding List Item Names to Images - angularjs

I want to list the names of the items in the module (name). I then want to click the name and have the corresponding image load. The first image should load automatically. Tried following this question to make work, but it's related to thumbnail pics. I believe i'm missing some code in the ng-repeat section. Thx!
How to bind the src of an image to ng-model and extract it in Angular?
HTML
<div ng-controller ="DemoController as main">
<div>
<img ng-src="{{selectedImg.src}}" />
</div>
<ul>
<li ng-repeat="cat in main.cats">
<img ng-src="{{cat.images[0].name}}"
ng-click="selectedImg.src = cat.images[0].name"/>
</li>
</div>
</ul>
</div>
JS
angular.module('myApp',[]);
angular.module('myApp').controller('DemoController',
['$scope',function($scope) {
this.cats = [
{
name: 'Fluffy',
images: 'images/Fluffy.jpeg'
},
{
name: 'Tabby',
images: 'images/tabby.jpeg'
}
];
$scope.selectedImg = {};
}]);

Initially you want to set first image, then do it manually inside controller.
$scope.selectedImg = this.cats.image[0];
and also change ng-click to assign whole object of cat.images[0]
ng-click="selectedImg= cat.images[0]"

Related

Angular 4 ngFor looping through array of objects with HTML

I'm using Angular 4 and I want to loop through an array of objects and display the information in Angular Material grids tiles. My html (app.component.html) looks a bit like this
<div class="list" *ngIf="listContacts == true">
<mat-grid-list cols="3" rowHeight="2:1" *ngFor="let i of contacts">
<mat-grid-tile>
<div class="card">
<img src={{contacts[i].image}} alt="picture">
<h2>{{contacts[i].name}}</h2>
</div>
</mat-grid-tile>
</mat-grid-list>
</div>
And contacts is an array of objects inside app.component.ts. There are NINE objects within contacts but for simplicity I've only put two here.
export class AppComponent {
// array of contact objects
contacts = [
{
name: "Robbie Peck",
image: "src/assets/robbie.jpg",
},
{
name: "Jenny Sweets",
image: "src/assets/jenny.jpg",
},
...
]
}
So rather than have nine different 's appear, each with their own information (looping i through contacts), I only have one and the console shows an error where it doesn't recognize contacts[i]. What have I done wrong? How can I get the to have 9 's, each with name and image i within the contacts object in the typescript (app.component.ts) file?
You don't have to pass the index, you can just use the variable i and access i.image and i.name
<mat-grid-list cols="3" rowHeight="2:1" *ngFor="let i of contacts" >
<mat-grid-tile>
<div class="card">
<img src={{i.image}} alt="picture">
<h2>{{i.name}}</h2>
</div>
</mat-grid-tile>

How do i select sibling Elements in AngularJS

I got a 4-5 rows of div ... when i click on any row it should change the selected one and color it as red and rest all to black ... i am using below logic but not working for me giving error as angular.min.js:117 TypeError: angular.element(...).siblings is not a function
Do i need to include jQuery file?
Can i do it without including jQuery file?
plz help
$scope.divClick = function($event){
console.log($event.target);
/* below siblings function not working for me*/
angular.element(event.target).siblings().css('color','black');
angular.element(event.target).css('color','red');
};
<div ng-controller="homeController">
<h1>Welcome</h1>
<div ng-repeat="x in json" ng-click="divClick($event)">
Name: {{x.Name}}<br/> City: {{x.City}}<br/> Country: {{x.Country}}<br/><br/>
</div>
</div>
Set your default color to be black.
<div ng-repeat="x in json" ng-class="{setRed: $index == value}" ng-click="divClick($index)">
Give CSS style to your class setRed.
Your controller function:
$scope.divClick = function(index){
$scope.value = index;
};
In general it's bad idea to modify DOM right from your controllers.
It's better to use scope or model properties and make decisions what class to apply based on them.
<div ng-repeat="x in json" ng-click="select($index)" ng-class="{'selected': $index == selectedIndex}">
Name: {{x.Name}}<br/>
City: {{x.City}}
Then you just have to update selectedIndex in you click handler
$scope.select = function(x) {
$scope.selectedIndex = x;
};
Fully working solution is here
https://jsfiddle.net/kvtcw8y6/4/
Other way is to have isSelected property on you model and update it accordingly.

AngularJS Expression Not Working Correctly in ng-src with JSF

The angularjs module. the products array contains 2 product objects that will be added as a property of the controller.
(function () {
var app = angular.module('myApp', []);
var products = [
{
title: "Dummy Title 1",
description: "Dummy Description 1",
image: "dummy_image_1.jpg"
},
{
title: "Dummy Title 2",
description: "Dummy Description 2",
image: "dummy_image_2.jpg"
}
];
app.controller('myController', function () {
this.products = products;
});
})();
The JSF Page, if I remove images/{{product.image}} with the actual image file name such as images/dummy_image_1.jpg, the images are displayed, but if I use angularjs expression instead, then nothing is shown. Please note that other expressions in the loop work besides {{product.image}}. If I add {{product.image}} somewhere else, then it displays the filename correctly, but used in ng-srs, it prints nothing if I view the html. I don't know why it is.
<h:form>
<div class="container" ng-controller="myController as controller">
<div class="row">
<div class="col-sm-offset-10">
Hello <b><h:outputText value="#{user.userName}"/></b><br/>
<h:commandLink action="cart" value="Cart"/>
</div>
</div>
<hr/>
<div ng-repeat="product in controller.products">
<div class="row">
<div class="media">
<div class="media-left">
<img class="media-object" ng-src="images/{{product.image}}"/> <!--If I replace that expression with a the image file name, it shows the image -->
</div>
<div class="media-body">
<h4 class="media-heading">{{product.title}}</h4>
<span class="caption">{{product.description}}</span><br/>
<h:commandLink action="cart" value="Add to cart"/>
</div>
</div>
</div>
</div>
</div>
</h:form>
Sometimes using interpolation {{}} won't evaluate & update value of attribute (most often this happens in IE). The way to do this is using ng-src directive with interpolation {{}} this will add src to the img tag after evaluation interpolation directive.
Markup
<div class="media-left">
<img class="media-object" ng-src="{{'images/'+product.image}}"/>
<!--If I replace that expression with a the image file name, it shows the image -->
</div>
Alternative
The other way would be using ng-attr directive which add attribute with evaluated value. This will ensure you everytime that the interpolated value has been assigned to the attribute mentioned it in ng-attr (the part after ng-attr is considered as attribute to be added, suppose we have ng-attr-value="{{someVar}}" then angular will evaluate someVar and then assign that value to value attribute on that element.
Markup
<div class="media-left">
<img class="media-object" ng-attr-src="{{'images/'+product.image}}"/>
<!--If I replace that expression with a the image file name, it shows the image -->
</div>

Live search in AngularJS: updating the results

I want a live search: the results are queried from web api and updated as the user types.
The problem is that the list flickers and the "No results" text appears for a fraction of second, even if the list of results stays the same. I guess I need to remove and add items with special code to avoid this, calculating differences between arrays, etc.
Is there a simpler way to avoid this flicker at least, and probably to have possibility to animate the changes?
It looks like this now:
The html part is:
<div class="list-group">
<a ng-repeat="test in tests track by test.id | orderBy: '-id'" ng-href="#/test/{{test.id}}" class="list-group-item">
<h4 class="list-group-item-heading">{{test.name}}</h4>
{{test.description}}
</a>
</div>
<div ng-show="!tests.length" class="panel panel-danger">
<div class="panel-body">
No tests found.
</div>
<div class="panel-footer">Try a different search or clear the text to view new tests.</div>
</div>
And the controller:
testerControllers.controller('TestSearchListCtrl', ['$scope', 'TestSearch',
function($scope, TestSearch) {
$scope.tests = TestSearch.query();
$scope.$watch('search', function() {
$scope.tests = TestSearch.query({'q':$scope.search});
});
}]);
You should use ng-animate module to get the classes you need for smooth animation. For each ng-repeat item that's moved, added, or removed - angular will add specific classes. Then you can style those classes via CSS or JS so they don’t flicker.
An alternative way of doing what you require is to use the angular-ui bootstrap Typeahead component (check at the bottom of the post). It has a type-ahead-wait property in milliseconds and also a template url for customising it.
<div ng-app>
<div ng-controller="MyController">
<input type="search" ng-model="search" placeholder="Search...">
<button ng-click="fun()">search</button>
<ul>
<li ng-repeat="name in names">{{ name }}</li>
</ul>
<p>Tips: Try searching for <code>ann</code> or <code>lol</code>
</p>
</div>
</div>
function MyController($scope, $filter) {
$scope.names = [
'Lolita Dipietro',
'Annice Guernsey',
'Gerri Rall',
'Ginette Pinales',
'Lon Rondon',
'Jennine Marcos',
'Roxann Hooser',
'Brendon Loth',
'Ilda Bogdan',
'Jani Fan',
'Grace Soller',
'Everette Costantino',
'Andy Hume',
'Omar Davie',
'Jerrica Hillery',
'Charline Cogar',
'Melda Diorio',
'Rita Abbott',
'Setsuko Minger',
'Aretha Paige'];
$scope.fun = function () {
console.log($scope.search);
$scope.names = $filter('filter')($scope.names, $scope.search);
};
}

LocalStorage of contenteditable content (AngularJS)

I have a little AngularJS tool that inserts cards. My goal is to store them locally. I worked that out for the cards array, but not for the card content, that is "contenteditable"
Can u help me with this and give me some best practice solutions?
Here's a Plunker (in JS) for that (the red big button deletes the localStorage. Be sure to have a wide window open): http://plnkr.co/edit/SlbWZ5Bh62MDKWViUsMr
This is my code (with CoffeeScript. For JS see the Plunker above):
This is the markup for the user input
<div class="card card-{{ card.color }}">
<header>
<p class="points" contenteditable></p>
<p class="number" contenteditable>#</p>
<h2 class="customerName" contenteditable>{{ card.customer.name }}</h2>
<h3 class="projectName" contenteditable>Project Name</h3>
</header>
<article>
<h1 class="task" contenteditable>Title</h1>
<p class="story" contenteditable>Description</p>
</article>
<footer>
<div class="divisions">
<p class="division"></p>
<button ng-click="deleteCard()" class="delete">X</button>
</div>
</footer>
</div>
<div class="card card-{{ card.color }} backside">
<article>
<h2 class="requirement">Requirements</h2>
<p contenteditable></p>
</article>
</div>
Here you see my localStorage setup for the cards array that was in my controller above:
Card = (#color, #customer) ->
$scope.cards = []
json = localStorage.getItem "cards"
getCards = JSON.parse(json) if json?
$scope.cards = $scope.cards.concat getCards if getCards?
$scope.reset = ->
localStorage.clear()
$scope.save = ->
cards = []
for card in $scope.cards
cards.push
color: card.color
customer:
name: card.customer.name
localStorage.setItem "cards", JSON.stringify(cards)
$scope.addCardRed = (customer) ->
$scope.cards.push new Card("red", customer)
$scope.save()
How do I store the user inputs in the different fields in localStorage? I heards something about serialization, but I don't know what it means in my case!
Thank you so much in advance!
You can use the ng-model directive with any contenteditable field, just like you would do with an input or textarea. so, instead of trying to use the {{...}} braces to bind your model to your view, you should just use ng-model, and then just treat your editable DOM elements as if they were fields in a form.
For example, in your view :
<header ng-repeat="card in cards">
<!-- These are just sample fields for the card object, but you can modify them -->
<p class="points" contenteditable ng-model="card.points"></p>
<p class="number" contenteditable ng-model="card.number"></p>
<h2 class="customerName" contenteditable ng-model="card.customer.name"></h2>
<h3 class="projectName" contenteditable ng-model="card.projectName"></h3>
</header>
And then in your controller you would attach the model to the $scope as you've already done using $scope.cards = $scope.cards.concat getCards if getCards?. This will two-way bind your cards model to your controller's scope.
And then in your controller, to mirror the model data in LocalStorage, you can do it yourself using something like this:
In your controller:
....
// for example's sake
$scope.cards = [ // the cards array would look something like this
{points: 0, number: 5, customer: {name: 'bob'}, projectName: 'myProj1'},
{points: 1, number: 6, customer: {name: 'joe'}, projectName: 'myProj2'},
{points: 2, number: 7, customer: {name: 'bill'}, projectName: 'myProj3'},
{points: 3, number: 8, customer: {name: 'jerry'}, projectName: 'myProj4'}
];
....
// listen to all changes to the $scope.cards array
$scope.$watch('cards', function(newVal){
var str = angular.toJson(newVal); // serialize the cards array into a string
// NOTE: the 'someKey' string is the key that you'll use later to retrieve it back
window.localStorage['someKey'] = str; // store the serialized string in localStorage
}, true);
....
In the above example, the angular.toJson(newVal) will take the newVal variable (which is just a reference to the "recently updated" cards array), and will serialize it into JSON string (ie angular.toJson just basically wraps the native JSON.stringify() method). In order to put a javascript object into LocalStorage it must be serialized to a string because you can only put primitives as the value in a LocalStorage key.
So the newVal will get put into localStorage looking something like this:
"[{"points":0,"number":5,"customer":{"name":"bob"},"projectName":"myProj1"},{"points":1,"number":6,"customer":{"name":"joe"},"projectName":"myProj2"},{"points":2,"number":7,"customer":{"name":"bill"},"projectName":"myProj3"},{"points":3,"number":8,"customer":{"name":"jerry"},"projectName":"myProj4"}]"
And then later (whenever you need it) you can retrieve the cards array from localStorage again using the following:
var str = window.localStorage['someKey'];
$scope.cards = angular.fromJson(str);
Or you can use a library to do the serialization/saving like this one: https://github.com/grevory/angular-local-storage. I've never used it but it does exactly what you want it to.
Hopefully that helps clarify things a bit.
UPDATE: This is beyond the scope of this question, but since you asked. It sounds like your'e not grasping the concept of the ng-repeat and ng-model directives. These are probably the two most famous (and most widely used) directives in Angular. By combining these two directives (as in the view example above), it will automatically keep your model (ie $scope.cards) and your view (ie <header>) in sync as users edit the data in your view. ng-repeat will "automagically" create a new header element for every card in your cards array (hence the ng-repeat="card in cards"). So as new cards get added or cards are removed from the cards array, Angular will add or remove <header> elements as needed. Then, using the contenteditable and ng-model directives, Angular will bind the content of those editable DOM elements to the values for each card. Typically ng-model is used with form elements (ie inputs/textareas/selects) but it can also be used for any editable field. Think of your editable elements as if they were an input, and then take a good long look at the <input ng-model="" /> example from the angular docs found here. That should help.
Now I know what confused me!
The Two-Way-Binding of my card properties didn't work with contenteditable. I removed each contenteditable attribute and instead of p, h2 and so on I replaced the tags with input tags. That worked fine for me.
Thank you for your patience and your great explanations, #tennisagent! And for the $watch hint! I am really grateful for your help :)
That's my solution for the Controller:
angular.controller 'CustomerController', ($scope) ->
$scope.customers = [
{ name: "name1" }
{ name: "name2" }
{ name: "name3" }
]
$scope.cards = []
Card = (#color, #customer, #points, #number, #projectName, #story) ->
$scope.$watch 'cards', ((newValue) ->
string = angular.toJson(newValue)
localStorage["cards"] = string
), true
json = localStorage["cards"]
parsedCards = angular.fromJson(json) if json?
$scope.cards = $scope.cards.concat parsedCards if parsedCards?
$scope.reset = ->
localStorage.clear()
sessionStorage.clear()
$scope.cards = []
$scope.addCardRed = (customer) ->
$scope.cards.push new Card("red", customer)
That's my solution for the Markup:
<div class="card card-{{ card.color }}">
<header>
<input class="points" contenteditable ng-model="card.points"></input>
<input class="number" placeholder="#" contenteditable ng-model="card.number"></input>
<input class="customerName" contenteditable ng-model="card.customer.name"></input>
<input class="projectName" placeholder="Projekt" contenteditable ng-model="card.projectName"></input>
</header>
<article>
<input class="task" placeholder="Titel" contenteditable ng-model="card.task"></input>
<textarea class="story" placeholder="Story" contenteditable ng-model="card.story"></textarea>
</article>
<footer>
<div class="divisions">
<p class="division"></p>
<button ng-click="deleteCard()" class="delete">X</button>
</div>
</footer>
</div>
<div class="card card-{{ card.color }} backside">
<article>
<h2 class="requirement">Requirements</h2>
<textarea class="requirements" placeholder="Aspects" contenteditable ng-model="card.requirements"></textarea>
</article>
</div>

Resources