AngularJS load view only after $resource has loaded - angularjs

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
}]);

Related

How do I setup $http.post in AngularJS to send data to my url?

All the documentation out there for AngularJS 1.6 is extremely ambiguous. Everybody has tutorials for how to do $http.get requests, but nothing on how to properly setup $http.post requests for version 1.6.
All I'm trying to do is setup a controller that gives my mini-project the functionality to allow the user to type in his/her city into the input box on the Home page and, when they submit it, the information for their forecast will appear on the Forecast page. So, theoretically, the controller will 'POST' data into the URL to be able to retrieve information from the Open Weather API. The Routes and everything else works just fine...I only need help for this POST method stuff.
My apologies for my code looking unintelligible. I just posted what I had.
As an aside, does anybody have REALLY good resources for documentation on AngularJS 1.6?
(function () {
'use strict';
// MODULE
angular.module("WeatherApp", ['ngRoute', 'ngResource', 'http'])
// ROUTES
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'templates/home.html',
controller: 'homeCtrl'
})
.when('/forecast', {
templateUrl: 'templates/forecast.html',
controller: 'forecastCtrl'
});
})
// SERVICES
.service('cityService', function () {
this.city = "Chicago";
})
// CONTROLLERS
.controller("homeCtrl", function ($scope, cityService) {
$scope.city = cityService.city;
$scope.$watch('city', function () {
cityService.city = $scope.city;
});
})
.controller("forecastCtrl", function ($scope, $http, cityService) {
$scope.city = cityService.city;
$http.post("http://api.openweathermap.org/data/2.5/forecast/daily", {q: $scope.city, cnt: 2, appid: "8a3dfe91838e8409da30958ed6b68932"}).then(function (data) {
console.log();
});
});
})();
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h4>Forecast by City</h4>
<div class="form-group"><input ng-model="city" type="text" class="form-control"></div>
Get Forecast
</div>
</div>
Here is an example of using $http.post. The API returns with an error and I hope you can investigate on that.
angular
.module('MyApp', []);
angular
.module('MyApp')
.controller('ForecastController', [
'$scope',
'$http',
function($scope,
$http) {
$scope.vm = {
city: 'Newyork',
errorMessage: ''
}
$scope.getForecast = function() {
var vm = $scope.vm;
console.log('Getting forecast for ' + vm.city + ' city');
/*$http.get("http://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b1b15e88fa797225412429c1c50c122a1")*/
$http.post("http://api.openweathermap.org/data/2.5/forecast/daily", {
q: vm.city,
cnt: 2,
appid: "8a3dfe91838e8409da30958ed6b68932"
})
.then(function(data) {
console.log('Got forecast successfully.');
}, function(error) {
var message = 'There was an error getting forecast';
console.log(message);
console.log(error);
vm.errorMessage = message;
});
}
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<style>
.item-row {
margin-bottom: 10px;
}
.item-row:last-child {
margin-bottom: 0;
}
</style>
<div ng-app="MyApp">
<div ng-controller="ForecastController">
<div class="item-row" ng-show="vm.errorMessage">
{{vm.errorMessage}}
</div>
<div class="item-row">
<input type="text" ng-model="vm.city" />
</div>
<div class="item-row"> You entered: {{vm.city}}</div>
<div class="item-row">
<button ng-click="getForecast()">Get Forecast</button>
</div>
</div>
</div>

AngularJS not displaying array from rest call when loading view

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
});
}])

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.

Ui-Router Create New Controller In View

Given that I am going to the following state:
$stateProvider.state('applicant', {
url: "/admin/applicants/:name",
templateProvider: function($http, $stateParams) {
return $http({
method: 'GET',
url: 'http://example.com/applicant',
params: {
request: $stateParams.name,
}
}).then(function successCallback(html) {
return html.data;
});
},
controller: 'SendToCtrl'
});
With the view that is being returned by $http looking like this:
<script>
app.controller('ChildController', ['$scope', function($scope) {
$scope.applicant = <?php echo $applicant ?>;
}]);
</script>
<div class="row" ng-controller="ChildController">
<div class="col-md-12">
{{applicant.State}}
</div>
</div>
I get a Argument 'ChildController' is not a function error. I read that in the regular ng-view calling a new controller like this is not supported, but couldn't find an answer for ui-router. What would be the proper way to do templating then if I need the template to display data that is only there once the view is served?
So I couldn't generate the controller from within the view like I wanted, so instead I used the controller generated by the state and attached the php variable to the view, then picked it back up again from said set controller, like so:
State Definition:
$stateProvider.state('applicant', {
url: "/admin/applicants/:name",
templateProvider: function($http, $stateParams) {
return $http({
method: 'GET',
url: 'http://example.com/applicant',
params: {
request: $stateParams.name,
}
}).then(function successCallback(html) {
return html.data;
});
},
controller: 'SendToCtrl'
});
View:
<?php
$applicant = str_replace('"', "'", $applicant);
?>
<div class="row">
<div id="fetchMe" class="col-md-12">
</div>
</div>
<script>
applicant = <?php echo $applicant ?>;
$('#fetchMe').data('key',applicant);
</script>
SendToCtrl:
app.controller('SendToCtrl', ["$scope", "$stateParams", "$state", function($scope, $stateParams, $state) {
console.log($stateParams);
$scope.applicant = $("#fetchMe").data('key');
console.log($scope.applicant);
}]);
:)

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);
};
}
]);

Resources