AngularJs crashes when trying to access data in directive - javascript

So I'm new to Angular and trying to do an app. My server is very basic and written in go. This is my angular code-
(function() {
var app = angular.module('dashboard', []);
app.controller("JobsController", function() {
this.currentJob = 0;
this.jobs = jobs;
this.setCurrentJob = function(index) {
this.currentJob = index;
};
});
var jobs = [
{
'id': 1,
'requester': 'Prakash Sanker',
'description': 'I want you to give me the entire world'
},
{
'id': 2,
'requester': 'MJ Watson',
'description': 'Please give me Spiderman, preferably upside down...but Im not fussy'
},
{ 'id': 3,
'requester': 'Josh Lyman',
'description': 'Matthew Santos as president...or Jed Bartlet..but please not anyone Republican'
},
{
'id': 4,
'requester': 'Barry Allen',
'description': 'A frictionless suit that wont burst into flames when I run at a zillion miles an hour...its for a friend'
},
{
'id': 5,
'requeter': 'A. Bambaata',
'description': 'Boombox, prime condition, from the 80s. Go back in time if you have to'
}
];
})();
This is my index.html -
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script type="text/javascript" src="/dashboard.js"></script>
<body>
<div class="dashboard">
<div ng-controller="JobsController as jobsCtrl">
<div class="jobs">
{{jobsCtrl.jobs[0]}}
</div>
</div>
</div>
</body>
This causes this error on my server
2015/07/29 12:18:02 http: panic serving [::1]:56857: runtime error: invalid memory address or nil pointer dereference
goroutine 18 [running]:
net/http.funcĀ·009()
/usr/local/go/src/pkg/net/http/server.go:1093 +0x9e
runtime.panic(0x20ed40, 0x4efaed)
/usr/local/go/src/pkg/runtime/panic.c:248 +0xda
html/template.(*Template).escape(0x0, 0x0, 0x0)
/usr/local/go/src/pkg/html/template/template.go:52 +0x30
Why is this happening?

You cannot use angular's default start {{ and end }} brackets in a go template because the go server interprets your angular as go template arguments and pipelines.
"Arguments" and "pipelines" are evaluations of data
To solve this you can make use of angular's $interpolate in your config to change the delimiters that define angular code:
var app = angular.module('dashboard', []);
app.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('[{');
$interpolateProvider.endSymbol('}]');
});
Alternatively if your pages are static you can just use Go's built in file server to not cause confusion with templates:
http://golang.org/pkg/net/http/#example_FileServer

Related

Trouble including JSON in angular controller

I've got an angular controller containing a json object
app.controller("buildControl", function($scope){
$scope.buildData = [
{
'title':'Workspace',
'options': [{
'title': 'Studio'
},{
'title': 'Garage'
},{
'title': 'Blank'
}]
},{
'title':'Frame',
'options': [{
'title': 'Studio'
},{
'title': 'Garage'
},{
'title': 'Blank'
}]
}]
});
I'm using this data to iterate the object's arrays using ng-repeat (plus I have other functions in my controller that use the data).
Everything runs fine until I moved the json out into it's own file and used $http to include it.
app.controller("buildControl",function($http, $scope){
$http.get("json/buildData.js")
.success(function(response){
$scope.buildData = response.data;
}).error(function(error){
console.log(error);
});
});
The only errors I get in my console are from all my other functions breaking without the data they need to run, plus an 'undefined' from my console log. So I know that it's going to '.error'.
I figured maybe I had the filepath wrong, have tried changing prefixing it with '../' to go up a directory out of my controllers. No luck. If I put the json in the same folder and just write;
.get("buildData.js")
I get this as the error
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /buildData.js was not found on this server.</p>
</body></html>
But then I sort out the filepath and it goes back to undefined. So I don't think this is the problem.
(I'm doing this using MAMP and having no HTTP errors associated with trying to get files locally).
Any help?
Not sure if you need to use $http here, why not include your data from a service, something like:
angular
.module('MyApp')
.service('buildData', buildData);
function buildData() {
return [{
'title':'Workspace',
'options': [{
'title': 'Studio'
},{
'title': 'Garage'
},{
'title': 'Blank'
}]
...
}]
};
Your controller would look something like this:
.controller('buildControl', buildControl);
buildControl.$inject = ['$scope', 'buildData'];
function buildControl($scope, buildData) {
$scope.buildData = buildData;
...
}

Absolute URLs behave like relative URLs

I have a bunch of links in the footer of my app (Angular 1.3.15).
html
<section class="footer-projects">
<div class="container">
<div class="row">
<section ng-repeat="area in ::links">
<h4 ng-bind="area.title"></h4>
<ul>
<li ng-repeat="project in ::area.projects">
</li>
</ul>
</section>
</div>
</div>
</section>
ng-repeat loops through this
js constants
'use strict';
require('./footer.module.js')
.constant('FooterLinkConstants', [
{
title: 'Space',
projects: [
{
name: 'Galaxy Zoo',
url: 'http://www.galaxyzoo.org/'
},
{
name: 'Moon Zoo',
url: 'http://www.moonzoo.org/'
},
{
name: 'Solar Storm Watch',
url: 'http://www.solarstormwatch.com/'
},
{
name: 'Planet Hunters',
url: 'http://planethunters.org/'
},
{
name: 'Planet Four',
url: 'http://planetfour.org/'
},
{
name: 'Radio Galaxy Zoo',
url: 'http://radio.galaxyzoo.org/'
},
{
name: 'Stardate M83',
url: 'http://www.projectstardate.org/'
},
{
name: 'Disk Detective',
url: 'http://diskdetective.org/'
}
]
}
]);
and this is my directive
js directive
'use strict';
require('./footer.module.js')
.directive('appFooter', appFooter);
// #ngInject
function appFooter($state, FooterLinkConstants) {
var directive = {
link: appFooterLink,
restrict: 'A',
replace: true,
templateUrl: 'footer/footer.html'
};
return directive;
function appFooterLink(scope) {
scope.links = FooterLinkConstants;
});
}
}
All works fine in development. However when I deploy to a remote server address, that address gets prepended to the values in my list above. For example:
instead of
http://www.galaxyzoo.org/
I get
http://preview.zooniverse.org/folgerdemo/http://galaxyzoo.org/
You can have a look at the live example (just inspect the links in the footer)
So why does that happen?
Other questions, like this, suggest to use absolute urls by including the protocol in the addresses; which I am already doing.
I'm pretty sure I'm missing something obvious, but could do with some fresh pair of eyes.
Right. Unfortunately only I could know the answer - not because it was difficult obviously, but because I didn't provide all the pieces of information in my question.
I built my application as a spin-off of somebody else's. The app uses Gulp as task manager.
It turns out that this line
gulp.src(filename).pipe(replace(/(href=")(?![http|\/\/])/g, '$1' + prefix)),
where prefix value is dependent on the type of AWS S3 bucket is used, was replacing my urls.
Once I commented out that line, things were back to normal.
Thanks to people who commented and apologies for wasting your time ;)

AngularJS Nested Object Array Pathway

I have a factory, which goes into a controller, and I am trying to get data from that display on an HTML page. I am having trouble specifying an Object's pathway however.
My Factory:
app.factory('APIMethodService', function() {
var Head = "api.example.com";
return {
apis:
[{
accounts: [
{
v1: [
{
uri: Head+"/v1/accounts/",
item1: "AccountNumber",
item2: "MoneyInAccount"
}],
v2: [
{
uri: Head+"/v2/accounts/",
item1: "AccountNumber",
item2: "MoneyInAccount"
}]
}
],
customers: [
{
v1: [
{
uri: Head+"/v1/customers/",
item1: "CustomerName",
item2: "CustomerID",
item3: "CustomerEmail"
}]
}
]
}]
};
});
My Controller:
app.controller('APIController', function($scope, APIMethodService) {
$scope.title = "API";
$scope.apiList = APIMethodService;
$scope.accountList = $scope.apiList.accounts.v1;
$scope.accountList2 = $scope.apiList[0][0];
});
My HTML
<div ng-controller="APIController">
<div id="api" class="row">
<div class="col-xs-12">
<div class="row" style="font-size:20px">
{{title}} Page!
<table class="table table-striped">
<tr ng-repeat="api in apiList | orderBy:'uri' | filter:search">
<td>{{api.uri}}</td>
<td>{{api.item1}}</td>
<td>{{api.item2}}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
The errors I get are in regards to the Controller trying to parse out the individual objects I wish to grab, like accounts or customers, and then any version v#, they may have.
So it will say something such as
TypeError: Cannot read property 'v1' of undefined
I just need some help specifying the proper pathways into my factory service.
You have a few problems. First, you are referring to the object returned from the factory incorrectly. APIMethodService is the factory that you're injecting, so you need to first reference the object that that factory is returning like this:
APIMethodService.apis
This will give you your entire JSON object.
From there, the rest of your object is made up of arrays of objects, so referring to 'v1' won't do you any good. You need to specify an index instead. If you want v1, you'll need:
APIMethodService.apis[0].accounts[0].v1
This will give you the v1 array, which again is an array of objects.
Customers would be:
APIMethodService.apis[0].customers[0].v1
The first problem you have is that the factory returns an object with a single property called apis. So basically this $scope.apiList.accounts.v1 should be $scope.apiList.apis.accounts.v1. Bu that's not all as this won't either work since dotting(.) into apis is an array you'd have to use the index. In this case it would be $scope.apiList.apis[0] and then you could .accounts[0].v1 which is also an array containing a single object.
Now if you can I would suggest to you that you'd change how you represent this data structure.
This is how you could do it.
app.factory('APIMethodService', function() {
var Head = "api.example.com";
return {
accounts: {
v1: {
uri: Head+"/v1/accounts/",
items: ["AccountNumber","MoneyInAccount"]
},
v2: {
... // skipped for brevity
}
},
customer: {
... // code skipped for brevity
}
};
});
And then it's just a matter of dotting into your APIMethodService-object like APIMethodService.accounts.v1.items[0] if you want the AccountNumber method name.
Constructing your url could then be done like this.
var baseUrl = APIMethodService.accounts.v1.uri; // 'api.example.com'
var url = baseUrl + APIMethodService.accounts.v1.items[0]; // 'AccountNumber'
// url = "api.example.com/v1/accounts/AccountNumber"
Again, this is one way you could do it but this can be further enhanced upon. The examples I provided are simply for demo purposes and this is not in any way the only way to do it.
Expanding upon recieved comments/questions your service (and data representation) could now look like this.
app.factory('APIMethodService', function() {
var Head = "api.example.com";
return {
accounts: {
v1: {
uri: Head+"/v1/accounts/",
items: [
{
name:'AccountNumber',
description:'Show the account number'
},
{
name:'AccountOwner',
description:'Show information about the owner of the account'
},
{
name:'MoneyInAccount',
description:'Show money in the Account'
}
]
},
v2: {
... // skipped for brevity
}
},
customer: {
... // code skipped for brevity
}
};
});
// Get descriptions
var accountNumberDescription = APIMethodService.accounts.v1.items[0].description; // 'Show the account number'
var accountOwnerDescription = APIMethodService.accounts.v1.items[1].description; // 'Show information about the owner of the account'
var moneyInAccountDescription = APIMethodService.accounts.v1.items[2].description; // 'Show money in the Account'
By using objects with properties like this it's alot easier to understand what you are trying to do. With arrays with indexes you'd have to know or take a look at the source to see what's going on. Here, someone viewing your code they can instantly understand that it is the description you are getting.

$http dependency injection throwing error

I've been working on the CodeSchool AngularJS app, which I've understand so far, but when I began using dependency injections, specifically, $http in order to make an call for my JSON data the app stops working, and I don't know why. Originally with the first line uncommented the app worked as it should using the variable gems declared within the closure, which is exactly the same code now found in products.json. I commented out the first line of the controller, and added the appropriate changes to for dependency injection, but now the app doesn't load at all, and it throws the error found below (also see $http-injection.png).
app.controller('StoreController', [ '$http', function($http) {
//this.products = gems; <-- works like this with data in closure
var store = this;
store.products = [ ]; // no erros on page load
$http.get('/data/products.json').success(function( data ) {
store.products = data;
});
}]);
Error: [ngRepeat:dupes] http://errors.angularjs.org/1.3.0-beta.10/ngRepeat/dupes?p0=product%20in%20storeCtrl.products&p1=string%3A%0D
at Error (native)
at http://angularjs.dev/angular-1.3/angular.min.js:6:457
at http://angularjs.dev/angular-1.3/angular.min.js:204:24
at Object.<anonymous> (http://angularjs.dev/angular-1.3/angular.min.js:108:144)
at Object.applyFunction [as fn] (<anonymous>:778:50)
at g.$digest (http://angularjs.dev/angular-1.3/angular.min.js:109:211)
at g.$delegate.__proto__.$digest (<anonymous>:844:31)
at g.$apply (http://angularjs.dev/angular-1.3/angular.min.js:112:325)
at g.$delegate.__proto__.$apply (<anonymous>:855:30)
at g (http://angularjs.dev/angular-1.3/angular.min.js:73:287) angular.js:10811
(anonymous function) angular.js:10811
(anonymous function) angular.js:7962
g.$digest angular.js:12560
$delegate.__proto__.$digest VM8634:844
g.$apply angular.js:12858
$delegate.__proto__.$apply VM8634:855
g angular.js:7380
x angular.js:8527
y.onreadystatechange
product.json
[
{
name: 'Hexagonal',
price: 250.00,
description: 'The six faces of the hexaonal gem have a habit to excite the eye, and attract good luck.',
canPurchase: true,
soldOut: false,
images: [ ],
reviews: [
{
stars: 5,
body: "I love this product!",
author: "mtpultz#gmail.com"
},
{
stars: 1,
body: "This product sucks!",
author: "mtpultz#hater.com"
}
]
},
{
name: 'Dodecahedron',
price: 1015.25,
description: 'Some gems have hidden qualities beyond their luster, beyond their shine... Dodeca is one of those gems.',
canPurchase: true,
soldOut: false,
images: [
"img/gem-06.gif",
"img/gem-02.gif",
"img/gem-01.gif"
],
reviews: [
{
stars: 3,
body: "I think this gem was just OK, could honestly use more shine, IMO.",
author: "mtpultz#hotmail.com"
},
{
stars: 4,
body: "Any gem with 12 faces is for me!",
author: "mtpultz#casensitive.ca"
}
]
},
{
name: 'Pentagonal Gem',
price: 5.95,
description: 'Origin of the Pentagonal Gem is unknown, hence its low value. It has a very high shine and 12 sides however.',
canPurchase: true,
soldOut: false,
images: [
"img/gem-02.gif",
"img/gem-06.gif",
"img/gem-01.gif"
],
reviews: [
{
stars: 4,
body: "The mystery of the Pentagonal Gem makes it sooo fascinating!",
author: "mtpultz#peanutbutter.com"
},
{
stars: 5,
body: "I can't get enough of the five faces of the Pentagonal Gem!",
author: "mtpultz#ketchup.ca"
}
]
}
];
Originally I was going to try and figure out how to use $log as well, and when I had $log injected it appears as if the json is received (see $http-and-$log-injection.png attached) based on the chrome batarang plugin's output, but the app still doesn't work either way, only the JSON appears on right side of batarang output.
app.controller('StoreController', [ '$http', '$log', function($http, $log) {
//this.products = gems;
var store = this;
store.products = [ ]; // no erros on page load
$http.get('/data/products.json').success(function( data ) {
store.products = data;
});
}]);
You shouldn't use the minified version of Angular while developing. You'll get better error messages when using the non-minified version. But even when you're using the minified version you can get a pretty good idea of what the problem is by visiting the url mentioned first in the exception: http://errors.angularjs.org/1.3.0-beta.10/ngRepeat/dupes?p0=product%20in%20storeCtrl.products&p1=string%3A%0D
It seems like you have duplicates in products.json. Without seeing the whole contents of products.json or any of your markup that would be my best guess.
--
Update: It seems like data is a string and not an array. This is probably because the response body from the server is not properly formatted JSON. Instead of traversing objects in an array, ng-repeat traverses characters in a string and throws an error on the second tab character (encoded as %0D) it detects. I've created a plnkr with properly and a bad response as an example: http://plnkr.co/edit/XbPuXkykzv36NyH3sSeu?p=preview
You should use $scope:
$scope.store = {};
$scope.store.products = [ ]; // no erros on page load
$http.get('/data/products.json').success(function( data ) {
$scope.store.products = data;
});
ngRepeat:dupes error occurs if there are duplicate keys(values in your array/json) in an ng-repeat expression, this is because AngularJS uses keys to associate DOM node with ng-repeated items. The requested data products.json may have fields that contain the same value, to solve this, you can append a track by $index expression in your ng-repeat so that the association of DOM node items will be keyed by the index of the your array/json instead of the value of each item.
E.G.
<div ng-repeat="product in Store.products track by $index"></div>

Toggling through multiple options in Angular Js

I am creating a small todo app to learn Angular. I am stuck in one place. My task will be listed as list of links. Each time user clicks on it its status should change. So when a user
clicks on a task its status becomes, doing. Then in next click it become done. And one click makes it later. This should also reflect be stored back to the $scope from where this loaded. I have no idea, what will be the best way to do this in Angular.
Right now I have $scope.tasks storing all the tasks and $scope.tasks.$status is holding the available stasuses.
var app = angular.module("app");
app.controller('Ctrl', function($scope) {
$scope.tasks = [
{id: 1, text: 'Do something.', status: 'doing'},
{id: 2, text: 'Undo that thing.', status: 'done'},
{id: 3, text: 'Redo it again.', status: 'started'},
{id: 4, text: 'Responsive', status:'later'}
];
$scope.tasks = {
status: ['doing','done', 'later' ]
};
$scope.clicked = function() {
//some logic.
};
}
Below is the html.
<div ng-app="app" ng-controller="Ctrl" class"container">
<div ng-repeat="task in tasks" >
<li>
<a href="#" ng-click="clicked()" id="{{task.id}}">
{{task.text}} - {{task.status}}
<a/>
</li>
</div>
</div>
Is whatever I done so far correct? What is the best approach to toggle through the options? Is there any functions for that?
There're tons of solutions depending on what you want to do with this next. The simplest one seems to be:
$scope.clicked = function(task){
task.status = nextStatus(task.status);
};
function nextStatus(st){
return $scope.statuses[$scope.statuses.indexOf(st) + 1];
}
Note that you also should pass task to the function:
<a ng-click='clicked(task)'>

Categories

Resources