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

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

Related

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.

Return data from node service to angularjs controller

I was following the Mean Machine book. One of the examples is to connect to mongo db and retrieve all the users. The book seems a little out dated( Already got 2-3 errors which i fixed).
Issue is that my service does not connect to mongodb and return the data. .From UI, the service returns html for the current page.The table is not being populated.
But when I invoke the service through postman, i get the data
The code for the User retrieval is this.
User Service:
angular.module('userService', [])
.factory('User', function($http) {
//create new object for factory
var userFactory = {};
//get single user
userFactory.get = function(id) {
return $http.get('/api/user/' + id);
};
//get all users
userFactory.all = function() {
return $http.get("/api/user/");
};
...
Shown only the relevant parts above.
Controller:
angular.module('userCtrl', ['userService'])
.controller('userController', function(User) {
var vm = this;
//processing control
vm.processing = true;
//Grab all users on load
User.all()
.then(function(udata) {
console.log(udata.data);
vm.processing = false;
vm.users = udata.data;
});
})
HTML(all.html)
<tr ng-repeat="person in user.users">
<td>{{person._id}}</td>
<td>{{person.name}}</td>
<td>{{person.username}}</td>
<td class="col-sm-2">
<a ng-href="/users/{{ person._id }}" class="btn btn-danger">Edit</a>
</td>
</tr>
Main Angular Files
angular.module('userApp', [
'ngAnimate',
'app.routes',
'authService',
'mainCtrl',
'userCtrl',
'userService'
])
//app config to integrate token to request
.config(function($httpProvider) {
//attach auth interceptor to http requests
$httpProvider.interceptors.push('AuthInterceptor');
})
HTML File(index.html)
<body ng-app="userApp" ng-controller="mainController as main">
....
<main class="container">
<!-- ANGULAR VIEWS -->
<div ng-view></div>
</main>
</body>
The users table is inserted into the above template.
Route:
angular.module('app.routes', ['ngRoute'])
.config(function($routeProvider, $locationProvider) {
$routeProvider
//users
.when('/users', {
templateUrl: 'app/views/pages/users/all.html',
controller: 'userController',
controllerAs: 'user'
});
});
Is the issue due to the version mismatch? Read that the $http service has been upgraded.
Thanks for the help

ng-route- Parameters with Special Characters

<nav>
<ul class="list">
<li ng-repeat="product in data | startFrom:currentPage*pageSize | limitTo:pageSize">
<span>{{product}}</span>
<a href="#getAttributes/{{product}}/{{code}}/{{name}}/{{hierName}}">
{{product.prd_name}}
</a>
</li>
</ul>
</nav>
I am calling the ng-route as above .
myApp.config([ '$routeProvider', function($routeProvider) {
$routeProvider
.when('/getAttributes/:product/:code/:name/:hierName', {
templateUrl : 'view/attributes.jsp',
controller : 'attributeController'
}).otherwise({
redirectTo : '/mainMenu'
});
} ]);
The Product is a JSON Object. The above code routes to otherwise if the product object contains "/" character. Any idea on how to resolve it.
First Approach: Use ng-click to Store the Model In A Service Then Go to Location
Start by adding an ng-click to your list item which calls get attributes on your controller taking caring to remove the href.
<nav>
<ul class="list">
<li ng-repeat="product in data | startFrom:currentPage*pageSize | limitTo:pageSize" ng-click="getAttributes(product)">
<span>{{product}}</span>
{{product.prd_name}}
</li>
</ul>
</nav>
Next, reconfigure your route to simply /getAttributes
myApp.config([
'$routeProvider', function($routeProvider) {
$routeProvider
.when('/getAttributes/', {
templateUrl: 'view/attributes.jsp',
controller: 'attributeController'
}).otherwise({
redirectTo: '/mainMenu'
});
}
]);
In your controller, add a method called getAttributes which will be invoked by the aforementioned ng-click. It will call a service and use its setModel function to persist which model was clicked. Finally, it will route getAttributes.
myApp.controller('someController', [
'$scope', '$location', 'someService', function($scope, $location, someService) {
$scope.getAttributes = function(model) {
someService.setModel(model);
$location.url('getAttributes');
};
}
]);
The attribute controller will call the same service and use its getModel method to set the model.
myApp.controller('attributeController', [
'$scope', 'someService', function($scope, someService) {
$scope.model = someService.getModel();
}
]);
The service would like something like the following:
myApp.service('someService', function() {
var model;
return {
getModel: function() {
return model;
},
setModel: function(newModel) {
model = newModel;
}
};
});
This approach is only suitable if you'll never be routing to getAttributes in any other way. A more flexible second approach is possible.
Second Approach: Use Resolve to Get the Model, Set In the Service Then Load in the Controller
Modify the href to be an ng-href and have it be getAttribute/123 or some such.
<nav>
<ul class="list">
<li ng-repeat="product in data | startFrom:currentPage*pageSize | limitTo:pageSize">
<span>{{product}}</span>
<a ng-href="/getAttributes/{{product.id}}">{{product.prd_name}}</a>
</li>
</ul>
</nav>
Modify your route to accept the product ID as seen below. Additionally, add a resolve object. This object will call someService which will in turn call your server, retrieve the products attributes and set the model variable equal to the result.
myApp.config([
'$routeProvider', function($routeProvider) {
$routeProvider
.when('/getAttributes/:id', {
templateUrl: 'view/attributes.jsp',
controller: 'attributeController',
resolve: {
getModel: function(someService) {
return someService.get();
}
}
}).otherwise({
redirectTo: '/mainMenu'
});
}
]);
As mentioned before, the service will take the passed ID on the /getAttributes/id route and use it to call the server. An alternative is to have the service call a repository for this instead. The end result is the same: set the model variable equal to the result of the call to the server.
myApp.service('someService', [
'$http', function($http) {
var model;
return {
get: function(id) {
return $http.get('api/attribute/' + id).then(function(response) {
model = response.data;
});
},
getModel: function() {
return model;
},
setModel: function(newModel) {
model = newModel;
}
};
}
]);
Finally, your attributeController will behave the same as the first example. It set $scope.model equal to someService.getModel() thereby providing the products attributes.
The advantage to this approach is that your route may deep linked from anywhere in your app without issue.

AngularJS Bind Data returned from Async Call in ngrepeat

I have an index.html file which I'm using as a layout/template with a header, ngview directive and footer. I'm using the 'resolve' property successfully on the views rendered in ngview to show data after async call completes, but I'm trying to link the navigation bar in the header which is in the main index.html file to another controller that is populated with values returned from the async call. I seem to have two issues.
Why won't the data from my factory "bind" to the ngrepeat such that when data becomes available it updates the view.
How can I implement a resolve on the index.html file itself? I figured having a resolve on a path to "/" would require all the data in the resolve to load before rendering the page, but I guess it's just for whatever is in the ngview component and not the whole page.
index.html (abbreviated)
<html ng-app="app">
<body>
<nav class="navbar navbar-default header-navbar" role="navigation">
<ul class="nav navbar-nav" data-ng-controller="NavController">
<li class="active">Home</li>
<li ng-repeat="brand in brands">
{{brand.name}}
</li>
</ul>
</nav>
<div ng-view></div>
</body>
</html>
app.js (abbreviated)
var app = angular.module('app',['MockDataService','ngSanitize']).
config(['$routeProvider',
function($routeProvider){
$routeProvider
.when('/',
{
controller: 'SiteController',
templateUrl: 'partials/home.html',
resolve:{
'Brands':function(BrandFactory){
return BrandFactory.promise;
}
}
})
...
shop.controller('SiteController',SiteController)
.controller('NavController',NavController);
controllers
var SiteController = function ($scope, SiteFactory, BrandFactory){
$scope.settings = SiteFactory.settings;
$scope.brands = BrandFactory.getBrands();
console.log($scope);
}
var NavController = function($scope, BrandFactory, $location)
{
$scope.brands = BrandFactory.getBrands();
console.log($scope);
}
service.js
var MockDataService = angular.module('MockDataService',[])
.factory('BrandFactory',function($http){
var factory = {};
var brands = {};
var promise = $http.get('/data/brands.json')
.success(function(data){
brands = data;
});
factory.getBrands = function(){
return brands;
};
factory.getBrandBySlug = function(slug){
for(var i=0; i< brands.length; i++)
{
if(brands[i].slug === slug) return brands[i];
}
};
factory.promise = promise;
return factory;
})
This worked for me: Binding variables from Service/Factory to Controllers
Also watching the videos at egghead.io helped a ton (http://www.egghead.io/lessons)

Resources