AngularJS $scope undefined for item inside $scope - javascript

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;

Related

Filtering data from fetch() into matching container

I'm using fetch() to create a section which pulls and sorts listings from greenhouse api into its matching container.
I have a predefined list of departments which I have stored in the departments array. If a fetched item has a similar value as the data-dept value, then that html will be added under that container.
Here's an example, one of the items in the array is "Sales". Once fetch() is complete, two things can happen:
Either a job with the department of "sales" exists, in which case it will be appended to data-dept="sales". Note: In my code, I'm using data-dept^= to find similar names. If "sales us" exits in the api, then I want that too to be appended to [data-dept="sales"].
No jobs exist with the department of "sales". In this case, if "[data-dept="sales"]` has no child elements, hide it, as there's no point showing departments with no listings.
Current issues:
You can see by accessing the API URL that jobs with the "department" of "sales" do exist, but they do not get appended to my data-dept="sales" div (it has no child elements).
Any jobs that are not similar departments to those that are in the array need to appended to data-dept="other", but this section is also empty. For example, thee api has jobs for the "department" of "Architects". This option isn't in the array, so these jobs will need to be appended to data-dept="other".
Code:
$(function() {
fetch('https://boards-api.greenhouse.io/v1/boards/example/jobs?content=true', {})
.then(function (response) {
return response.json();
})
.then(function (data) {
appendDataToHTML(data);
})
.catch(function (err) {
console.log(err);
});
function appendDataToHTML(data) {
const mainContainer = document.getElementById("careers-listing");
// for each object, create card
for (var i = 0; i < Object.keys(data.jobs).length; i++) {
var department = data.jobs[i].departments[0].name;
department = department.replace(/\s+/g, '-').toLowerCase();
var job_title = data.jobs[i].title;
var job_location = data.jobs[i].location.name;
var html =
'<figure class="careercard" data-dept="'+ department +'">' +
'<div class="careercard__inner">' +
'<figcapton class="careercard__role">' +
'<span class="careercard__title">' + job_title + '</span>' +
'</figcapton>' +
'<div class="careercard__address">' +
'<span class="careercard__location">' + job_location + '</span>' +
'</div>' +
'</div>' +
'</figure>';
// filter card in correct parent category
if ("[data-dept^="+ department +"]") {
$(".careersIntegration__accordion-jobs[data-dept^='" + department + "']").append(html);
} else{
$(".careersIntegration__accordion-jobs[data-dept='other']").append(html);
}
}
}
/* fetch end */
$('.careersIntegration__accordion-jobs').each(function(index, obj){
console.log(this);
if ( $(this).length == 0 ) {
console.log("hide");
} else{
console.log("dont hide");
}
});
});
{% set departments = ["Sales" "Technology", "Creative", "Other"] %}
<section class="careersIntegration">
<div class="careersIntegration__listing" id="careers-listing">
{% for dept in departments %}
<div class="careersIntegration__accordion">
<div class="careersIntegration__accordion-header">
<span class="careersIntegration__accordion-dept">{{ dept }}</span>
</div>
<div class="careersIntegration__accordion-jobs" data-dept="{{ dept|lower|replace( ' ', '-' ) }}"></div>
</div>
{% endfor %}
</div>
</section>
Here is a visual guide of the layout I'm trying to achieve if it helps:
So this is not an exact answer but it does give you a good example. I just used mock data but here is the idea. Im sure there is a better way to do this but this is the quick and dirty.
Create a few variables to store the data for each department. This is a filter function that just stores anything inside the include() this will allow you to catch something like "US Sales" in the sales department.
Create a map function that takes in two paramiters the first is the variable you created earlier and the second is the name of the department which should match the name of whereever you are going to append this information.
The first part of the function creates the item using a template literal. this will create an array of all the items
The second part of the function wraps the array in a UL which is not super important but what is important is that you join the array using an empty sting.
Last part simply appends the html to the end of the department by using the name of the department as an ID and .insertAdjacentHTML("beforeend", list) which puts it before the end of the element and passes in the HTML which I have named as list
const data = [
{
title: "Lead Sales Person",
dept: "sales",
desc: "be a leader"
},
{
title: "Sales Person",
dept: "sales",
desc: "sell stuff to people"
},
{
title: "US Sales Person",
dept: "sales US",
desc: "sell stuff to people"
},
{
title: "Lead Developer",
dept: "dev",
desc: "be a leader"
},
{
title: "Developer",
dept: "dev",
desc: "Develop things and stuff"
},
{
title: "Random Guy",
dept: "other",
desc: "Do Random Stuff"
},
{
title: "Random Girl",
dept: "other",
desc: "Do Random Stuff"
}
];
let sales = data.filter(job => job.dept.includes("sales")),
dev = data.filter(job => job.dept.includes("dev")),
other = data.filter(job => job.dept.includes("other"));
mapDepartment(sales, "sales");
mapDepartment(dev, "dev");
mapDepartment(other, "other");
function mapDepartment(dept, name){
let items = dept.map(position => {
return `
<li>
<b>Title:</b> ${position.title}<br>
<b>Description:</b> ${position.desc}
</li>
`
})
let list = `<ul>${items.join("")}</ul>`;
document.getElementById(name).insertAdjacentHTML("beforeend", list)
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
margin-bottom: 1rem;
}
<div id="sales">
<h1>Sales</h1>
</div>
<div id="dev">
<h1>Development</h1>
</div>
<div id="other">
<h1>Other</h1>
</div>

Angular js solve a different format of a json array of objects

Based on the result below , how can an angular for each loop be able to solve that json array of objects format ? The value is title and the id is key. Any Idea? Thank you.
mycode
me.record.questionaires = []
angular.forEach(detail.questionaires, function (value, key) {
me.record.questionaires.push({ "id": key, "title": value })
});
Formated json data (detail.questionaire result)
[
"{'sub_title': 'dsadsa', 'instruction': 'You Must',…elimit': '01:05:19', 'title': 'asdsa', 'id': 133}",
"{'sub_title': 'sdasdsa', 'instruction': None, 'cre…melimit': '05:30:09', 'title': 'asda', 'id': 131}"
]
You need to
Loop over the array
Parse the string as JSON
Push or map the appropriate values into your questionaires array (it's not clear what data you want)
me.record.questionaires = detail.questionaires.map(json => {
let { id, title } = JSON.parse(json)
return { id, title }
})
I had to change your sample formatted JSON a bit because it was giving me console errors. Please see if this helps.
angular
.module("myModule", [])
.controller("myController", function($scope) {
var me ={record: {questionaires: []}};
$scope.me = me;
var detail ={};
detail.questionaires = [
"{'sub_title': 'dsadsa', 'instruction': 'You Must','…elimit': '01:05:19', 'title': 'asdsa', id: 133}",
'{"sub_title": "sdasdsa", "instruction": "None", "cre…melimit": "05:30:09", "title": "asda", "id": 131}'
];
angular.forEach(detail.questionaires, function (value, key) {
var questionaire = JSON.parse(value.replace(/'/g, '"').replace(/id:/g, '"id":'));
me.record.questionaires.push({ "id": questionaire.id, "title": questionaire.title });
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="myModule">
<div ng-controller="myController">
<div><strong>me.record.questionaires:</strong></div>
<div ng-repeat="q in me.record.questionaires">
<div>{{q}}</div>
</div>
</div>
</div>

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>

Select using ng-option is not updating model in controller

I have the following array:
[{
"Id": 3,
"Name": "A"
},
{
"Id": 3,
"Name": "B"
},
{
"Id": 3,
"Name": "C"
}]
I am using this in the following Angular view:
<select ng-model="selectedCategory" ng-options="category.Name for category in categories"></select>
<pre>{{selectedCategory | json}}</pre>
<button type="button" ng-click="move()">Move</button>
The controller looks like:
var moveCategoryController = function ($scope, category, categoriesService) {
var getCategories = function () {
categoriesService.getCategories()
.success(function (result) {
$scope.categories = [];
for (var i = 0; i < result.Results.length; i++) {
var cat = result.Results[i];
if (cat.Id !== category.Id) {
$scope.categories.push(cat);
}
}
$scope.selectedCategory = $scope.categories[0];
})
.error(function () {
$scope.errorMessage = "There was a problem loading the categories.";
});
};
getCategories();
$scope.move = function () {
alert($scope.selectedCategory.Name);
};
}
bpApp.controller("moveCategoryController", moveCategoryController);
For info, the category object injected into the controller is a category object (the controller is being used in a modal and the category is passed to it from the parent page).
The Problem
On loading, the select is bound to the data fine, and when the user changes the select list the <pre> content updates correctly with the newly selected category.
The problem is when I click the Move button, which calls the move() function on the controller scope, the selectedCategory property of the scope has not been updated. For example, if I select the category "B", the alert still pops up with "A".
So, it seems that the ng-model is updated in the view, but not in the controller?!

Master Detail with Nested JSON

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.

Categories

Resources