Master Detail with Nested JSON - javascript

I want to create a master detail view with some nested JSON and am out of ideas. I know I am probably missing something simple here but I turn to you all for help.
This is a sample of the JSON. There are around 50 Items with the same number and letter pattern (e.g. 1a, 30d, etc).
{
"Items": [
{
"Name": "Item 1",
"Number: 1
"Subcategories": [
{
"Name": Sub Item 1",
"Letter: "A",
"Description": "A description about the item..."
}
"Name": Sub Item 2",
"Letter: "B",
"Description": "A description about the item..."
}
}
"Name": Sub Item 3"
"Letter: "C",
"Description": "A description about the item..."
}
}
}
I have successfully set up the master page with an accordion that lists all the items in the list that when clicked, displays all of the sub items but where I am getting confused is when I click on the sub menu item and am routed to the details page, I can only view the name parameter that is stored in $state.params. I want the details page to be able to display the description as well.
Here is the accordion list:
<div ng-repeat="item in items">
<ion-item class="item-stable"
ng-click="toggleGroup(item)"
ng-class="{active: isGroupShown(item)}">
<i class="icon" ng-class="isGroupShown(item) ? 'ion-minus' : 'ion-plus'"></i>
{{item.name}}
</ion-item>
<ion-item class="item-accordion"
ng-repeat="item_type in item.subcategories"
ng-show="isGroupShown(item)"
ng-click="onSelectItems(item_type)">
{{item_type.name}}
</ion-item>
</div>
Here is the relevant app.js .config:
.state('tab.item-listing', {
url:'/item-listing',
views: {
'tab-home': {
templateUrl: 'templates/item-listing.html',
controller: 'itemListingsCtrl'
}
}
})
.state('tab.itemDetail', {
url:'/:name',
views: {
'tab-home': {
templateUrl: 'templates/item-detail.html',
controller: 'itemListingDetailCtrl'
}
}
})
Here is the relevant function within the itemListingsCtrl controller:
$scope.onSelectItems = function(item_type) {
var params = {
name: item_type.name,
};
$state.go('tab.itemDetail', params);
console.log($state.params);
};
});
Here is the controller for itemListingDetailCtrl:
.controller("itemListingDetailCtrl", function ($scope, itemService, $stateParams, $state)
{
console.log($state.params);
$scope.name = $state.params.name;
})
Finally, this is the relevant section from the service.js that pulls in the JSON.
.factory('itemService', function($http,$location) {
var items = [];
return {
getitems: function(){
return $http.get(/path/to/JSON).then(function(resp){
items = resp.data.items;
return items;
});
},
}
});
Is anyone able to lend a hand? Thanks in advance.

Related

Animation not being applied to all items in Angular ng-repeat list

Newbie to Angular Question:
Would like my app to animate all items in an ng-repeat list each time it changes.
Looked over multiple items on this topic including:
AngularJS ng-repeat rerender
http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
Angular.js: Is it possible to re-render ng-repeats based on existing scope data?
Appears I have the same ng-repeat-needs-render-changed-list issue. App works great except animation not being applied to all items in list after the first click.
I have attempted to position $scope.$apply() in code but keeps throwing me ugly errors. Just cannot figure out how/where to apply what I think should be a $scope.$apply().
Relevant HTML here
<!-- Toolbar -->
<md-toolbar class="md-primary">
<div class="md-toolbar-tools">
<span class="product-label"><h1>Car List</h1></span>
</div>
</md-toolbar>
<md-content class="md-padding">
<!--Navigation Bar-->
<md-nav-bar md-selected-nav-item="currentNavItem" nav-bar-aria-label="navigation links">
<md-nav-item md-nav-click="goto('page1')" name="Domestic" ng-click="getType(currentNavItem)">Domestic</md-nav-item>
<md-nav-item md-nav-click="goto('page2')" name="Foreign" ng-click="getType(currentNavItem)">Foreign</md-nav-item>
</md-nav-bar>
<!--run div if nav item has been clicked-->
<div ng-show = "currentNavItem">
<!--flip thru array via ng-repeat and apply animation-->
<div layout="row" layout-wrap>
<div flex="50" class="repeat-animation" ng-repeat="x in carhold2|orderBy track by $index">
<div class="itemcard">
{{x}}
</div>
</div>
</div>
</div>
</md-content>
Javascript here
var app = angular.module('carDash',['ngMaterial', 'ngMessages', 'material.svgAssetsCache', 'ngAnimate'])
.config(configFn);
function configFn($mdThemingProvider) {
$mdThemingProvider.theme('default')
.primaryPalette('blue')
.accentPalette('red');
}
app.controller('AppCtrl', ['$scope', "$http", "$log",
function ($scope, $http, $log) {
//obtain entire json
var g = $http({
url : "car-list.json",
method: 'get'
});
g.success(function (data) {
//populate array based on domestic or foreign selection
$scope.cars = data;
$scope.getType = function (currentNavItem) {
$scope.typo = currentNavItem;
var carhold = [];
for (var i = 0; i < $scope.cars.length; i++) {
if ($scope.cars[i].type == $scope.typo) {
carhold.push($scope.cars[i].done)
};
};
$scope.carhold2 = carhold;
};
});
g.error(function (err){
$log.error(err);
});
}]);
JSON here
{
"id": 1,
"type": "Domestic",
"done": "Ford"
},
{
"id": 2,
"type": "Domestic",
"done": "Chrysler"
},
{
"id": 3,
"type": "Domestic",
"done": "Tesla"
},
{
"id": 4,
"type": "Foreign",
"done": "Mercedes Benz"
},
{
"id": 5,
"type": "Foreign",
"done": "BMW"
},
{
"id": 6,
"type": "Foreign",
"done": "Volvo"
},
{
"id": 7,
"type": "Foreign",
"done": "VW"
}
Code now doing what I was looking for.
Pushed entire object rather than specific value into new array.
$scope.getType = function (currentNavItem) {
$scope.typo = currentNavItem;
var carhold = [];
for (var i = 0; i < $scope.cars.length; i++) {
if ($scope.cars[i].type == $scope.typo) {
carhold.push($scope.cars[i]);
};
};
Then did ng-repeat thru new collection and animation appeared on all items.
<!--flip thru array via ng-repeat and apply animation-->
<div layout="row" layout-wrap>
<div flex="50" class="repeat-animation" ng-repeat="x in carhold2">
<div class="itemcard">
{{x.done}}
</div>
</div>
</div>

AngularJS $scope undefined for item inside $scope

I'm quite new to AngularJS.
I've given myself a small project to create a list of local businesses and then the ability to 'like' each one - with the number of likes increasing by 1 each time the 'likes' div is clicked.
Here's my HTML
<ion-content ng-controller="BusCtrl" ng-init="init()" class="has-header">
<div class="list">
<div ng-repeat="item in nodes" class="item">
<b>{{item.node.title}}</b><br>
<b>{{item.node.website}}</b><br>
<img ng-src="{{ item.node.main_image.src }}">
<div class="engagement">
<p class="likes" ng-click="plusOne($index)">{{item.node.likes}}</p>
</div>
<span ng-bind-html="item.node.summary"></span>
</div>
</div>
Here's my controller code
.controller('BusCtrl', function($scope, $http) {
$scope.init = function() {
$http.get("sample-json/business-directory.json")
.success(function(data) {
$scope.nodes = data.nodes;
$scope.plusOne = function(index) {
$scope.nodes.node[index].likes += 1;
};
$scope.browse = function(v) {
window.open(v, "_system", "location=yes");
};
window.localStorage["nodes"] = JSON.stringify(data.nodes);
})
.error(function(data) {
console.log("ERROR: " + data);
if(window.localStorage["nodes"] !== undefined) {
$scope.entries = JSON.parse(window.localStorage["nodes"]);
}
});
};
})
Here's some sample JSON
{
"nodes": [
{
"node": {
"title": "2013 Business Survey",
"website": "http://www.portumnachamber.ie",
"likes": 0,
"main_image": {
"src": "http://portumnachamber.com/sites/default/files/styles/main-business-image-teaser/public/LOVEPORTUMNA_FINAL%20LOGO_%20small_3.jpg?itok=L5IE2Du_",
"alt": "Portumna Business Survey 2013, What does Portumna Co. Galway need and want"
}
}
},
{
"node": {
"title": "All Occasions Boutique, Portumna",
"website": "http://www.portumnachamber.com",
"likes": 0,
"main_image": {
"src": "http://portumnachamber.com/sites/default/files/styles/main-business-image-teaser/public/all%20occasions%201.jpg?itok=LFvCQIAT",
"alt": "Boutique Clothes Shop Portumna county Galway Ireland, All Occasions, Portumna"
}
}
},
{
"node": {
"title": "Anthony Williams",
"website": "http://www.portumnachamber.com",
"likes": 0,
"main_image": {
"src": "http://portumnachamber.com/sites/default/files/styles/main-business-image-teaser/public/default_images/portumna-coc-logo.png?itok=V9G81lx4",
"alt": ""
}
}
},
]
}
I get a list of each business - title, website, image, and the number 0 for likes. However, when I click on the 0, I get this error:
Error: $scope.nodes.node is undefined
It seems that it's a $scope within a $scope or something like that.
Any tips? Thanks a lot.
I am going to assume that your nodes data source doesn't have another node array property on it. In this case, your function plusOne becomes:
$scope.plusOne = function(index) {
$scope.nodes[index].node.likes += 1;
};
You are iterating ( ng-repeat ) on the nodes variable from your scope, so you have to put your indexer on that property. $scope.nodes is your array, not $scope.nodes.node. For each item from $scope.nodes you have a node property.
For example $scope.nodes[0] gives you first item which has a .node property.
You are using the wrong structure.
Nodes is the array so you need to apply the index there, not to node. Node is an object which contains the parameter "likes".
The undefined item here was nodes.node[index] not the $scope
To access a node you should write $scope.nodes[index].node.likes += 1;
According to your JSON please change
$scope.nodes.node[index].likes += 1;
to
$scope.nodes[index].node.likes += 1;

Use the value of a variable to find an item in array

I am still having trouble using the var to identify the object in the array. Here is what I have right now:
in controller:
$scope.allbooks=[
{book1={title:"Eat Pray Love", price:"$3.99"},
{book2={title:"DaVinci Code", price:"$7.99"}}
]
$scope.pick=function(name){
$rootScope.choice = name;
}
in html template
<ion-item ng-click="pick(book1)">
{{allbooks.choice[0].title}} <----This does not work
Used an alert to make sure choice=book1 which it does...I don't know what I am doing wrong
PLEASE HELP :(
[
{book1={title:"Eat Pray Love", price:"$3.99"},
{book2={title:"DaVinci Code", price:"$7.99"}}
]
is not valid javascript. If you are trying to define an object literal you'll need to adjust your syntax a bit:
$scope.allbooks = [
{title:"Eat Pray Love", price:"$3.99"},
{title:"DaVinci Code", price:"$7.99"}
]
Now you can access each book:
$scope.allbooks[0]; // returns first book
$scope.allbooks[1]; // returns second book
in controller:
$scope.allbooks=[
{book1:{title:"Eat Pray Love", price:"$3.99"}},
{book2:{title:"DaVinci Code", price:"$7.99"}}
];
$scope.choice = {}; //a variable to store your choice
$scope.pick=function(name){
$scope.choice = $scope.allbooks[name];
}
in html template
<ion-item ng-click="pick('book1')"> <!-- pass book1 as string -->
{{choice.title}}
angular.module("app", [])
.controller("main", function($scope) {
$scope.allbooks = {
book1: {
title: "Eat Pray Love",
price: "$3.99"
},
book2: {
title: "DaVinci Code",
price: "$7.99"
}
};
$scope.choice = {}; //a variable to store your choice
$scope.pick = function(name) {
$scope.choice = $scope.allbooks[name];
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="main">
<button ng-click="pick('book1')">Test</button>
<!-- pass book1 as string -->
{{choice.title}}
</div>

How to load template from dropdown menu Angular

I am fairly new to angular and am working with a client that wants a dropdown allowing users to select their neighborhood which is then saved in a cookie to load upon return. I am able to save cookie but am having trouble getting dropdown selected neighborhood to load proper template.
Here is the html:
<select id="mNeighborhood" ng-model="mNeighborhood" ng-options="neighborhood.id as neighborhood.name for neighborhood in neighborhoods" ng-change="saveCookie()"></select>
And then, I have added the following in html:
<div ng-view=""></div>
Here is the app.js code:
var app = angular.module('uSarasota', ['ngCookies', 'ngRoute']);
app.config(function ($routeProvider) {
$routeProvider
.when('/', {
template: '<div><h1 style="margin: 200px;">This is our main page</h1></div>'
})
.when('/downtown', {
templateUrl: "templates/downtown.html"
})
.otherwise({
template: '<div><h1 style="margin: 200px;""><strong>NOTHING TO SEE HERE</strong></h1></div>'
})
});
//Select Neighborhood
app.controller('myNeighborhood', ['$scope', '$cookies', function($scope, $cookies) {
$scope.neighborhoods = [{
name: "My Neighborhood",
id: 0
}, {
name: "All of Sarasota",
id: 1
}, {
name: "Downtown",
url: "/downtown",
id: 2,
}, {
name: "North Sarasota",
id: 3
}, {
name: "Lakewood Ranch",
id: 4
}, {
name: "Longboat Key",
id: 5
}, {
name: "St. Armands Circle",
id: 6
}, {
name: "Southside Village",
id: 7
}, {
name: "Siesta Key",
id: 8
}, {
name: "Gulf Gate",
id: 9
}];
//Set Cookie so when user returns to site, it will be on their neighborhood
$scope.mNeighborhood = parseInt($cookies.get('sNeighborhood')) || 0;
$scope.saveCookie = function() {
$cookies.put('sNeighborhood', $scope.mNeighborhood);
};
}]);
This all works fine to save and load user selection, but am having trouble finding solution to get template based on selection. So, should I add url to the array for each neighborhood and if so, how do I get the link?
Basically you need to change the URL programatically on selection of dropdown. For achieving this thing you need to first change you ng-options to return object on selection. And then using that object get url property from it to load particular template.
Markup
<select id="mNeighborhood"
ng-model="mNeighborhood"
ng-options="neighborhood.name for neighborhood in neighborhoods"
ng-change="saveCookie()">
</select>
Code
$scope.saveCookie = function() {
var mNeighborhood = $scope.mNeighborhood;
$cookies.put('sNeighborhood', mNeighborhood.id);
//do add $location dependency in controller function before using it.
$location.path(mNeighborhood.url);
};
Update
On initial Load the value should be set as object as per new implementation.
$scope.mNeighborhood = {}; //at the starting of controller
//the other code as is
//below code will get the object from the neighborhoods array.
$scope.mNeighborhood = $filter('filter')($scope.neighborhoods, parseInt($cookies.get('sNeighborhood')) || 0, true)[0];
$scope.saveCookie = function() {
var mNeighborhood = $scope.mNeighborhood;
$cookies.put('sNeighborhood', mNeighborhood.id);
//do add $location dependency in controller function before using it.
$location.path(mNeighborhood.url);
};

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.

Categories

Resources