angular js nested controllers list/item - javascript

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

Related

How to display menu items based on sessionStorage

I'm kind of lost in which is the best way to proceed. After fetching the user data from an API, I store the user data in the sessionStorage. How can I display menu items based on roles (ie- Admin, Representative, Guest).
I have a basic Bootstrap Sidebar that looks like below.
<ul class="sidebar-nav">
<li><a>Home</a>
<ul class="sidebar-dropdown">
<li>Default</li>
<li>Reports</li>
</ul>
</li>
<li><a>Contact</a>
<ul class="sidebar-dropdown">
<li>Admin Contact</li>
<li>Contact 1</li>
</ul>
</li>
</ul>
Should I add classes to menu items based on roles to my menu items and with a JavaScript function that runs on load display or hide menu items??
Any guidance would be greatly appreciated.
JSFIDDLE
What you want is dynamically render a list based on data. Receiving data from session storage is not an issue because it can be done by
JSON.parse(sessionStorage.getItem('data'));
your issue is to render them. Try to use hardcoded data first to see if you are able to render what you want and then just plug in data from the storage. For an example
https://codesandbox.io/s/elated-fast-my3ud?file=/src/index.js
const routes = [
{
href: "/home",
title: "Home",
children: [
{
href: "/home/defaults",
title: "Defaults"
},
{
href: "/home/reports",
title: "Reports"
}
]
},
{
href: "/contact",
title: "Contact",
children: [
{
href: "/contact/admin-contact",
title: "Admin Contanct"
},
{
href: "/contact/contact-1",
title: "Contact 1"
}
]
}
];
function renderSingleRoute(route) {
const children = route.children ? renderRoutes(route.children) : "";
return `
<li>
${route.title}
${children}
</li>
`;
}
function renderRoutes(routes) {
return `
<ul>
${routes.map((route) => renderRoute(route)).join("")}
</ul>
`;
}
document.getElementById("app").innerHTML = renderRoutes(routes);
So the idea here is to build your routes however you want and then pass it to render function

Weird bug with ng-click

So I've used ng-repeat to create a list of all my songs in an album (refer to this question I asked earlier)
So what I am trying to do now is make it so when a user clicks an item from the list, it plays the refered track. This is my app:
enitoniApp.controller('musicItems', ['$scope', function ($scope) {
$scope.ngplaySong = function (ref, name) {
playSong(ref, name)
}
$scope.albums = [
{
name: 'Over The Mountains',
price: 0,
tracks: [
{
name: 'Over The Mountains',
ref: 'otm',
released: 0,
},
{
name: '!C3',
ref: 'ice',
released: 0,
},
{
name: 'Dark Clouds',
ref: 'clouds',
released: 0
},
{
name: 'Fog',
ref: 'fog',
released: 0
}
]
},
{
name: 'test-album',
price: 5000,
tracks: [
{
name: 'test',
ref: 'null'
},
]
}
]
}]);
As you can see, I'm trying to call a regular function using ng-click. This regular function (playSong()) is inside the code for my player, and it plays a track based on the reference id.
snippet from player.js:
/** Play single song **/
function playSong(ref, name) {
showPlayer();
clearPlaylist()
playlistPosition = 0;
addToPlaylist(ref, name, 'play')
}
So I have this in my html:
<li ng-repeat="album in albums">
<div class="info">
<p>{{album.name}}</p>
<p>{{album.price | currency}}</p>
</div>
<ul>
<li ng-animate="grid-fade" ng-repeat="track in album.tracks">
<div class="grid-item" ng-click="ngplaySong('{{track.ref}}','{{track.name}}')">
<div class="cover">
<img ng-src="/img/covers/art_{{track.ref}}.png" alt="">
</div>
<div class="info">
<p>{{track.name}}</p>
<p>{{track.released}}</p>
</div>
</div>
</li>
</ul>
</li>
The weird thing is that even though this is rendering correctly:
THIS gets outputted into the console even though the parameters are correct:
Why is it not binding the data when the function gets called, am I missing something here?
I do not think that you need those braces inside your ng-click. Try this:
<div class="grid-item" ng-click="ngplaySong(track.ref, track.name)">
The thing is that you pass an expression to ng-click which is then parsed by Angular and it is smart enough to recognize the variables from current scope. You can read more on Angular expressions here: https://docs.angularjs.org/guide/expression
In fact, there is a very nice and easy example in Angular ng-click documentation which includes accessing a local variable inside the ng-click expression: https://docs.angularjs.org/api/ng/directive/ngClick

How to properly execute a function inside ng-repeat

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.

Nested ng-repeat undefined errors in AngularJS

I'm having an issue with a very simple nested ng-repeat with AngularJS 1.2.0-rc3.
I have a JSON object inside my controller representing a menu structure, like this:
$scope.menus = [
{
title: "menu1",
action: "#",
menus: [
{
title: "submenu1_1",
action: "stuff"
},
{
title: "submenu1_2",
action: "moreStuff"
}
]
},
{
title: "menu2",
action: "#",
menus: [
{
title: "submenu2_1",
action: "awesomeStuff"
},
{
title: "submenu2_2",
action: "moreAwesomeStuff"
}
]
}
]
My view uses this information to create a Bootstrap nav menu structure, like this:
<div class="navbar">
<ul class="nav navbar-nav">
<li ng-repeat="menu in menus" ng-class="{dropdown: menu.menus}">
{{menu.title}}
<ul ng-if="menu.menus" class="dropdown-menu">
<li ng-repeat="submenu in menu.menus">
{{submenu.title}}
</li>
</ul>
</li>
</ul>
</div>
This should work fine, and it does... 50% of the time. Sometimes, almost randomly, the menu won't show up at all and I'll get this error in the console:
Error: 'undefined' is not an object (evaluating 'node.childNodes')
Is there something wrong with my markup? The error appears sometimes on page load, and always when I click on a submenu (in my code the actions are replaced simply by an "#").

why din't computed don't track changes in this simple knockout app?

The menu is what I want, when mouse over the left, the right should changes but doesn't.
Here is my simplified viewmodel:
var currentSelectIndex = 0;
var AppModel = {
CurrentIndex: ko.observable(currentSelectedIndex),
OnMouseOver: function (data, event) {
// change currentIndex or currentSelectedIndex here
// CurrentSubCategory didn't updated
},
CurrentSubCategory: ko.computed({
read: function() {
return AppModel.Menu[AppModel.CurrentIndex()].subcategory;
},
deferEvaluation: true
}),
Menu: [
{
subcategory: [
{ name: '1', id: 50000436 },
{ name: '2', id: 50010402 },
{ name: '3', id: 50010159 }
],
}
};
And my html:
<div class="categories" id="categories">
<div class="first-category" id="first-category">
<ul data-bind="foreach:Menu">
<li data-bind="text:name,attr:{id:id,class:className},event{ mouseover: $root.myfunction}"></li>
</ul>
</div>
<div class="sub-category" id="sub-category">
<ul data-bind="foreach:CurrentSubCategory()">
<li><a data-bind="text:name,attr:{href:getListUrl(id)}"></a></li>
</ul>
<div class="clear">
</div>
</div>
<div class="clear">
</div>
</div>
Sorry, can't post images due to less than 10 reputation.
Thanks for any help!
There were several syntax errors in your code which I imagine are a result of your making it simpler to post.
I have posted a working jsFiddle here: http://jsfiddle.net/Gy6Gv/2/
I changed Menu to be an observable array only because knockout provides the helper method .indexOf to make it easier to get the index of the menu from the mouseover. Other than that there was no problem with the computed. I imagine there is some other syntactical error in your actual code.

Categories

Resources