AngularJS not displaying array from rest call when loading view - angularjs

I am new to angularJS and may be missing something small but I have a spring boot backend providing a rest api for angular front end. I am using ngResourse in my factory and this appears to work fine. The problem is when I load the view, my array of items is not displayed, what confuses me more is that I made a function to load the same data again in my controller and the view displays the details then. I have included the code that's relevant.
Here is my factory:
angular.module('myApp.products.module')
.factory('productsFactory', ['$resource', function($resource) {
return $resource('http://localhost:8080/product/findall/:id', {id : '#id'});
}])
Here is Controller (as vm):
angular.module('myApp.products.module')
.controller('productsCtrl', [
'$rootScope', '$log', '$state', '$timeout', '$location', '$mdDialog', '$resource', 'productsFactory',
function ($rootScope, $log, $state,
$timeout, $location, $mdDialog, $resource, productsFactory) {
var scope = this;
var init = function () {
scope.products = productsFactory.query();
scope.test(1);
};
scope.test = function(productId) {
scope.oneProduct = productsFactory.get({id: productId});
scope.products = productsFactory.query(/*console.log*/);
};
init();
}])
Here is Html:
<div layout="column" ng-cloak>
<div layout="column">
<div>
<md-button class="md-no-focus" ng-click="vm.test(2)">test</md-button>
Test Result: {{ vm.oneProduct }}
</div>
<div>
<ul ng-repeat="prod in vm.products">
<li> {{ prod.id }}</li>
<li> {{ prod.name }}</li>
</ul>
</div>
</div>
When the view loads the oneProduct shows up fine. The list items show nothing, but when I press the test button and load call the query again they all show up. Any help would be greatly received.
Thanks.

Looks like you are trying to use one method for two different queries, you said 'findall' but at the same time you need to send an ID. I think the best idea is split this method.
//Inject the new service before to use it.
scope.test = function(productId) {
scope.oneProduct = productByIdFactory.get({id: productId});
scope.products = productsFactory.query(/*console.log*/);
};
// Get all products
.factory('productsFactory', ['$resource', function($resource) {
return $resource('http://localhost:8080/product/findall', {
listAllProducts: {
method: "GET",
isArray: true
});
}])
// Get product by id
.factory('productByIdFactory', ['$resource', function($resource) {
return $resource('http://localhost:8080/product/findById/:id', {
listProduct: {
method: "GET",
params: {id: "#productId"},
isArray: false
});
}])

Related

Data-binding between ngInclude and ngView

I want to make a sidebar with list item that can be dynamically changed based on the settings page.
My app request settings.json via factory() and then called it in a controller. The controller will be used by settings.html (ngView) and sidebar.html (ngInclude).
The json will return Boolean value that also can be changed on setting page that contain checkbox which return true if check and false if not checked. I use ngShow on the sidebar to display/hide the list items.
How can I made the sidebar to reflect the changes as I tick the checkbox?
settings.factory.js
var settingsFactory = angular.module('settingsFactory', []);
settingsFactory.factory('SettingsFilterFactory', ['$http', function ($http) {
var settingsFactory = {};
settingsFactory.getSettings = function () {
return $http.get('app/data/settings.json');
};
return settingsFactory;
}]);
controller
var settingsControllers = angular.module('settingsControllers', ['settingsFactory']);
settingsControllers.controller('SettingsFilterController', ['$scope', '$http', 'SettingsFilterFactory', function ($scope, $http, SettingsFilterFactory) {
$scope.settings;
$scope.status;
getSettings();
function getSettings() {
SettingsFilterFactory.getSettings()
.then(function (response) {
$scope.settings = response.data;
}, function (error) {
$scope.status = 'Unable to load: ' + error.message;
});
}
}]);
app.js
var app = angular.module('app', ['ngRoute', 'settingsControllers']);
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/settings', {
title: 'Settings',
templateUrl: 'app/components/settings/settings.html',
controller: 'SettingsFilterController'
}).
otherwise({
redirectTo: '/home'
});
}]);
My index.html is something like this:
...
<body>
<section class="sidebar">
<div ng-include="'app/components/sidebar/sidebar.html'"></div>
</section>
<section class="content">
<div ng-view></div>
</section>
</body>
...
sidebar.html
<ul class="sidebar-menu" ng-controller="SettingsFilterController">
<li ng-show"settings.hiddenMenu">This is secret link</li>
</ul>
settings.html
...
<div class="checkbox">
<input type="checkbox" ng-model="settings.hiddenMenu" ng-true-value=true ng-false-value=false> Check this to show hidden menu
</div>
...
Try something like this (untested):
settings.factory.js
var settingsFactory = angular.module('settingsFactory', []);
settingsFactory.factory('SettingsFilterFactory', ['$http', function ($http) {
var settingsFactory = {};
settingsFactory.getSettings = function () {
return $http.get('app/data/settings.json');
};
settingsFactory.hiddenMenu= true;
settingsFactory.someOtherSetting = {};
return settingsFactory;
}]);
sidebar controller
settingsControllers.controller('SidebarController', ['$scope', '$http', 'SettingsFilterFactory', function ($scope, $http, SettingsFilterFactory) {
//do this in each controller, so that the factory becomes a property of $scope and can be seen in the HTML
$scope.settingsFactory = SettingsFilterFactory;
}
sidebar.html
<ul class="sidebar-menu" ng-controller="SidebarController">
<li ng-show"settingsFactory.hiddenMenu">This is secret link</li>
</ul>
settings.html
...
<div class="checkbox">
<input type="checkbox" ng-model="settingsFactory.hiddenMenu" ng-true-value=true ng-false-value=false> Check this to show hidden menu
</div>
...
Essentially, you are binding the settingsFactory object which is a singleton to each $scope that is provided by each controller. Each controller is able to change the property on the factory object, which is then visible in all other controllers that have injected this object.

How to load a new page and pass data from api?

I have page with list of teams. On click I need title and members of that team on another page. I just loaded teams in the list. Code is here:
angular.module('my-app').controller('MainController', function($scope, $http) {
$http.get("http://...").success(function(response) {
$scope.details = response;
});
});
and
<article ng-app="seed-angular">
<section ng-controller="MainController as mc">
<ul>
<li ng-repeat="x in details">
{{x.name}}
</li>
</ul>
</section>
</article>
As mentioned in the comments by Claies, you need to use a router in your app. From the docs:
Application routes in Angular are declared via the $routeProvider, which is the provider of the $route service. This service makes it easy to wire together controllers, view templates, and the current URL location in the browser. Using this feature, we can implement deep linking, which lets us utilize the browser's history (back and forward navigation) and bookmarks.
There are some libraries in which you can do that. I'll use ngRoute as an example as it comes with Angular.js.
To do so, you need to add the angular-route.js and load the ngRoute module:
var app = angular.module('seed-angular', [
'ngRoute'
]);
You also need to configure your $routeProvider to setup your URL routes, like this:
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/team', {
templateUrl: 'team-list.html',
controller: 'MainController'
}).
when('/team/:teamId', {
templateUrl: 'team-detail.html',
controller: 'TeamDetailController'
}).
otherwise({
redirectTo: '/team'
});
}
]);
In the routes above, we've configured /team to route to a template page team-list.html. Note that the <a href=""> now passes a parameter teamId:
<article>
<section ng-controller="MainController">
<ul>
<li ng-repeat="x in details">
{{x.name}}
</li>
</ul>
</section>
</article>
We also configured /team/:teamId to route to a template page team-detail.html (note that :teamId means it's a parameter):
<article>
<section ng-controller="TeamDetailController">
Back
<div ng-repeat="x in details">
<strong>{{x._id}}</strong>
<ul>
<li>
{{x.firstName}} {{x.lastName}}
</li>
</ul>
</div>
</section>
</article>
In which you will be able to access teamId via $routeParams.teamId to retrieve the team details:
app.controller('TeamDetailController', function($scope, $http, $routeParams) {
var url = "http://est-teams.estudent.hr/api/teams/" + $routeParams.teamId + "/members";
$http.get(url).success(function(response) {
$scope.details = response;
});
});
See this fiddle for a working example.
if you want to pass the data you can create service and create function that store or set your data, then call it in your first controller that called the data and call it again in your next page controller here is the code :
this is the service :
myApp.service('sharedProperties', function() {
var stringValue = 'test string value';
var objectValue = {
data: 'test object value'
};
return {
getString: function() {
return stringValue;
},
setString: function(value) {
stringValue = value;
},
getObject: function() {
return objectValue;
}
};
});
you can call it in your controller like this :
myController.controller('UserUpdateController', ['$scope', '$http','sharedProperties',
function($scope, $http, sharedProperties ) {
$scope.updateUser = function() {
.....
id = sharedProperties.getString();
};
}
]);
and set the data from your previous page like this :
myController.controller('UserListController', ['$scope', '$http','sharedProperties',
function($scope, $http, sharedProperties) {
$scope.updateUser = function(id){
console.log("from index "+id);
sharedProperties.setString(id);
};
}
]);

Angularjs - Calling only one of many subcontrollers/ multiple controllers

I have an index page wherein I define two controllers. I want to call one main controller always (should be rendered always) and the other is called only for specific sub URL calls. Should I make one nested within another, or I can keep them independent of each other? I don't have access to change routes or anything, only the controller.
Right now when I use the template (HTML) mentioned, it calls/renders both controllers, even though url is say /index
Only for /index/subPage, I want both controllers to be rendering.
/index
/index/subPage
HTML:
<div ng-controller="MainCtl" ng-init=initMain()>
<p> Within ctller2 {{results}} </p>
</div>
<div ng-controller="Ctller2"> <!-- should not be displayed unless /subPage/mainUrl is rendering -->
<p> Within ctller2 {{results}} </p>
</div>
JS:
app.controller('MainCtl', ['$scope', '$http', '$location', function ($scope, $http, $location) {
$http.get('xx/mainUrl').then(function(data) {
$scope.results = someDataJSON;
console.log(someDataJSON);
});
$scope.initMain = function() {
$scope.initMethods();
}
}]);
app.controller('Ctller2', ['$scope', '$http', '$location', function ($scope, $http, $location) {
// This controller gets initialized/rendered/called even when xx/mainUrl is called, and when xx/subPage/mainUrl is called too..
$http.get('xx/subPage/mainUrl').then(function(data) {
$scope.results = someDataJSON;
console.log(someDataJSON);
})
$http.get('xx/subPage').then(function(data) {
$scope.results = data.data;
console.log(data);
})
angular.element(document).ready(function () {
alert('Hello from SubCtl, moving over from main controller to here');
});
}]);
What am I doing wrong? I'm new to Angular.js
You can conditionally initiate a controller using ng-if. So you could try something like this:
<body ng-controller="MainCtrl">
<div ng-controller="ctrl1">{{hello}}</div>
<div ng-controller="ctrl2" ng-if="showCtrl2">{{hello}}</div>
</body>
and then set the value of the variable in a parent controller by checking the current url using $location.path()
var app = angular.module('plunker', []);
app.config(function($locationProvider){
$locationProvider.html5Mode(true);
});
app.controller('MainCtrl', function($scope, $location) {
$scope.showCtrl2 = ($location.path() === 'my path');
});
app.controller('ctrl1', function($scope){
$scope.hello = 'ctrl1 says hello';
});
app.controller('ctrl2', function($scope){
$scope.hello = 'ctrl2 says hello';
});
But it's a bit hacky and for a larger project a more robust solution would require using something like ui.router.

AngularJS load view only after $resource has loaded

In my project I want the pages to load only after the requests on those pages are finished. For example I have a view that contains an ng-init to getArtistsInfo. I want that the page that contains that ng-init to be shown only after the request to getArtistsInfo have loaded:
<div class="row" ng-controller="ArtistController" ng-init="getArtistInfo(params)">
<div class="col-lg-2 col-md-2 col-sm-3 col-xs-4">
<img ng-src="{{ artist.image_url }}" alt="{{ artist.name }}">
</div>
<div class="col-lg-10 col-md-10 col-sm-9 col-xs-8">
{{ artist.bio }}
</div>
</div>
To make the request to the backend api I use resources:
angular.module('ArtistService', []).factory('Artist', ['$resource',
function ($resource) {
return $resource('/api/artists/:artistId', {
artistId: '#id'
},
{
getArtistInfo: {
method: 'GET',
url: '/api/artists/getArtistInfo'
}
});
}
]);
In controller I have:
angular.module('ArtistController', []).controller('ArtistController', ['$scope', 'Artist', '$location', '$routeParams',
function ($scope, Artist, $location, $routeParams) {
$scope.getArtistInfo = function (params) {
$scope.artist = Artist.getArtistInfo(params);
}
$scope.params = $routeParams;
}
]);
How can I make the whole page load only after I receive the response from the getArtistInfo request?
I have tried using resolve in $routeProvider but that doesn't seem to work.
Thank you very much!
You use the ngRoute module? In this case you can add a resolve parameter to load the artist info (see: https://docs.angularjs.org/api/ngRoute/provider/$routeProvider). If a resolve parameter returns a promise event ($resource does this), the router waits for the promise to be resolved, so in your case, it would wait until the artist info has been loaded:
$routeProvider.when('...', {
controller: 'ArtistController',
// ...
resolve: {
artist: ['Artist', '$route', function(Artist, $route) {
return Artist.getArtistInfo($route.current.params).$promise;
}]
}
})
and use the loaded artist in the controller:
.controller('ArtistController', [..., 'artist', function(..., artist) {
$scope.artist = artist; // This is the loaded artist
}]);

AngularJS ng-click and nothing happens

AngularJS
var theApp = angular.module('theApp', []);
theApp.controller('ContentController', ['$scope', function ($scope) {
}]);
theApp.controller('MenuSideController', ['$scope', 'CategoryService',
function ($scope, CategoryService){
CategoryService.getList()
.success(function(list){
$scope.list = list;
});
}
]);
function menuType(id) {
console.log(id);
//do something
}
HTML
<ul>
<li ng-repeat="company in list">{{ company.name }}</li>
</ul>
I'm trying to get a click method working using AngularJS but nothing works, there is no error message or even a console.log. What am I doing wrong?
The expression passed to ngClick is evaluated in the current scope. This means you have to add the function to the $scope variable in the controller:
theApp.controller('MenuSideController', ['$scope', 'CategoryService',
function ($scope, CategoryService){
CategoryService.getList()
.success(function(list){
$scope.list = list;
});
$scope.menuType = function(id) {
// do things here
}
}
]);

Resources