How to display custom layout with ng-repeat - angularjs

Edit: My original question was not good enough, so edited it now.
Hi Im new to angular and im trying to display a custom Layout
for my data list with ng-repeat. I cant use ng-class, as I would like to display diffent model vaules too.
I implemented a function in my controller, that calculates true or false according to my desired design. Then im trying to use ng-if to display my desired HTML with the data. The way I implemeted seems a bit awkward, especially if the layout gets more complicated, is there a better way to achieve this behaviour?
Here a sample: http://plnkr.co/edit/puE7OE?p=info
Controller:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.eventModels=[{name:'event1', description: 'description1'}, {name:'event2', description: 'description2'},
{name:'event3', description: 'description3'}, {name:'event4', description: 'description4'},
{name:'event5', description: 'description5'}, {name:'even6', description: 'description6'}, {name:'even7', description: 'description7'}];
var counter = 0;
this.isLarge = false;
$scope.isLargeContainer = function() {
if(counter === 0) {
this.isLarge = true;
counter++;
} else {
this.isLarge = false;
if(counter === 2) {
counter = 0;
} else {
counter++;
}
}
};
});
View:
<body ng-controller="MainCtrl">
<section ng-repeat="(key, eventModel) in eventModels" >
<div ng-init="isLargeContainer(eventModel)"></div> <!--the only way I found to call a function within the repeat directive.-->
<div ng-if="isLarge">
<!--Display large content -->
<p class='large'>my large event: {{eventModel.name}}, {{eventModel.description}}</p>
</div>
<div ng-if="!isLarge">
<!--Display content small-->
<p class='small'>{{eventModel.name}}</p>
</div>
</section>
</body>
Thanks a lot in advance!
Im using Anguler 1.3.3

I just solved your problem. You can find the solution here:
http://jsfiddle.net/anasfirdousi/46saqLmw/
Here is the HTML
var myApp = angular.module('myApp',[]);
function myController($scope){
$scope.msg = 'Learning Angular JS ng-class Directive';
$scope.menu = [ 'Menu Item 1','Menu Item 2','Menu Item 3','Menu Item 4'];
}
.large {
font-size:16px;
}
body{
font-size:12px;
}
<div ng-app="myApp" ng-controller="myController">
{{ msg }}
<ul>
<li ng-repeat="(key, value) in menu" ng-class="{'large': $index==2 }"> {{ value }} </li>
</ul>
</div>
In your case, you actually have to use ng-class with expressions. The class is applied only if the condition holds true.

http://jsfiddle.net/anasfirdousi/cuo6cn3L/
Check the above link. This is another example if you want to do it on any specific condition. You can call a function which checks a condition and returns either a true or a false rather than using an inline expression.
var myApp = angular.module('myApp',[]);
function myController($scope){
$scope.msg = 'Learning Angular JS ng-class Directive';
$scope.menu = [ 'Menu Item 1','Menu Item 2','Menu Item 3','Menu Item 4'];
$scope.CheckSometing = function(v){
if(v=='Menu Item 3'){
return true;
}
else
{
return false;
}
}
}
.large {
font-size:16px;
}
body{
font-size:12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
{{ msg }}
<ul>
<li ng-repeat="(key, value) in menu" ng-class="{'large': CheckSometing(value) }"> {{ value }} </li>
</ul>
</div>

Related

Filter the object in the array by dynamic filter - AngularJS

Right now my data structure is like
product = [{att1:'2',att2:'red',att3:'gold'},
{att1:'1',att2:'blue',att3:'wood'},
{att1:'2',att2:'green',att3:'plastic'},
{att1:'1',att2:'red',att3:'plastic'}]
And I have a filter on the web page, it has three parts: att1, att2, att3. The user doesn't have to choose options for every part.
For filter att1 it has 2 options: "1" and "2".
Filter att2 it has 2 options: "red" "blue" and "green"
Filter att3 it has 3 options: "gold", "wood" and "plastic".
I can get the options that are selected. For example:
{att1:['2'],att3:['gold','plastic']} or {att1:['1']}
My question is, how do I use product.filter to filter the product data?
Thanks!
You can use a custom filter function which is easy to use, I used att1 but you can expand it to all fields:
var app = angular.module("MyApp", []);
app.controller("MyCtrl", function($scope) {
$scope.products = [{att1:'2',att2:'red',att3:'gold'},
{att1:'1',att2:'blue',att3:'wood'},
{att1:'2',att2:'green',att3:'plastic'},
{att1:'1',att2:'red',att3:'plastic'}];
$scope.filterFunction = function(element) {
return element.att1.match(/^Ma/) ? true : false;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="MyCtrl">
<form class="form-inline">
<input ng-model="query" type="text"
placeholder="Filter by" autofocus>
</form>
<ul ng-repeat="product in products | filter:query">
<li>{{product}}</li>
</ul>
<ul ng-repeat="product in products | filter:filterFunction">
<li>{{product}}</li>
</ul>
</div>
</div>
I banged out the particular logic for this one, using the three nested loops isn't super great but it does do the job. I'm sure you can optimize this further by using some maps or something but I just went with the get it done by brute force approach :)
angular.module('myApp',[])
.service('ProductService', function(){
return {
products:[
{att1:'2',att2:'red',att3:'gold'},
{att1:'1',att2:'blue',att3:'wood'},
{att1:'2',att2:'green',att3:'plastic'},
{att1:'1',att2:'red',att3:'plastic'}
]
}
})
.controller('TestCtrl', function(ProductService){
this.ProductService = ProductService;
this.filterObject1 = {att1:['2'],att3:['gold','plastic']};
this.filterObject2 = {att1:['1']};
})
.filter('productFilter', function(){
return function(input,filterObj){
if(!filterObj){
return input;
}
var newArray = [];
var filterKeys = Object.keys(filterObj);
for(var i=0;i<input.length;i++){
var curElement = input[i];
innerLoops:
for(var j=0;j<filterKeys.length;j++){
var curKey= filterKeys[j];
for(var k=0;k<filterObj[curKey].length;k++){
var curFilterValue = filterObj[curKey][k];
if(curElement[curKey].match(curFilterValue)){
//We found a match keep this element and move on to checking the next one by breaking out of the inner loops that are checking particular keys/values
newArray.push(curElement);
break innerLoops;
}
}
}
}
return newArray;
};
})
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.js"></script>
<div ng-app="myApp" ng-controller="TestCtrl as ctrl">
Unfiltered
<div ng-repeat="product in ctrl.ProductService.products|productFilter">
{{product}}
</div>
<hr/>
<strong>Filtered with: {{ctrl.filterObject1}}</strong>
<div ng-repeat="product in ctrl.ProductService.products|productFilter:ctrl.filterObject1">
{{product}}
</div>
<hr/>
<strong>Filtered with {{ctrl.filterObject2}}</strong>
<div ng-repeat="product in ctrl.ProductService.products|productFilter:ctrl.filterObject2">
{{product}}
</div>
</div>

angularjs pass index through directive

I need help with this code.
<ul ng-init="round = 'round'">
<li ng-repeat="mesa in mesas" ng-click="selected($index)">
<div id="{{round}}"> </div>
MESA {{mesa}}
</li>
</ul>
$scope.selected = function ($index){
$scope.index.round = 'round1';
}
I need that only the li that is being clicked to change the css name, but instead it change all of the li's that I have listed.
You're initializing the variable round outside of the repeat loop, so the expression {{round}} will always point to that singular, shared variable. If you update it once, it updates for all children of the ng-repeat.
If I understand your question, you're trying to change the CSS class for the div inside the repeat, correct? What you can do in that case is store the selected index on the controller's scope, and check that value against the index inside the loop
<ul>
<li ng-repeat="mesa in mesas" ng-click="select($index)">
<div class="round1" ng-if="selectedIndex === $index"> </div>
<div class="round" ng-if="selectedIndex !== $index"> </div>
MESA {{mesa}}
</li>
</ul>
$scope.select = function ($index){
$scope.selectedIndex = $index;
}
You could also use ng-class instead of ng-if depending on how your CSS is structured, but the idea is the same.
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<style type="text/css">
.round {
background: green;
}
.round1 {
background: blue;
}
</style>
</head>
<body ng-controller="controller">
<ul>
<li ng-repeat="mesa in mesas" ng-click="selected($index)" ng-class="[index[$index].round]">
<div id="{{ index[$index].id }}"> </div>
MESA {{ mesa }}
</li>
</ul>
</html>
<script type="text/javascript">
angular.module('app', [])
.controller('controller', controller);
controller.$inject = ['$scope'];
function controller($scope) {
$scope.mesas = ['1', '2', '3'];
$scope.index = [{
id: '1',
round: 'round'
}, {
id: '2',
round: 'round'
}, {
id: '3',
round: 'round'
}];
$scope.selected = function($index) {
$scope.index[$index].round = $scope.index[$index].round === 'round' ? 'round1' : 'round';
}
}
</script>
I think is what u looking for¿?

Ionic , angular , Show or disable a component within controller

I have the following code. The following code displays 5 tag item.
What I wanted to do is when user click on any of the tag item, it will be hidden or blank-out. But when I clicked on any of it, all the tag item got blanked out instead of the item I selected. Can anyone give me some idea ?
<div data-ng-repeat="x in mylist">
<a class="button" ng-click="hideme();" style="{{visibility}}">{{x}}</a>
</div>
.controller('MyCtrl', function($scope, $stateParams, chats) {
$scope.hideme = function(index, value) {
$scope.visibility = "background-color: #000000 !important;";
}
});
Firstly, you might want to change your object structure a bit. Remeber always have a dot (.) in your model value.
Try this.
<div ng-app ng-controller="myCtrl">
<div ng-repeat="x in myList">
{{x.value}}
</div>
</div>
function myCtrl($scope){
$scope.myList = [{
value: "a"
}, {
value: "b"
}, {
value: "c"
}];
$scope.hide = function(obj){
obj.hide = true;
}
}
JSFiddle

Looping though an object in Angular and using the key inside a ng-show

i am creating a simple Angular.js tabbing box which changes the box that is active accoring to a value 'tab' that is used inside ng-show on the elements.
This is working fine, however, at the moment I am writing all the HTML statically and I would rather cut down my code into a simple ng-repeat loop to loop through all the divs.
This is easy enough in PHP as I would use a foreach loop and use the key to generate the tab number, I just can't seem to do this in Angular. Here is my code at the moment:
<div id="services-box-nav">
<ul>
<li>Rewards</li>
<li>Community</li>
<li>Partners</li>
<li>Jobs</li>
<li>Volunteering</li>
<li>Feedback</li>
<li>Gallery</li>
</ul>
</div>
<div id="services-content-boxes">
<div ng-show="tab == 1">
<div class="row">
<div class="col-md-12">
<h3>{{serviceBoxes.rewards.title}}</h3>
</div>
</div>
<div class="row">
<div class="col-md-5">
{{serviceBoxes.rewards.text}}
</div>
<div class="col-md-7">
</div>
</div>
</div>
<div ng-show="tab == 2">dwd</div>
<div ng-show="tab == 3">d</div>
<div ng-show="tab == 4">df</div>
<div ng-show="tab == 5">gr</div>
<div ng-show="tab == 6">r</div>
<div ng-show="tab == 7">rg</div>
</div>
controller('servicesController', function($scope, $location, joomlaService) {
$scope.serviceBoxes = {};
joomlaService.getArticleDetails(21).then(function(articleReturnData) {
$scope.serviceBoxes.rewards = articleReturnData;
});
joomlaService.getArticleDetails(22).then(function(articleReturnData) {
$scope.serviceBoxes.community = articleReturnData;
});
joomlaService.getArticleDetails(23).then(function(articleReturnData) {
$scope.serviceBoxes.partners = articleReturnData;
});
joomlaService.getArticleDetails(24).then(function(articleReturnData) {
$scope.serviceBoxes.jobs = articleReturnData;
});
joomlaService.getArticleDetails(25).then(function(articleReturnData) {
$scope.serviceBoxes.volunteering = articleReturnData;
});
joomlaService.getArticleDetails(26).then(function(articleReturnData) {
$scope.serviceBoxes.feedback = articleReturnData;
});
joomlaService.getArticleDetails(27).then(function(articleReturnData) {
$scope.serviceBoxes.gallery = articleReturnData;
});
});
What I want to do is loop through the serviceBoxes object and dynamically create the ng-show condition (tab == i) using the key, which should increment each time (1, 2, 3, 4, etc). I don't know how I go about this using Angular. It would cut down my code considerably so feel it is necessary.
Can anyone point out how this is done?
Thanks
You can use angular-ui bootstrap Tabset directive.
<tabset>
<tab ng-repeat="serviceBox in serviceBoxes" heading="{{serviceBox.title}}" active="serviceBox.active">
{{serviceBox.text}}
</tab>
</tabset>
Thus your view is clean and tidy.
And your controller will look like:
controller('servicesController', function($scope, $location, joomlaService) {
$scope.serviceBoxes = [];
joomlaService.getArticleDetails(21).then(function(articleReturnData) {
$scope.serviceBoxes.push(articleReturnData);
});
joomlaService.getArticleDetails(22).then(function(articleReturnData) {
$scope.serviceBoxes.push(articleReturnData);
});
joomlaService.getArticleDetails(23).then(function(articleReturnData) {
$scope.serviceBoxes.push(articleReturnData);
});
joomlaService.getArticleDetails(24).then(function(articleReturnData) {
$scope.serviceBoxes.push(articleReturnData);
});
joomlaService.getArticleDetails(25).then(function(articleReturnData) {
$scope.serviceBoxes.push(articleReturnData);
});
joomlaService.getArticleDetails(26).then(function(articleReturnData) {
$scope.serviceBoxes.push(articleReturnData);
});
joomlaService.getArticleDetails(27).then(function(articleReturnData) {
$scope.serviceBoxes.push(articleReturnData);
});
});
<ul>
<li ng-repeat="serviceBox in serviceBoxes">{{serviceBox.title}}</li>
</ul>
<div ng-repeat="serviceBox in serviceBoxes" ng-show="tab == {{selectedIndex}}">{{serviceBox.contents}}</div>
In your controller:
$scope.selectedIndex = 0; // Default selected index, use -1 for no selection
$scope.itemClicked = function ($index) {
$scope.selectedIndex = $index;
};
The ng-repeat directive loops through every element and makes a copy of the html element it's placed inside of. If there at 10 items to look through, you will have 10 html elements. It also you references to the index of the current element via $index.
ng-click will call the function on itemClicked(), passing in the current index through the $index reference that ng-repeat supplied. In our function we're using that parameter to set our $scope.selected to it.
I have tried creating something similar. Try below code
I have hardcoded mapTab array.You can populate myTab using the corresponding values from $scope
In controller-
$scope.tab;
$scope.mapTab=[{},{"1": "dwd"},{"2":"d"},{"3":"dwd"},{"4":"df"},{"5":"gr"},{"6":"r"},{"7":"rg"}];
In html--
<div ng-repeat="(key,val) in mapTab">
<div ng-repeat="prop in val">
<div ng-show="tab==key">{{prop}}</div>
</div>
</div>
</div>
Demo--http://jsfiddle.net/RahulB007/HB7LU/9348/

Access child scope corresponding to a model variable

I have a list of items and I've rendered them in a template via ng-repeat. Each of them is controlled by an itemController which exposes some behavior for item (e.g. grabing focus). Here is the HTML:
<body ng-controller="mainController">
<button ng-click="addItem()">add</button>
<ul>
<li ng-repeat="item in items" ng-controller="itemController">
<div ng-if="item.isEditing">
<input ng-model="item.name"/>
<button ng-click="item.isEditing=false">done</button>
</div>
<span ng-if="!item.isEditing">{{item.name}}</span>
</li>
</ul>
</body>
In the mainController, I have a function for adding a new item to items. Here is the code for mainController:
app.controller('mainController', function($scope){
$scope.items = [
{
name: "alireza"
},
{
name: "ali"
}
];
$scope.addItem = function(){
$scope.items.push({isEditing: true});
}
});
Whenever I add an item to items array, a corresponding li element is added into the view which is controlled by an instance of itemController, and the corresponding model is the new item I've just added (or maybe the scope of the itemController, which contains item).
Problem:
When I add some item to items, I only have access to item and not the scope of the recently created item. So I can't run some function (like grabFocus) on the scope of new item.
Is it something semantically wrong in my design? What is the canonical approach for this problem?
Plunker link
Here is the plunker link with related comments
You can use $broadcast from the parent scope, along with $on from the child scope, to notify the child scopes of the newly added item. And by passing (as an argument) the $id of the child scope that corresponds to the newly added item, each child catching the event can know whether or not it's the one that needs to have grabFocus() called.
Here's a fork of your Plunker that uses that approach. I wasn't sure what you were trying to accomplish with $element.find(":text").focus(); in your original Plunker, so I tweaked it to toggle a $scope property that in turn controlled a style in the view. The newly added item will be red (by calling its own grabFocus function to toggle the flag to true), and the others will be black (by calling their own loseFocus function to toggle the flag to false).
Modified HTML (just the repeated li):
<li ng-repeat="item in items" ng-controller="itemController">
<div ng-if="item.isEditing">
<input ng-model="item.name"/>
<button ng-click="item.isEditing=false;handleItemAdded($index);">done</button>
</div>
<span ng-if="!item.isEditing" ng-style="{ color: isFocused ? 'red' : 'black' }">{{item.name}}</span>
</li>
Full JavaScript:
var app = angular.module("app",[]);
app.controller('mainController', function($rootScope, $scope){
$scope.items = [ { name: "alireza" }, { name: "ali" } ];
$scope.addItem = function(){
$scope.items.push({isEditing: true});
};
$scope.handleItemAdded = function (index) {
// $rootScope.$broadcast('item-added', { index: index });
for(var cs = $scope.$$childHead; cs; cs = cs.$$nextSibling) {
if (cs.$index === index) {
$rootScope.$broadcast('item-added', { id: cs.$id });
break;
}
}
};
});
app.controller('itemController', function($scope, $element){
$scope.$on('item-added', function (event, args) {
if ($scope.$id === args.id + 1) {
$scope.grabFocus();
} else {
$scope.loseFocus();
}
});
$scope.grabFocus = function() {
$scope.isFocused = true;
};
$scope.loseFocus = function() {
$scope.isFocused = false;
};
});
I changed your approach a little bit by creating an unique id for every input, based on its index number. See code below, hope it helps.
// Code goes here
var app = angular.module("app",[]);
app.controller('mainController', function($scope,$timeout){
$scope.items = [
{
name: "alireza"
},
{
name: "ali"
}
];
$scope.addItem = function(){
$scope.items.push({isEditing: true});
$timeout(function(){
document.getElementById("newItem"+($scope.items.length-1)).focus();
},0)
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.3.0" data-semver="1.3.0" src="//code.angularjs.org/1.3.0/angular.js"></script>
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body ng-controller="mainController">
<button ng-click="addItem()">add</button>
<ul>
<li ng-repeat="item in items">
<div ng-if="item.isEditing">
<input ng-model="item.name" id="newItem{{$index}}"/>
<button ng-click="item.isEditing=false">done</button>
</div>
<span ng-if="!item.isEditing">{{item.name}}</span>
</li>
</ul>
</body>
</html>

Resources