AngularJS ui-router update parent controller model - angularjs

So I have these states set up:
// Now set up the states
$stateProvider.state('collections', {
url: '/collections/:centerId',
templateUrl: '/assets/tpl/collections/index.html',
controller: 'CollectionsController',
controllerAs: 'controller',
resolve: {
// Resolve our collections before the state loads
collections: ['$stateParams', 'Api', function ($stateParams, api) {
// Get our center id
var centerId = $stateParams.centerId;
// Return our collections
return api.get('/api/collections', { centerId: centerId });
}]
},
data: {
requireLogin: true,
pageTitle: 'Collections'
}
}).state('collections.import', {
url: '/import',
templateUrl: '/assets/tpl/collections/import.html',
controller: 'ImportCollectionsController',
controllerAs: 'controller',
data: {
requireLogin: true,
pageTitle: 'Import your collections'
}
});
If you look at the 2 states, you can see that in the parent state I resolve all the current collections from our API. This is then assigned to the CollectionsController scope (this.collections).
In the ImportCollectionsController I can get access to that data via the $scope:
.controller('CollectionsController', ['$stateParams', 'collections', function ($stateParams, collections) {
// Assign this to a variable
var self = this;
// Get our collections
self.collections = collections;
/// --- removed for brevity --- ///
}])
.controller('ImportCollectionsController', ['$stateParams', '$scope', 'CollectionsService', 'toastr', function ($stateParams, $scope, collections, toastr) {
// Assign this to a variable
var self = this;
// Get our center id
self.centerId = $stateParams.centerId;
/// --- removed for brevity --- ///
// Save our data
self.save = function () {
// Import our results into our collections
self.import = collections.import(self.centerId, self.results);
// If the import is successful
self.import.promise.then(function (response) {
// Get our original collection array
var originals = $scope.$parent.controller.collections.data
// Update our original collection with the new imported items
originals.push(response.data);
// Set our results to nothing
self.results = null;
// Display a success message
toastr.success('Your collections have been imported successfully.');
});
};
}])
Just incase you need it, this is my API service:
.service('Api', ['$http', 'HttpHandler', function ($http, handler) {
// Private function to build our request
var buildRequest = function (url, method, data, params) {
var model = {
method: method,
url: url,
data: data,
params: params
};
return $http(model);
};
// GET
this.get = function (url, params) {
return handler.loadData(buildRequest(url, 'GET', null, params));
};
// POST
this.post = function (url, data) {
return handler.loadData(buildRequest(url, 'POST', data));
};
// PUT
this.put = function (url, data) {
return handler.loadData(buildRequest(url, 'PUT', data));
};
// DELETE
this.delete = function (url, data) {
return handler.loadData(buildRequest(url, 'DELETE', data));
};
}])
.service('HttpHandler', ['ErrorService', function (service) {
// Function to handle promises
this.loadData = function (promise) {
// Declare our model
var model = {
data: null,
loading: true,
promise: promise,
error: null
};
// If our promise succeeds
promise.then(function (response) {
// Store the data
model.data = response.data.result || response.data;
}, function (error) {
// Process our error
model.error = service.process(error);
});
// Finally
promise.finally(function () {
// Set our loading flag to false regardless if there is an error or not
model.loading = false;
});
// Return our model
return model;
};
}])
So in theory, when my save function is called in the ImportCollectionsController if the promise is successful I add the array to our existing array. What I expect is that if I navigate back to the collections state I should now see my new data, but I don't.
Can anyone tell me why?
Update
So I have changed my states to this (note the resolves)
.config(['$stateProvider', function ($stateProvider) {
// Now set up the states
$stateProvider.state('collections', {
url: '/collections/:centerId',
templateUrl: '/assets/tpl/collections/index.html',
controller: 'CollectionsController',
controllerAs: 'controller',
resolve: {
// Resolve our collections before the state loads
collections: ['$stateParams', 'Api', function ($stateParams, api) {
// Get our center id
var centerId = $stateParams.centerId;
// Return our collections
return api.get('/api/collections', { centerId: centerId });
}]
},
data: {
requireLogin: true,
pageTitle: 'Collections'
}
}).state('collections.import', {
url: '/import',
templateUrl: '/assets/tpl/collections/import.html',
controller: 'ImportCollectionsController',
controllerAs: 'controller',
resolve: {
originals : ['collections', function (collections) {
return collections.data;
}]
},
data: {
requireLogin: true,
pageTitle: 'Import your collections'
}
}).state('collections.create', {
url: '/create',
templateUrl: '/assets/tpl/collections/save.html',
controller: 'SaveCollectionController',
controllerAs: 'controller',
data: {
requireLogin: true,
pageTitle: 'Save your collection'
}
});
}])
and in my ImportCollectionsController I have changed the save function to this:
.controller('CollectionsController', ['$stateParams', 'collections', function ($stateParams, collections) {
// Assign this to a variable
var self = this;
// Get our collections
self.collections = collections;
console.log(self.collections);
}])
.controller('ImportCollectionsController', ['$stateParams', 'originals', 'CollectionsService', 'toastr', function ($stateParams, originals, collections, toastr) {
// Assign this to a variable
var self = this;
// Get our center id
self.centerId = $stateParams.centerId;
// Save our data
self.save = function () {
// Import our results into our collections
self.import = collections.import(self.centerId, self.results);
// If the import is successful
self.import.promise.then(function (response) {
// Update our original collection with the new imported items
originals.push(response.data);
// Set our results to nothing
self.results = null;
// Display a success message
toastr.success('Your collections have been imported successfully.');
});
};
// Cancel our import
self.cancel = function () {
// Set our results to nothing
self.results = null;
}
}])
and I am still getting the same issue. When I navigate back to collections they are not listed.

ok, to fix this I changed my states to this:
.config(['$stateProvider', function ($stateProvider) {
// Now set up the states
$stateProvider.state('collections', {
url: '/collections/:centerId',
templateUrl: '/assets/tpl/collections/index.html',
controller: 'CollectionsController',
controllerAs: 'controller',
resolve: {
// Resolve our collections before the state loads
collections: ['$stateParams', 'Api', function ($stateParams, api) {
// Get our center id
var centerId = $stateParams.centerId;
// Return our collections
return api.get('/api/collections', { centerId: centerId });
}]
},
data: {
requireLogin: true,
pageTitle: 'Collections'
}
}).state('collections.import', {
url: '/import',
templateUrl: '/assets/tpl/collections/import.html',
controller: 'ImportCollectionsController',
controllerAs: 'controller',
data: {
requireLogin: true,
pageTitle: 'Import your collections'
}
}).state('collections.create', {
url: '/create',
templateUrl: '/assets/tpl/collections/save.html',
controller: 'SaveCollectionController',
controllerAs: 'controller',
data: {
requireLogin: true,
pageTitle: 'Save your collection'
}
});
}])
There is no need to create a separate resolve on the child states because they inherit from the parent.
With that in mind, I can change my ImportCollectionsController to this:
.controller('ImportCollectionsController', ['$stateParams', 'collections', 'CollectionsService', 'toastr', function ($stateParams, originals, collections, toastr) {
// Assign this to a variable
var self = this;
// Get our center id
self.centerId = $stateParams.centerId;
// Save our data
self.save = function () {
// Import our results into our collections
self.import = collections.import(self.centerId, self.results);
// If the import is successful
self.import.promise.then(function (response) {
// Update our original collection with the new imported items
for (var i = 0; i < response.data.length; i++)
originals.data.push(response.data[i]);
// Set our results to nothing
self.results = null;
// Display a success message
toastr.success('Your collections have been imported successfully.');
});
};
// Cancel our import
self.cancel = function () {
// Set our results to nothing
self.results = null;
}
}])
and that has resolved the issue.

Related

pass data without adding into url

$stateProvider.state('academicYearCatalogue', {
url: '/academicYearCatalogue/:programKey',
templateUrl: 'app-ace/academicYearCatalogue/views/academicYearCatalogue-detail.tpl.html',
controller: 'AcademicYearCatalogueDetailCtrl as AcademicYearCatalogueDetailCtrlVM',
resolve: {
academicYearList: ['AcademicYearService', '$stateParams',
function (AcademicYearService, $stateParams) {
var obj = {
programKey: $stateParams.programKey,
}
return AcademicYearService.getAllYear(obj)
.$promise.then(function (result) {
return result.data;
});
}]
}
});
i want to pass data between controllers via ui-router v 0.2.8 but don't want to add to url, how do i achieve this?
this programkey is coming from some controller and i want to use this in resolve but not with url.how do i achieve this.
angular js version is 1.2.20
Using params property of state you can pass the data as given below
params: {
user_desisred_params_name: null,
}
So the complete code is given below-
$stateProvider.state('academicYearCatalogue',
{ url: '/academicYearCatalogue',
templateUrl: 'app-ace/academicYearCatalogue/views/academicYearCatalogue-detail.tpl.html',
controller: 'AcademicYearCatalogueDetailCtrl as AcademicYearCatalogueDetailCtrlVM',
resolve: {
academicYearList: ['AcademicYearService', '$stateParams',
function (AcademicYearService, $stateParams) {
var obj = {
programKey: $stateParams.programKey,
}
return AcademicYearService.getAllYear(obj)
.$promise.then(function (result) {
return result.data;
});
}]
},
params:{
yourKey : programKey
}
});

Bad timing or order of functions with resolves on Ui-Router

I have an app where my main state makes a resolve makes an http call and fetches an array.
Then, the child array is supposed to display one object from this array, but it seems that the variables from the controller are defined too early and don't get updated properly, so the child comes out empty.
I've tried it without http call (var array = [array]), and it works fine, but not with the http call.
Any tips on how to fix this?
Here's the controllers:
.controller('appCtrl',['$scope', 'SearchService','fair', function($scope, SearchService, fair){
$scope.data = SearchService;
$scope.datafairs = $scope.data.flatexhibitors;
console.log($scope.datafairs);
}])
.controller('ChildController',['$scope', 'exhibitor', '$filter', function($scope, exhibitor, $filter){
$scope.$watch(function() { return $scope.fair; }, function(newVal) {
$scope.fairs = newVal;
console.log($scope.fairs);
$scope.chosenexhibitor = $filter("filter")($scope.fairs, {'slug':exhibitor}, true);
}, true);
}])
The service:
.factory("SearchService", function($http) {
var service = {
flatexhibitors : [],
datafairs : [],
getAllExhibitors : function (wop) {
var searchindex = wop;
console.log(searchindex);
var url = '../register/backend/databaseconnect/getexhibitors.php';
var config = {
params: {
search: searchindex
},
cache:true
};
$http.get(url, config).then(function (data) {
service.datafairs = data.data.rows;
for (var i in service.datafairs) {
service.flatexhibitors.push(service.datafairs[i].doc);
};
return service.flatexhibitors;
});
}
}
return service;
})
And the states:
.config(function($stateProvider) {
$stateProvider.state('berliner', {
url: '/berlinerliste',
params : {search: 'Berliner 2017'},
resolve: {
fair: function(SearchService, $stateParams) {
return SearchService.getAllExhibitors($stateParams.search);
}
},
views: {
'header': {
templateUrl: 'header.htm'
},
'main':{
templateUrl: 'bl2017.htm',
controller: 'appCtrl'
}
}
})
.state('berliner.exhibitor', {
url: '/{id}',
resolve: {
exhibitor: function($stateParams) {
var slug = $stateParams.id;
return slug;
}
},
views: {
'header': {
templateUrl: 'header.htm'
},
'wop':{
templateUrl: 'exhibitor.htm',
controller: 'ChildController'
}
}
})
})
I've managed to replicate the issue in a Plunkr.
Change the getAllExhibitors to return a promise like below:
getAllExhibitors : function (wop) {
var searchindex = wop;
console.log(searchindex);
var url = '../register/backend/databaseconnect/getexhibitors.php';
var config = {
params: {
search: searchindex
},
cache:true
};
return $http.get(url, config).then(function (data) {
service.datafairs = data.data.rows;
for (var i in service.datafairs) {
service.flatexhibitors.push(service.datafairs[i].doc);
};
return service.flatexhibitors;
});
}

Server calls for every View Redirect(Change) in AngularJS

I have an usual AngularJS Controller:
controllers.UController = function ($scope, uFactory) {
$scope.data1 = uFactory.getDataUsingAjax1();
$scope.data2 = uFactory.getDataUsingAjax2();
$scope.data3 = uFactory.getDataUsingAjax3();
...
}
The mentioned fields (data1 - data3) gets populated using Ajax call.
I also have several Views.
When I run my app the first time, I can see all the 3 Ajax calls in order to populate data1-data3.
But every time I redirect to another View, I can see that this population starts again and again.
In my understanding it's not really a SPA architecture or it's a bad SPA.
Is this how it should work or I am missing something?
Here are the details:
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'UController',
templateUrl: '/Partial/View1.html'
})
.when('/View2',
{
controller: 'UController',
templateUrl: '/Partial/View2.html'
})
.otherwise({ redirectTo: '/View3' });
}]);
myApp.factory('uFactory', function () {
var factory = {};
data1 = [];
data2 = [];
factory.getAjaxData1 = function () {
$.ajax({
url: url,
type: 'GET',
contentType: "application/json",
async: false,
success: function (result) {
data1= result;
}
});
return data1;
}
factory.getAjaxData2 = function () {
$.ajax({
url: url,
type: 'GET',
contentType: "application/json",
async: false,
success: function (result) {
data2= result;
}
});
return data2;
}
}
var controllers = {};
controllers.uController = function ($scope, $location, uFactory) {
$scope.data1 = uFactory.getAjaxData1();
$scope.data2 = uFactory.getAjaxData2();
}
Redirection is done by href link:
a href="#/View1"Do it/a

Child View/Route that Resolves from Different Service than Parent

I'm having an issue loading a child view/route while resolving a GET from a different service from the parent view/route.
Within the parent view, each ion-item links to /#/tab/categories/{{ category.listing_category_id }}
When clicked the URL populates (for a flash) with the correct category ID, the child's service successfully receives the category ID, runs the subsequent GET request & returns the data as a promise...that all works as intended. The issue is that the child view/route never loads. All help/guidance is very much appreciated.
Parent
angular.module('rainbowPages.tab.categories', [])
.config(function($stateProvider, $urlRouterProvider) {
// UI Router
$stateProvider.state('tab.categories', {
url: '/categories',
views: {
'tab-categories': {
templateUrl: 'views/tab-categories/tab-categories.html',
controller: 'CategoriesCtrl'
}
}
});
// otherwise
$urlRouterProvider.otherwise('/tab/categories');
})
.factory('CategoriesService', function($resource) {
var remoteCategoriesURL = 'http://104.167.104.163:7000/api/v1/categories',
categoriesService = $resource(remoteCategoriesURL, {}, {
getAll: {
method: 'GET',
isArray: true
}
});
return categoriesService;
})
.controller('CategoriesCtrl', function($scope, CategoriesService) {
$scope.categories = CategoriesService.getAll();
});
Child
angular.module('rainbowPages.tab.categories.detail', [])
.config(function($stateProvider, $urlRouterProvider) {
// UI Router
$stateProvider.state('tab.category-detail', {
url: '/categories/:listing_category_id',
views: {
'tab-categories': {
templateUrl: 'views/category-detail/category-detail.html',
controller: 'categoryDetailCtrl'
}
},
resolve: {
listings: function($stateParams, CategoryDetailService) {
// bind data to listing
return CategoryDetailService($stateParams.listing_category_id);
}
}
});
})
.factory('CategoryDetailService', function($resource) {
var remoteCategoryURL = 'http://104.167.104.163:7000/api/v1/category/:categoryID',
categoryDetailService = $resource(remoteCategoryURL, {categoryID:'#listing_category_id'}, {
get: {
method: 'GET',
isArray: true
}
}),
getListingsInCategory = function getListingsInCategory(categoryID) {
listingsInCategory = categoryDetailService.get({categoryID:categoryID}, function(promise) {
if(promise.$resolved = true) {
console.log(promise); // how I know the promise has the correct data
return promise;
}
});
};
return getListingsInCategory;
})
.controller('categoryDetailCtrl', function($scope, listings){
console.log('listings are : ' + listings);
$scope.listings = listings;
});

Pass a request header to uriTemplate in AngularJS

I have this Angular code:
.state('UserTables', {
url: '/Tables',
resolve: {
auth: function resolveAuthentication(SessionService) {
return SessionService.isUser();
}
},
views: {
"containerMain": {
templateUrl: 'Views/Tables',
controller: TableController
},
}
})
And would like to pass some request header to the templateUrl call.
Anyone done something like that?
Basically I have a REST service that can generate the view I need depending on 1 header and some property's. Property's are no problem but I have no clue how to make a call to the service and wait for the result.
Tried:
views: {
"containerMain": {
template: function (SessionService, $http, $q) {
console.log('template');
var resp = SessionService.getTable($http, $q, 'Generate/Table?objectId=WfObject');
var r = '';
resp.then(function (result) {
r = result;
console.log('resp:', r);
});
console.log('r:',r);
return r;
}
I created working plunker here
To load template with custom headers, we can call do it like this (check the state 'UserTables' in the plunker):
views: {
"containerMain": {
//templateUrl: 'Views/Tables',
templateProvider: ['$http',
function ($http) {
var tplRequest = {
method: 'GET',
url: 'Generate/Table?objectId=WfObject',
headers: {
'MyHeaderKey': 'MyHeaderValue'
},
}
return $http(tplRequest)
.then(function(response) {
console.log('loaded with custom headers')
var tpl = response.data;
return tpl;
}
);
}],
controller: 'TableController'
},
}
In case, we want (and can) cache the template, we can do it like this (check the state 'UserTablesWithCache'):
views: {
"containerMain": {
//templateUrl: 'Views/Tables',
templateProvider: ['$http', '$templateCache',
function ($http, $templateCache) {
var templateName = 'Generate/Table?objectId=WfObject';
var tpl = $templateCache.get(templateName)
if(tpl){
console.log('returning from cache');
return tpl;
}
var tplRequest = {
method: 'GET',
url: templateName,
headers: {
'MyHeaderKey': 'MyHeaderValue'
},
}
return $http(tplRequest)
.then(function(response) {
console.log('loaded, placing into cache');
var tpl = response.data;
$templateCache.put(templateName, tpl)
return tpl;
}
);
}],
controller: 'TableController'
},
}
And if we would not need headers, and we could cache, it is really very easy, as documented here:
Trying to Dynamically set a templateUrl in controller based on constant
Drafted version could be: (no custom headers but effective loading and caching)
templateProvider: ['$templateRequest', function(CONFIG, $templateRequest) {
var templateName = 'Generate/Table?objectId=WfObject';
return $templateRequest(templateName);
}],
templateUrl property can also take function as value. So you can add dynamic properties to the templateUrl via there.
templateUrl : function(stateParams) {
// before returning the URL, add additional properties and send
// stateParamsargument object refers to $stateParams and you can access any url params from there.
return 'Views/Tables';
}

Resources