SITUATION:
I am making an app in AngularJs that assign permissions.
In order to do this i have three nested ng-repeat.
First loop: display PERMISSION GROUP
Second loop: For each permission group display CATEGORIES.
Inside this loop execute a function that will get all the SUB CATEGORIES for each category
Third loop: display SUB CATEGORIES
ISSUE:
The problem is in the execution of the function inside the second loop.
ATTEMPT 1 - ng-init:
<div class="row" ng-repeat="permission_group in list_permission_groups">
<div class="col-sm-3">
<h3>
{{permission_group.permission_group_name}}
</h3>
</div>
<div class="col-sm-9">
<ul>
<li ng-repeat="category in list_categories">
<span>
{{ category.name }}
</span>
<div class="checkbox">
<label>
<div ng-init="temp_result = get_Sub_Categories(category.category_id)">
<p ng-repeat="sub_category in temp_result">
{{ sub_category.name }}
</p>
</div>
</label>
</div>
</li>
</ul>
</div>
</div>
In the controller:
$scope.get_Sub_Categories = function(category_id) {
$http({
url: base_url + 'main/json_get_list_sub_categories',
data: {
category_id: category_id
},
method: "POST"
}).success(function(data) {
return data;
});
}
Te behavior is quite strange. Porbably due to dirty checking the page is loaded 682 times.
No result is displayed.
ATTEMPT 2 - ng-click: (only for debug)
<div class="row" ng-repeat="permission_group in list_permission_groups">
<div class="col-sm-3">
<h3>
{{permission_group.permission_group_name}}
</h3>
</div>
<div class="col-sm-9">
<ul>
<li ng-repeat="category in list_categories">
<span>
{{ category.name }}
</span>
<div class="checkbox">
<label>
<button ng-click="get_Sub_Categories(category.category_id)">
GET SUB-CATEGORIES
</button>
{{ list_sub_categories }}
</label>
</div>
</li>
</ul>
</div>
</div>
In the controller:
$scope.get_Sub_Categories = function(category_id) {
$http({
url: base_url + 'main/json_get_list_sub_categories',
data: {
category_id: category_id
},
method: "POST"
}).success(function(data) {
$scope.list_sub_categories = data;
});
}
This time the page is loaded only once.
If I press the button the proper sub-categories are displayed BUT of course not only for the corresponding category but FOR ALL, because i am modifying the var in the global scope.
THE AIM:
What I want to obtain is simply displaying all the proper sub-categories for each category.
Without using a button, but simply see all the proper content as soon as the page load.
But i don't understand how can this be done properly in AngularJs.
THE QUESTION:
How can i properly execute a function inside a ng-repeat that return and display different data for each loop?
EDIT - DUMP OF EXAMPLE OF SUB-CATEGORIES FOR ONE CATEGORY:
[{
"sub_category_id": "1",
"name": "SUB_CATEGORY_1",
"category_id_parent": "1",
"status": "VISIBLE"
}, {
"sub_category_id": "2",
"name": "SUB_CATEGORY_2",
"category_id_parent": "1",
"status": "VISIBLE"
}, {
"sub_category_id": "3",
"name": "SUB_CATEGORY_3",
"category_id_parent": "1",
"status": "VISIBLE"
}, {
"sub_category_id": "4",
"name": "SUB_CATEGORY_4",
"category_id_parent": "1",
"status": "VISIBLE"
}]
Calling a function inside ng-repeat is same as normal one. Since you need to display the sub categories at the time of page loading its better to get these data beforehand.
Asynchronously loading sub categories will not fit into this scenario.
Here is a minimal snippet achieving this (JS Fiddle)
<div ng-app="app" ng-controller="ctrl">
<div ng-repeat="category in model.categories"> <span> Category: {{ category.name }} </span>
<p ng-repeat="subCategory in getSubCategories(category.Id)">{{ subCategory.name }}</p>
</div>
</div>
Controller
angular.module("app", [])
.controller('ctrl', ['$scope', function ($scope) {
$scope.model = {
categories: [{
"Id": 1,
name: '1'
}, {
"Id": 2,
name: '2'
}],
subCategories: [{
"parentId": 1,
name: 'a1'
}, {
"parentId": 1,
name: 'a2'
},
{
"parentId": 2,
name: 'a3'
}]
}
$scope.getSubCategories = function(parentId){
var result = [];
for(var i = 0 ; i < $scope.model.subCategories.length ; i++){
if(parentId === $scope.model.subCategories[i].parentId){
result.push($scope.model.subCategories[i]);
}
}
console.log(parentId)
return result;
}}])
The subcategory example did not work for my case and it took my code into an infinte loop for some reason. may be because i was using an accordion.
I achieved this function call inside ng-repeat by using ng-init
<td class="lectureClass" ng-repeat="s in sessions" ng-init='presenters=getPresenters(s.id)'>
{{s.name}}
<div class="presenterClass" ng-repeat="p in presenters">
{{p.name}}
</div>
</td>
The code on the controller side should look like below
$scope.getPresenters = function(id) {
return SessionPresenters.get({id: id});
};
While the API factory is as follows:
angular.module('tryme3App').factory('SessionPresenters', function ($resource, DateUtils) {
return $resource('api/session.Presenters/:id', {}, {
'query': { method: 'GET', isArray: true},
'get': {
method: 'GET', isArray: true
},
'update': { method:'PUT' }
});
});
I think that the good solution here is to use Angular Directive.
You can see an example of directive used in a ng-repeat here : Angular Directive Does Not Evaluate Inside ng-repeat
For more information on directives, you can check the official documentation : https://docs.angularjs.org/guide/directive
I would create a factory for category then move your get_sub_categories function into this new factory.
Related
I have a input (top right) where users can search things, when it's directive length get 3 characters it will display a list of products and highlight the matches...
Look at my code:
html
<div id="app">
<div id="header">
<div class="right"><input type="text" v-model="message" v-on:keyup="searchStart()" v-on:blur="searchLeave()"/>
<ul v-if="this.searchInput" class="product-list">
<li v-for="product in products">
{{ product.id }} - {{ product.name | highlight }} - {{ product.qtd }}</li></ul>
</div>
</div>
<div id="main">
<div id="menu">fdfds</div>
<div id="container">{{ message }}</div>
</div>
</div>
js
var search = new Vue({
el: "#app",
data: {
message: "",
searchInput: false,
products: [
{
id: 1,
name: "produto 01",
qtd: 20
},
{
id: 2,
name: "produto 02",
qtd: 40
},
{
id: 3,
name: "produto 03",
qtd: 30
},
]
},
methods: {
searchStart: function(){
if(this.message.length >= 3)
this.searchInput = true;
console.log(this.searchInput);
},
searchLeave: function(){
this.searchInput = false;
this.message = "";
console.log(this.searchInput);
}
},
filters: {
highlight: function(value){
return value.replace(search.message, '<span class=\'highlight\'>' + search.message + '</span>');
}
}
});
Here you can see a live pen: http://codepen.io/caiokawasaki/pen/dXaPyj
try to type prod inside the pen...
Is my filter correct? The way I created the filter is correct?
The main question is: How to output the HTML from my filter?
Edit/Solution
The problem in the case was codepen, there is some kind of conflict with vue, so I was not able to escape the html using {{{}}}, put the code in another editor (jsfidle) and it worked.
I'm accepting the answer given to the reward because it's right.
You'll need 3 steps here for achieve what you want:
Use triple braces {{{ }}} to display unescaped html
Filter your users by your v-model variable, in order to just show the matches
Replace the substring matching by the <span> tag
Check out the computed property filteredUsers and the filter in this working jsfiddle
Im currently trying to nest a <select> inside a div with a ng-repeat
something like this:
<li ng-repeat="item in items >
<div class="row">
<div class="col-xs-7">
<span ng-bind="item.Name"></span>
</div>
<div class="col-xs-4 modal-body--ul--li__dropdown_container">
<select ng-model="typemodel" >
<option ng-repeat="usertype in item.userGroups"></option>
</select>
</div>
<div class="col-xs-1">
<a ng-click="add(item)"></a>
</div>
</div>
</li>
In the controller im adding the item to a new list with selected items. I also want to add the value from the dropdown as a new value to the list in the controller.
$scope.add = function (item) {
//New value with the value from the dropdown
item.recipientType = typemodel;
$scope.selected.push(item);
};
The list :
$scope.items = [
{
"Name":"Name1",
"userGroups":[
{ "type": "gruop1" },
{ "type": "gruop2" },
{ "type": "group3" }
]
},
{
"Name":"Name2",
"userGroups":[
{ "type": "gruop1" },
{ "type": "gruop2" },
{ "type": "group3" }
]
}
]
In short i need the selected value from the dropdown when i hit the "add", the value of the current typemodel.
This is how it looks now, but it dont work. I know i have to get the correct model and maybe track the items list by $index, but after searching the web the whole day for a solution, im still stuck!
All help is appreciated.
-Thanks!
ps. please comment if something is missing from the question, or something more is needed to solve this.
First, change your dropdown list to
<select ng-options="usertype in item.userGroups" ng-model="item.recipientType">
Then when you trigger the add function, item's property recipientType is already set.
$scope.add = function (item) {
console.log(item.recipientType);
$scope.selected.push(item);
};
Suppose I have following html file:
<!DOCTYPE html>
<html>
<head>
<title>LOG</title>
</head>
<body>
<div class="panel panel-success">
<!-- Default panel contents -->
<div class="panel-heading">Log data</div>
<div class="panel-body">
<!-- List group -->
<ul class="list-group">
<li class="list-group-item">Start processing at {{StartProcessing }}</li>
<li class="list-group-item">Finished processing at {{EndProcessing }}</li>
</ul>
<div id="logTvId" kendo-tree-view
k-data-source="treeData">
</div>
</div>
</div>
and following controller code:
Arch.LogController = function ($scope, $resource, $routeParams)
{
var LogResource = $resource('log/:markerId', {}, {
get: {method: "GET", isArray: false}
});
LogResource.get({markerId: $routeParams.markerId}, function (data1)
{
$scope.StartProcessing = new Date(data1.StartProcessing).toLocaleString();
$scope.EndProcessing = new Date(data1.EndProcessing).toLocaleString();
$scope.treeData = new kendo.data.HierarchicalDataSource({ data: [
{ text: "Item 1" },
{ text: "Item 2", items: [
{ text: "SubItem 2.1" },
{ text: "SubItem 2.2" }
] },
{ text: "Item 3" }
]});
});
};
After page load I can see StartProcessing and EndProcessing on page, but I can't see treeview. If I take out code related to $scope.treeData from resource load (say the next instruction after)
then everything works as expeteced. If I add $scope.$apply() to my initial controller code it throws exception...
What I'm doing wrong? Should I deal with promises ($q ??) and wait after resource is loaded?
Thanks in advance.
Ok, actually problem is solved. In resource handler I create kendo treeview by "hand" (via jquery, as usual), so I'm not using kendo-tree-view directive. Simple div with id + jquery.
I'm using Angular; I have have an ng-repeat loop where I'd like to make some elements clickable.
Something like the following:
<div ng-repeat="item in itemList">
<span ng-class="{ 'interactive' : item.clickable }"
ng-click="doSomething(item)"> .... </span>
</div>
Where doSomething in the controller is something like
$scope.doSomething = function(item) {
if (!item.clickable) return;
/* do stuff */
};
This works, but my fear is that if most of the elements are not clickable setting lot of useless handlers could slow down the page. Is it the case ? If it is so, is there a way to set the ng-click attribute only where needed, i.e. only for those elements such that item.clickable === true ?
Take a look at this
Working Demo
script
angular.module('main', []);
// Main Controller
function Controller($scope) {
$scope.itemList = [{
clickable: true,
id:1,
name: "ABC-Name"
}, {
clickable: false,
id:2,
name: "XYZ-Name"
}, {
clickable: true,
id:3,
name: "LMN-Name"
}];
$scope.doSomething = function(item) {
console.log(item);
}
}
html
<div class="container" ng-app="main" ng-controller="Controller">
<div ng-repeat="item in itemList">
<span ng-class="{ 'interactive' : item.clickable }"
ng-click="!item.clickable||doSomething(item)">{{item.name}}
</span>
</div>
</div>
I am new to angular and wanted advice on the best route to achieve something like this. This jsFiddle doesn't work but here is the idea.
I want tabs along the top with items available for selection. When you select the item, the data is populated below.
I wanted to have a ListController and an ItemController, so i can separate out the methods that act on the list vs that act on the item; since i wanted the items to be updatable directly. I am getting all the data on the page load, so i don't want to load each tab dynamically.
How can i do this and/or how can i fix the fiddle or new fiddle?
jsFiddle
plunker
<div ng-app="myApp">
<div ng-controller="ListController">
<ul class="nav nav-pills">
<li ng-repeat="artist in list">
<a show-tab="" ng-href="" ng-click="select(artist)">{{$index}} - {{artist.name}}</a>
</li>
</ul>
<div ng-controller="ItemController">
<p>{{name}} - {{selected.name}}</p>
<span>{{totalSongs}}</span>
<span>{{selected.songs.length}}</span>
<ul>
<li ng-repeat="song in selected.songs" ng-controller="ItemController">{{song}} - {{totalSongs}}</li>
</ul>
</div>
</div>
</div>
I would really like to keep the controllers separate and logic separate.
I created some functionality in the ItemController to illustrate how you could act on them separately:
http://jsfiddle.net/jdstein1/LbAcz/
Added some data to the list controller:
$scope.list = [{
name: "Beatles",
songs: [{
title: "Yellow Submarine",
time: "424"
}, {
title: "Helter Skelter",
time: "343"
}, {
title: "Lucy in the Sky with Diamonds",
time: "254"
}]
}, {
name: "Rolling Stones",
songs: [{
title: "Ruby Tuesday",
time: "327"
}, {
title: "Satisfaction",
time: "431"
}]
}];
And fleshed out the item controller:
app.controller('ItemController', ['$scope', function ($scope) {
$scope.selectItem = function (song) {
$scope.song.time++;
};
}]);