I have set up a small HTML page with a ngView directive in it, which should update when any of the navigation links are clicked, after calling a Restlike API. This bit seems to work, especially when there's no params to use in the request.
As soon as I try to add a param it fails with the following error:
TypeError: undefined is not a function
at Object.<anonymous> (http://localhost:8080/dashboard/js/factorys.js:11:16)
at Object.invoke (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.js:3965:17)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.js:3807:37
at getService (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.js:3929:39)
at invoke (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.js:3956:13)
at Object.instantiate (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.js:3976:23)
at $get (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.js:7315:28)
at link (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular-route.js:907:26)
at nodeLinkFn (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.js:6752:13)
at compositeLinkFn (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.js:6146:13) <div class="container ng-scope" ng-view="">
My angular scripts look like this:
controllers.js:
var dashboardControllers = angular.module('dashboardControl',['ngRoute','dashboardFactory']);
dashboardApp.controller('PlayersController', function ($scope, $routeParams, Player) {
$scope.players = Player.findAll();
});
dashboardApp.controller('PlayerDetailsController', function ($scope, $routeParams, Player) {
$scope.playerId = $routeParams.playerId;
$scope.player = Player.get();
});
dashboardApp.controller('OverviewController', function ($scope, $routeParams, Overview) {
$scope.overview = Overview.query();
});
factories.js:
var dashboardFactories = angular.module('dashboardFactory',['ngResource']);
dashboardFactories.factory("Overview", ['$resource',
function ($resource) {
return $resource('webresources/overview/', {}, {
query: {method: 'GET', isArray: false}
});
}]);
dashboardFactories.factory("Player", ['$resource',
function ($scope, $resource) {
return $resource('webresources/player/:playerId', {}, {
findAll: {method: 'GET', isArray: true},
get: {method: 'GET', params: {playerId: $scope.playerId}, isArray: false}
});
}]);
The overview controller work, its the Player Controller I seem to be having the error message for.
I created working example here.
NOTE: It uses UI-Router instead of ngRoute... which I guess is really the right way... but the concept is exactly the same.
Firstly we would define the resoruce "player":
.factory('player', ['$resource', function($resource){
return $resource('player:playerId.json', {}, {
findAll: {method: 'GET', isArray: true},
get: {method: 'GET', isArray: false}
});
}])
Why we used url like this player:playerId.json? Just because of the plunker. We profit here from the parametrized URL template ... which allows in the plunker have player.json for a list and player + playerId +.json for each player.
States definition:
// States
$stateProvider
.state('list', {
url: "/list",
templateUrl: 'tpl.list.html',
controller: 'PlayerListCtrl',
resolve : {
list : ['player', function(player){return player.findAll();}]
}
})
.state('player', {
url: "/player/:playerId",
templateUrl: 'tpl.player.html',
controller: 'PlayerCtrl',
resolve : {
player : ['player', '$stateParams'
, function(player, $stateParams){
return player.get({playerId: $stateParams.playerId});
}]
}
})
The most important part is this:
player : ['player', '$stateParams'
, function(player, $stateParams){
return player.get({playerId: $stateParams.playerId});
}]
Because we are letting angular to inject the $stateParams into our resolve. We then take the playerId parameter and pass that into Templated URL - so it will result in player1.json, player2.json,...
In real life it would url like this 'server/api/resource/:id' - but the logic will be the same.
And these are consuming controllers:
.controller('PlayerListCtrl', ['$scope', 'list', function ($scope, list) {
$scope.list = list;
}])
.controller('PlayerCtrl', ['$scope', 'player', function ($scope, player) {
$scope.player = player;
}])
Check it here
Related
// Angular factory
wpApp.factory('Posts', function ($resource) {
return $resource(appInfo.api_url + 'posts/:ID', {
ID: "#id"
})
});
// Index page controller.
wpApp.controller('ListCtrl', ['$scope', 'Posts', function ($scope, Posts) {
$scope.page_title = "Blog Listing";
Posts.query(function(res) {
$scope.posts = res;
})
}]);
// Show page controller.
wpApp.controller('DetailCtrl', ['$scope', '$stateParams', 'Posts', function($scope, $stateParams, Posts) {
console.log($stateParams);
Posts.get({ ID : $stateParams.id }, function(res) {
$scope.post = res;
});
}]);
// Angular app configuration.
wpApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state( 'list', {
url: '/',
controller: 'ListCtrl',
templateUrl: appInfo.template_directory + 'templates/list.html'
})
.state('detail', {
url: '/posts/:id',
controller: 'DetailCtrl',
templateUrl: appInfo.template_directory + 'templates/detail.html'
})
});
From code in DetailsCtrl, I understood that we are going to do a GET request by calling url with id == id passed to angular url params.
But what does this ID: "#id" do? What is it used for? How does it work?
Per the docs.
If the parameter value is prefixed with #, then the value for that parameter will be extracted from the corresponding property on the data object (provided when calling actions with a request body). For example, if the defaultParam object is {someParam: '#someProp'} then the value of someParam will be data.someProp. Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action method that does not accept a request body)
So in your case that second argument is simply configuring defaults for your URI. In this case it is making the URI value of ID dynamic.
What I have are ID values that I want to append to a URL to query the correct object in an API
HTML:
<div ng-init="tileID = '38'"></div>
Controller:
app.controller('appCtrl',['$scope', '$http', 'ImageTiles', function($scope, $http, ImageTiles){
$scope.$watch('tileID', function(){
console.log($scope.tileID);
$scope.get = function(tileID){
ImageTiles.get({id: tileID}, function(data){
})
}
});
}]);
Factory
app.factory('ImageTiles', ['$resource', function ($resource) {
return $resource('http://api/v1/tiles/:id', {
id: "#id"
},
{
'query': {
method: 'GET',
isArray: false
}
});
}]);
Example:
In the HTML I have a value of 38 that's always manually entered in. Would then like to take that value (38) and create the URL of http://api/v1/tiles/38 to bring back the data of the object that has an ID of 38 on the API.
I can get the ID value but have no idea how to pass it along and make the call to the API.
There's really no need to play with your $resource before consuming it, by wrapping it in that get function, just call it directly:
app.controller('appCtrl',['$scope', '$http', 'ImageTiles', function($scope, $http, ImageTiles){
$scope.$watch('tileID', function(){
console.log($scope.tileID);
ImageTiles.get({id: $scope.tileID}).$promise.then(function(tile) {
$scope.tile = tile;
});
});
}]);
I look like
// Get
$http.get("http://api/v1/tiles/"+ tileID)
.success(function (response) {
// Do somthing
});
Hope It helps you
In your watch you can specify the arguments newValue and oldValue, and use newValue to call your API.
app.controller('appCtrl',['$scope', '$http', 'ImageTiles', function($scope, $http, ImageTiles){
$scope.$watch('tileID', function(newValue, oldValue){
console.log(newValue);
ImageTiles.get({id: newValue}, function(data){
console.log(data);
});
});
}]);
It seems only a basic skeleton for the Angular Application.To provide actual routing mechanism you have to modify app.js file for configuring different routes.
please see below sample code :
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when("/Apps", {templateUrl: "partials/App.html", controller: "driversController"}).
when("/App/:id", {templateUrl: "partials/App.html", controller: "appCtrl"}).
otherwise({redirectTo: '/App'});
}]);
I'm getting unknown provider error when i'm trying to use resolve from a state. The object i want returned seems to be returned correctly, so i can't really figure out what the problem is.
This is my first angular project so if I something seems wierd it probably is.
The error: https://docs.angularjs.org/error/$injector/unpr?p0=boardProvider%20%3C-%20board%20%3C-%20AppCtrl
var ponk = angular.module("ponk", ["ui.router", "ngResource"]);
ponk.config(['$stateProvider', '$urlRouterProvider',
"$locationProvider", function ($stateProvider, $urlRouterProvider,
$locationProvider) {
$locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/');
$stateProvider.state('board', {
url: '/b/:slug',
templateUrl: 'views/board.html',
controller: "AppCtrl",
controllerAs: "pk",
resolve: {
board: function($stateParams, boardFactory) {
var board = {};
if($stateParams.slug) {
board = boardFactory.get({slug:$stateParams.slug}).$promise;
}
return board;
}
}
});
}]).run(function($state) { $state.go('board'); });;
ponk.factory("boardFactory", ["$http", "$resource",
function($http, $resource) {
return $resource('/board/:slug', {slug:'slug'}, {update: { method: "PUT" }});
}]);
ponk.controller("AppCtrl", ["$scope", "$http", "boardFactory", "board",
function($scope, $http, boardFactory, board ) {
console.log(board); // correct object, but error
}]);
EDIT:
discovered the above code works. The problem is when i add this to the controller:
var pk = this;
var pk.board = board;
I have the following code.
controller.js
angular.module('LiveAPP.main',['LiveAPP.factory'])
.controller('mainCtrl', ['$scope','$http', '$location','dataFactory',mainCtrl])
.directive('ratehome',function(){
return {
restrict:"E",
template: "<div id='rateYo'></div>",
link: function(scope, ele, attrs){
console.log("NEW",scope.recentArtist)
}
}
})
function mainCtrl($scope,$http,$location,dataFactory){
$scope.getRecentArtists = function(){
return $http({
method: 'GET',
url: '/artistsearch',
params: {getArtist: "all"}
}).then(function(recent){
$scope.recentArtist = recent.data
})
};
$scope.getRecentArtists();
$scope.recentArtist = ""
$scope.$watch('recentArtist',function(newValue,oldValue){
$scope.recentArtist = newValue
})
}
test.html
<ratehome></ratehome>
<ratehome></ratehome>
<ratehome></ratehome>
What happens here is upon instantiation of my controller(routing is set up correctly) there is a $http GET request that responds with data that I need that gets assigned to $scope.recentArtist. I want this data to be accessible in my link function, but it's not. I have a feeling that my directive is compiling before this request is sent. Is there any way around this? What is odd to me is that when I console.log(scope) and check in Chrome Developer Tools my data is there. Yet when I console.log(scope.recentArtist) its empty similar to its state in the controller. I was thinking I could maybe make the $http.get in my directive, but that seems a little awkward to me.
I have been having trouble with this problem for a few days, hopefully somebody can help me out.
If you're using angular ui-router you could also use resolve. With resolve you can do the $http before the controller starts.
You can use resolve to provide your controller with content or data that is custom to the state. resolve is an optional map of dependencies which should be injected into the controller.
If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the
$stateChangeSuccess event is fired.
from the docs.
Please have a look at the demo below or this jsfiddle.
angular.module('demoApp', ['ui.router'])
.config(function ($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider.state('home', {
url: '/',
template: '<ratehome></ratehome><ratehome></ratehome><ratehome></ratehome>',
controller: 'mainCtrl',
resolve: {
artists: function (artistsService) {
console.log('Resolve');
return artistsService.get(); //'/artistsearch',//artistsService.get();
}
}
});
})
.controller('mainCtrl', ['$scope', '$http', '$location', 'artists', MainCtrl])
.directive('ratehome', function () {
return {
restrict: "E",
template: '<div id="rateYo"><ul><li ng-repeat="artist in recentArtist">{{artist}}</li></ul></div>',
link: function (scope, elem, attrs) {
console.log("NEW", scope.recentArtist);
}
}
})
.factory('artistsService', function ($http) {
return {
get: function () {
console.log('getting');
return $http({
method: 'GET',
url: 'http://crossorigin.me/http://www.mocky.io/v2/558b30615f3dcbc414067170', //'/artistsearch',
//params: {getArtist: "all"}
}).then(function (recent) {
//console.log(recent);
return recent.data;
});
}
};
});
function MainCtrl($scope, $http, $location, artists) {
$scope.recentArtist = artists;
console.log('ctrl', artists);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.1/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"></script>
<div ng-app="demoApp">
<div ui-view=""></div>
</div>
AS your directive is not using isolated scope, that does mean you can directly access your controller scope inside your directive. I'd suggest you to to put that $watch inside directive link instead of your controller. That would intimate you that the ajax has been completed and data got changed & you get those changed value inside watcher function.
Code
.directive('ratehome',function(){
return {
restrict:"E",
template: "<div id='rateYo'></div>",
link: function(scope, ele, attrs){
$scope.$watch('recentArtist',function(newValue,oldValue){
console.log("NEW",newValue)
});
}
}
})
Your link function is running before the $http response comes back as you suspect. You can wait for it by using $broadcast and $on:
angular.module('LiveAPP.main',['LiveAPP.factory'])
.controller('mainCtrl', ['$scope','$http', '$location','$rootScope','dataFactory',mainCtrl])
.directive('ratehome',function(){
return {
restrict:"E",
template: "<div id='rateYo'></div>",
link: function(scope, ele, attrs){
scope.$on('artistLoaded', function(){
console.log("NEW",scope.recentArtist);
});
}
};
});
function mainCtrl($scope,$http,$location,$rootScope,dataFactory){
$scope.getRecentArtists = function(){
return $http({
method: 'GET',
url: '/artistsearch',
params: {getArtist: "all"}
}).then(function(recent){
$scope.recentArtist = recent.data
$rootScope.$broadcast('artistLoaded');
});
};
$scope.getRecentArtists();
$scope.recentArtist = "";
$scope.$watch('recentArtist',function(newValue,oldValue){
$scope.recentArtist = newValue
});
}
This way the code will not run until the response has been returned and set
I have issue with Angular factory, I've tried many ways, but it's same..
This is error:
TypeError: Cannot read property 'getSchedule' of undefined
at new <anonymous> (http://127.0.0.1:4767/js/ctrls/main.js:3:19)
at d (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js:35:36)
at Object.instantiate (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js:35:165)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js:67:419
at link (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-route.min.js:7:248)
at N (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js:54:372)
at g (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js:47:256)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js:46:377
at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js:48:217
at F (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js:52:28) <ng-view class="app-content ng-scope" ng-hide="loading">
I have constructed main app this way:
'use strict';
// Declare chat level module which depends on views, and components
angular.module('BenShowsApp', [
'ngRoute',
'ngResource',
'mobile-angular-ui',
'BenShowsApp.filters',
'BenShowsApp.services',
'BenShowsApp.directives',
'BenShowsApp.controllers'
]).
config(['$routeProvider',
function ($routeProvider) {
$routeProvider
.when('/schedule', {
templateUrl: 'partials/main.html',
controller: 'MainCtrl'
});
}]);
//Initialize individual modules
var services = angular.module('BenShowsApp.services', []);
var factories = angular.module('BenShowsApp.factories', []);
var controllers = angular.module('BenShowsApp.controllers', []);
var filters = angular.module('BenShowsApp.filters', []);
var directives = angular.module('BenShowsApp.directives', []);
and tried use this factory/service
services.factory('tvRage', function($http) {
var tvRage = {};
tvRage.getSchedule = function() {
return $http({
method: 'get',
url: 'http://services.tvrage.com/feeds/fullschedule.php',
params: {
country: 'US',
key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
}).then(function (response) {
return response.data;
});
};
return tvRage;
});
with this controller
controllers.controller('MainCtrl', ['$scope','$http','tvRage',
function ($scope, $http, tvRage) {
tvRage.getSchedule().success(function(data){
var parser = new X2JS();
var x2js = parser.xml_str2json(data);
$scope.request = x2js;
}).error(function(){
alert('nouuu');
});
}
]);
$http works when it's all in controller, but from functional side that request should be in factory I think.
You are returning $q promise from the factory. It does not have the methods success and error they are special functions added by $http in the returned httpPromise (which is just an extension of QPromise).
You can either change your factory to return httpPromise by removing the then chaining:
return $http({
method: 'get',
url: 'http://services.tvrage.com/feeds/fullschedule.php',
params: {
country: 'US',
key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
});
Or chain it in your controller with standard q promise functions then/ catch.
controllers.controller('MainCtrl', ['$scope','$http','tvRage',
function ($scope, $http, tvRage) {
tvRage.getSchedule().then(function(data){
var parser = new X2JS();
var x2js = parser.xml_str2json(data);
$scope.request = x2js;
}).catch(function(){
alert('nouuu');
});
}
]);
But with the specific error you are getting it looks like possibly in your original code your DI list does not match argument list. Re-verify by logging what is tvRage and other arguments injected in the controller. This could easily happen because of argument mismatch in the original code. Ex:-
.controller('MainCtrl', ['$scope','tvRage', function ($scope, $http, tvRage){
//Now tvRage will be undefined and $http will be tvRage.
Working Demo
angular.module('app', []).controller('ctrl', ['$scope', '$http', 'tvRage',
function($scope, $http, tvRage) {
tvRage.getSchedule().success(function(data) {
console.log(data)
}).error(function() {
alert('nouuu');
});
}
]).factory('tvRage', function($http) {
var tvRage = {};
tvRage.getSchedule = function() {
return $http({
method: 'get',
url: 'http://services.tvrage.com/feeds/fullschedule.php',
params: {
country: 'US',
key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
});
};
return tvRage;
});;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
</div>