I'm trying to use the Angular injection come with asp.net Boilerplate,
I'm following the "tutorial" from here http://www.codeproject.com/Articles/791740/Using-AngularJs-ASP-NET-MVC-Web-API-and-EntityFram#ArticleBuildWebApiServices
to get started with my project, for now I want to load the Tasks as Parts
but for some reason I get the following error:
Error: [ng:areq] http://errors.angularjs.org/1.3.2/ng/areq?p0=os.views.part.list&p1=not%20aNaNunction%2C%20got%20undefined
at Error (native)
at http://localhost:6234/Scripts/angular.min.js:6:416
at Nb (http://localhost:6234/Scripts/angular.min.js:19:417)
at ob (http://localhost:6234/Scripts/angular.min.js:20:1)
at $get (http://localhost:6234/Scripts/angular.min.js:75:177)
at http://localhost:6234/Scripts/angular.min.js:57:112
at r (http://localhost:6234/Scripts/angular.min.js:7:408)
at I (http://localhost:6234/Scripts/angular.min.js:56:496)
at g (http://localhost:6234/Scripts/angular.min.js:51:299)
at http://localhost:6234/Scripts/angular.min.js:50:414
the application is a SPA.
the Main app is loaded here:
(function() {
"use strict";
var app = angular.module("app", [
"ngAnimate",
"ngSanitize",
"ui.router",
"ui.bootstrap",
"ui.jq",
"abp"
]);
//Configuration for Angular UI routing.
app.config([
"$stateProvider", "$urlRouterProvider",
function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state("home", {
url: "/",
templateUrl: "/App/Main/views/home/home.cshtml",
menu: "Home" //Matches to name of 'Home' menu in OnderdelenshopNavigationProvider
})
.state("about", {
url: "/about",
templateUrl: "/App/Main/views/about/about.cshtml",
menu: "About" //Matches to name of 'About' menu in OnderdelenshopNavigationProvider
})
.state("listParts", {
url: "/list",
templateUrl: "/App/Main/views/parts/list.cshtml",
menu: "ListParts"
});
}
]);
})();
then when routed to /list the following JS is loaded:
(function() {
var app = angular.module("app");
var controllerId = "os.views.parts.list";
app.controller(controllerId, [
'$scope', 'abp.services.tasksystem.part',
function($scope, partService) {
var vm = this;
vm.localize = abp.localization.getSource("Onderdelenshop");
vm.parts = [];
$scope.selectedTaskState = 0;
$scope.$watch("selectedPartState", function(value) {
vm.refreshParts();
});
vm.refreshParts = function () {
console.log("test");
abp.ui.setBusy( //Set whole page busy until getTasks complete
null,
partService.getParts({ //Call application service method directly from javascript
state: $scope.selectedPartState > 0 ? $scope.selectedPartState : null
}).success(function (data) {
console.log("hey");
vm.parts = data.parts;
})
);
};
vm.changePartState = function(part) {
var newState;
if (part.state === 1) {
newState = 2; //Completed
} else {
newState = 1; //Active
}
partService.updatePart({
partId: part.id,
state: newState
}).success(function() {
part.state = newState;
abp.notify.info(vm.localize("TaskUpdatedMessage"));
});
};
vm.getPartCountText = function() {
return abp.utils.formatString(vm.localize("Xparts"), vm.parts.length);
};
}
]);
})();
this is the http://localhost:6234/api/AbpServiceProxies/GetAll?type=angular
(function (abp, angular) {
if (!angular) {
return;
}
var abpModule = angular.module('abp');
abpModule.factory('abp.services.tasksystem.person', [
'$http', function ($http) {
return new function () {
this.getAllPeople = function (httpParams) {
return $http(angular.extend({
abp: true,
url: abp.appPath + 'api/services/tasksystem/person/GetAllPeople',
method: 'POST',
data: JSON.stringify({})
}, httpParams));
};
};
}
]);
})((abp || (abp = {})), (angular || undefined));
(function (abp, angular) {
if (!angular) {
return;
}
var abpModule = angular.module('abp');
abpModule.factory('abp.services.tasksystem.task', [
'$http', function ($http) {
return new function () {
this.getTasks = function (input, httpParams) {
return $http(angular.extend({
abp: true,
url: abp.appPath + 'api/services/tasksystem/task/GetTasks',
method: 'POST',
data: JSON.stringify(input)
}, httpParams));
};
this.updateTask = function (input, httpParams) {
return $http(angular.extend({
abp: true,
url: abp.appPath + 'api/services/tasksystem/task/UpdateTask',
method: 'POST',
data: JSON.stringify(input)
}, httpParams));
};
this.createTask = function (input, httpParams) {
return $http(angular.extend({
abp: true,
url: abp.appPath + 'api/services/tasksystem/task/CreateTask',
method: 'POST',
data: JSON.stringify(input)
}, httpParams));
};
};
}
]);
})((abp || (abp = {})), (angular || undefined));
Any ideas what I might be forgetting?
Update 1: Got further; the controller definition must be like this:
app.controller(controllerId, [
'$scope', '$location', 'abp.services.tasksystem.part',
function ($scope, $location, partService) { ... }
Now getting internal error though, trying to fix that
I was wondering where are you getting the abp in the controller. You need to inject it
Related
I am a newbee in angular and in a web project i implementing service for the first time and I am having problem:
var app = angular.module("mdmapp", ['ui.router']);
app.config(function ($stateProvider) {
$stateProvider
.state('creafund', {
url: '/creafund',
templateUrl: '/mdm/html/fundtype.html'
})
.state('creareceipt', {
url: '/creareceipt',
templateUrl: '/mdm/html/receipttype.html'
})
.state('fundentry', {
url: '/fundentry',
templateUrl: '/mdm/html/fundentry.html'
})
.state('payentry', {
url: '/payentry',
templateUrl: '/mdm/html/payentry.html'
})
.state('reports', {
url: '/reports',
templateUrl: '/mdm/html/reports.html'
});
});
app.service('DataServer', function ($rootScope, $http) {
//this function is to get the list of fund types.
this.GetFundType = function () {
$http.get('/mdm/server/app.php/GetFundType')
.then(function (response) {
return response;
});
};
});
//controllers for the various pages and sections.
app.controller('mainctrl', function ($scope, DataServer) {
$scope.FundTypeList = DataServer.GetFundType();
});
app.controller('ftctrl', function ($scope, $http, DataServer) {
$scope.SaveFundType = function () {
var data = {desc: $scope.ftdesc};
$http.post('/mdm/server/app.php/FundTypeCreate', data).success(
function (data, status, headers) {
$scope.ftdesc = null;
$scope.FundTypeList = DataServer.GetFundType();
}
);
};
});
in upper mention code i am unable to get data from 'datasever' service, it is fetching json response from a rest api...will render a table.
You need to wait for the promise in the controller like this:
function Factory($http) {
var service = {
GetFundType: GetFundType
};
return service;
function GetFundType() {
return $http.get('/mdm/server/app.php/GetFundType');
}
}
function Controller1($scope,Factory) {
Factory.GetFundType().then(function(response) {
$scope.FundTypeList = response.data;
});
}
I am working with wordpress' rest api and I am extracting a list of posts which allow the user to see a single post. Now I want to include the comments as well but I cannot wrap my head around this. I am using a factory for the calls:
.factory('Articles', function ($http) {
var articles = [];
storageKey = "articles";
function _getCache() {
var cache = localStorage.getItem(storageKey );
if (cache)
articles = angular.fromJson(cache);
}
return {
all: function () {
return $http.get("http://www.examplesite.com/tna_wp/wp-json/posts?filter[category_name]=test").then(function (response) {
articles = response.data;
console.log(response.data);
return articles;
});
},
get: function (articleId) {
if (!articles.length)
_getCache();
for (var i = 0; i < articles.length; i++) {
if (parseInt(articles[i].ID) === parseInt(articleId)) {
return articles[i];
}
}
return null;
}
}
})
My controller:
.controller('ExampleCtrl', function ($scope, $stateParams, _, Articles) {
$scope.articles = [];
Articles.all().then(function (response){
$scope.articles = response;
window.localStorage.setItem("articles", JSON.stringify(response));
},
function (err) {
if(window.localStorage.getItem("articles") !== undefined) {
$scope.articles = JSON.parse(window.localStorage.getItem("articles"));
}
}
);
$scope.doRefresh = function() {
Articles.all().then(function (articles){
var loadedIds = _.pluck($scope.articles, 'id');
var newItems = _.reject(articles, function (item){
return _.contains(loadedIds, item.id);
});
$scope.articles = newItems.concat($scope.articles);
$scope.$broadcast('scroll.refreshComplete');
});
};
})
//THIS IS WHERE I AM TRYING AND FAILING
.controller('ExampleInnerCtrl', function ($http, $scope, $stateParams, $cordovaSocialSharing, $ionicModal, Articles) {
$scope.article = Articles.get($stateParams.articleId);
var url = Articles.get($stateParams.articleId);
$scope.comments = [];
$http.get("http://www.example.com/tna_wp/wp-json/posts/" +url+ "/comments").then(function (response, commentId) {
$scope.comments = response.data;
console.log(response.data);
return $scope.comments;
});
$scope.comment = $stateParams.commentId;
$ionicModal.fromTemplateUrl('gauteng-comments.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal
})
$scope.openModal = function() {
$scope.modal.show()
}
$scope.closeModal = function() {
$scope.modal.hide();
};
$scope.$on('$destroy', function() {
$scope.modal.remove();
});
$scope.sharePost = function(link){
window.plugins.socialsharing.share('I just read this article on The New engage: ', null, null, "http://example.com" + link);
};
})
now in the controller if I include the post id manually I can get the comments for that post, however I cannot seem to store that post ID in a variable to use
--------EDIT
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: "NavCtrl"
})
.state('app.home', {
url: "/home",
views: {
'menuContent': {
templateUrl: "templates/home.html"
}
}
})
.state('app.provinces', {
url: "/provinces",
views: {
'menuContent': {
templateUrl: "templates/provinces.html"
}
}
})
.state('app.example', {
url: "/provinces/example",
views: {
'menuContent': {
templateUrl: "templates/example.html",
controller: "ExampleCtrl"
}
}
})
.state('app.exampleSingle', {
url: "/provinces/example/:articleId",
views: {
'menuContent': {
templateUrl: "templates/exampleSingle.html",
controller: "ExampleInnerCtrl"
}
}
})
;
$urlRouterProvider.otherwise("/app/home");
});
Ok it was my stupidity... I just stored the variable as: var url = $scope.article.ID;
I am trying to create a tag layout filled with categories, but I am not getting my Authentication because I am trying to resolve that service in my Router.
this is my Router code
(function () {
'use strict';
angular
.module('learningApp')
.config(sslRouter);
// Minification safe dependency Injection
sslRouter.$inject = ['$stateProvider'];
function sslRouter ($stateProvider) {
// SSL Route Definition
$stateProvider.state('ssl', {
parent: 'policy',
url: '/ssl',
data: {
roles: ['USER']
},
views: {
'policyConfig': {
templateUrl: 'components/configuration/service/policy/ssl/ssl.tpl.html',
controller: 'SSL'
}
},
resolve: {
'sslServiceData': function(sslService) {
return sslService.promise;
}
}
});
}
}());
This is my Service
(function() {
'use strict';
angular
.module('learningApp')
.factory('sslService', sslResource);
sslResource.$inject = ['Principal', '$resource', 'BASE_URL', 'exDomainService'];
function sslResource (Principal, $resource, BASE_URL, exDomainService) {
debugger;
var res = $resource(BASE_URL + '/api/companies/' + Principal.company() + '/sconfig/ssl/sslConfiguration', {}, {
query: {
method: 'GET',
isArray: false
},
update: {
method: 'PUT'
}
});
var data = {};
var servicePromise = _initService();
servicePromise.$promise.then(function (d) {
data = d;
if (!data.excludedCategories) {
data.excludedCategories = [];
}
if (!data.excludedDomains) {
data.excludedDomains = [];
}
exDomainService.tableData = getExcludedDomains();
});
function _initService () {
return res.query();
}
return {
promise: servicePromise,
rest: res
}
}
}());
This is my controller
(function() {
'use strict';
angular
.module('learningApp')
.controller('SSL', SSLController);
SSLController.$inject = ['$scope', 'sslService', 'preDefinedCategoryService', '$timeout', 'exDialog', 'exDomainService'];
function SSLController ($scope, sslService, preDefinedCategoryService, $timeout, exDialog, exDomainService) {
var vm = $scope;
/**
* #desc Flags for different type checks
* Booleans and Categories
*/
vm.flags = {
// By default true
enableInspectSSLTraffic: sslService.getSSlInspectionFlag(),
allowUntrustedCertificates: sslService.getUntrustedCertificatesFlag(),
allowHostnameMismatch: sslService.getHostnameMismatchFlag(),
selectedCategory: undefined,
initializing: true
};
vm.excludedCategories = sslService.getExcludedCategories();
vm.predefinedCategories = preDefinedCategoryService.rest.query();
vm.predefinedCategories.$promise.then(function() {
vm.categories = _processedCategories(vm.predefinedCategories, vm.excludedCategories);
});
}
}());
So basically problem is, I am getting Principal.Identity as undefined, but if I remove resolution from Router, I got identity but then I lose my data coming from service. I want my service to be loaded completely before its Controller, and I want my principal service to be loaded before service.
for Reference, This is my Principal Class
'use strict';
angular.module('learningApp')
.service('Principal',['$q', 'Account', 'localStorageService', function Principal($q, Account, localStorageService) {
var _identity,
_authenticated = false;
return {
isIdentityResolved: function () {
return angular.isDefined(_identity);
},
isAuthenticated: function () {
return _authenticated;
},
isInRole: function (role) {
if (!_authenticated || !_identity || !_identity.roles) {
return false;
}
return _identity.roles.indexOf(role) !== -1;
},
isInAnyRole: function (roles) {
if (!_authenticated || !_identity.roles) {
return false;
}
for (var i = 0; i < roles.length; i++) {
if (this.isInRole(roles[i])) {
return true;
}
}
return false;
},
company: function () {
debugger;
if (_identity) return _identity.companyId;
},
authenticate: function (identity) {
_identity = identity;
_authenticated = identity !== null;
},
identity: function (force) {
var deferred = $q.defer();
if (force === true) {
_identity = undefined;
}
// check and see if we have retrieved the identity data from the server.
// if we have, reuse it by immediately resolving
if (angular.isDefined(_identity)) {
deferred.resolve(_identity);
return deferred.promise;
}
// rather than retrieving from server, use cookie or whatever method
var cookieFound = UTIL.cookie("token");
if (cookieFound) {
var response = JSON.parse(JSON.parse(cookieFound));
var expiredAt = new Date();
expiredAt.setSeconds(expiredAt.getSeconds() + response.expires_in);
response.expires_at = expiredAt.getTime();
localStorageService.set('token', response);
}
// retrieve the identity data from the server, update the identity object, and then resolve.
Account.get().$promise
.then(function (account) {
account.data.roles = ["ADMIN", 'USER'];
account.data.langKey = "en";
_identity = account.data;
_authenticated = true;
deferred.resolve(_identity);
})
.catch(function() {
_identity = null;
_authenticated = false;
deferred.resolve(_identity);
});
return deferred.promise;
}
};
}]);
I am using the ng-resource module to call rest services in my angular js app. I based my angular js code in another one I used using the cross domain library for a sharepoint hosted app.
The old code was like this:
Now in my new angular js code looks like this:
My ProductResource.js
I had to use a substring because the app its a SPA and the routing adds a # at the end, so with that it also had a bad request, but I believe its better to remove it.
(function () {
"use strict";
angular
.module("common.services")
.factory("productResource",
["$resource",
productResource]);
function productResource($resource) {
var listName = "Products";
var n = SPAppWebUrl.indexOf("#");
var resUrl = SPAppWebUrl.substring(0, n);
// the url to use for the REST call.
var url = resUrl + "/_api/SP.AppContextSite(#target)" +
// this is the location of the item in the parent web. This is the line
// you would need to change to add filters, query the site etc
// "/web/lists/getbytitle('" + listName + "')/items?" +
"/web/lists/getbytitle('" + listName + "')/items?$select=Id,productName,productCode,releaseDate,description,cost,price,category,tags,imageUrl" +
"&#target='" + SPHostUrl + "'";
//return $resource(url);
//return $resource("/api/products/:productId")
return $resource(url, {}, {
query: {
method: 'GET',
headers: { "Accept": "application/json; odata=verbose" }
},
create: { method: 'POST' }
});
}
}());
ProductListCtrl, which uses the query method
(function () {
"use strict";
angular
.module("productManagement")
.controller("ProductListCtrl",
["productResource",
ProductListCtrl]);
function ProductListCtrl(productResource) {
var vm = this;
productResource.query(function (data) {
vm.products = data;
});
vm.showImage = false;
vm.toggleImage = function () {
vm.showImage = !vm.showImage;
}
}
}());
and App.js which might not be relevant
var SPHostUrl;
var SPAppWebUrl;
var ready = false;
$(document).ready(function () {
var params = document.URL.split("?")[1].split("&");
for (var i = 0; i < params.length; i = i + 1) {
var param = params[i].split("=");
switch (param[0]) {
case "SPAppWebUrl":
SPAppWebUrl = decodeURIComponent(param[1]);
break;
case "SPHostUrl":
SPHostUrl = decodeURIComponent(param[1]);
break;
}
}
});
(function () {
"use strict";
var app = angular.module("productManagement",
["common.services",
"ui.router",
"ui.mask",
"ui.bootstrap"]);
app.config(["$stateProvider",
"$urlRouterProvider",
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state("home", {
url: "/",
templateUrl: "../Scripts/app/welcomeView.html"
})
// Products
.state("productList", {
url: "/products",
templateUrl: "../Scripts/app/products/productListView.html",
controller: "ProductListCtrl as vm"
})
.state("productEdit", {
abstract: true,
url: "/products/edit/:productId",
templateUrl: "../Scripts/app/products/productEditView.html",
controller: "ProductEditCtrl as vm",
resolve: {
productResource: "productResource",
product: function (productResource, $stateParams) {
var productId = $stateParams.productId;
return productResource.get({ productId: productId }).$promise;
}
}
})
.state("productEdit.info", {
url: "/info",
templateUrl: "../Scripts/app/products/productEditInfoView.html"
})
.state("productEdit.price", {
url: "/price",
templateUrl: "../Scripts/app/products/productEditPriceView.html"
})
.state("productEdit.tags", {
url: "/tags",
templateUrl: "../Scripts/app/products/productEditTagsView.html"
})
.state("productDetail", {
url: "/products/:productId",
templateUrl: "../Scripts/app/products/productDetailView.html",
controller: "ProductDetailCtrl as vm",
resolve: {
productResource: "productResource",
product: function (productResource, $stateParams) {
var productId = $stateParams.productId;
return productResource.get({ productId: productId }).$promise;
}
}
})
}]
);
}());
The error is bad request, but I cant find nothing wrong with the REST url.
"https://levalencia-00346f286827fc.sharepoint.com/sites/dev/CoreAngularJSCRUDSPList/_api/SP.AppContextSite(#target)/web/lists/getbytitle('Products')/items?$select=Id,productName,productCode,releaseDate,description,cost,price,category,tags,imageUrl&#target='https://levalencia.sharepoint.com/sites/dev'"
See my console error here:
http://screencast.com/t/uxNh781s
that is probably because you are sending problematic values on your request (such a https:// inside your url). try to modify your config file with like the following:
<configuration>
<system.web>
<pages validateRequest="false"/>
I am trying to create a "Todo App" with angularjs ui-router. It has 2 columns:
Column 1: list of Todos
Column 2: Todo details or Todo edit form
In the Edit and Create controller after saving the Todo I would like to reload the list to show the appropriate changes. The problem: after calling $state.go('^') when the Todo is created or updated, the URL in the browser changes back to /api/todo, but the ListCtrl is not executed, i.e. $scope.search is not called, hence the Todo list (with the changed items) is not retrieved, nor are the details of the first Todo displayed in Column 2 (instead, it goes blank).
I have even tried $state.go('^', $stateParams, { reload: true, inherit: false, notify: false });, no luck.
How can I do a state transition so the controller eventually gets executed?
Source:
var TodoApp = angular.module('TodoApp', ['ngResource', 'ui.router'])
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/api/todo');
$stateProvider
.state('todo', {
url: '/api/todo',
controller: 'ListCtrl',
templateUrl: '/_todo_list.html'
})
.state('todo.details', {
url: '/{id:[0-9]*}',
views: {
'detailsColumn': {
controller: 'DetailsCtrl',
templateUrl: '/_todo_details.html'
}
}
})
.state('todo.edit', {
url: '/edit/:id',
views: {
'detailsColumn': {
controller: 'EditCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
.state('todo.new', {
url: '/new',
views: {
'detailsColumn': {
controller: 'CreateCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
;
})
;
TodoApp.factory('Todos', function ($resource) {
return $resource('/api/todo/:id', { id: '#id' }, { update: { method: 'PUT' } });
});
var ListCtrl = function ($scope, $state, Todos) {
$scope.todos = [];
$scope.search = function () {
Todos.query(function (data) {
$scope.todos = $scope.todos.concat(data);
$state.go('todo.details', { id: $scope.todos[0].Id });
});
};
$scope.search();
};
var DetailsCtrl = function ($scope, $stateParams, Todos) {
$scope.todo = Todos.get({ id: $stateParams.id });
};
var EditCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Edit';
var id = $stateParams.id;
$scope.todo = Todos.get({ id: id });
$scope.save = function () {
Todos.update({ id: id }, $scope.todo, function () {
$state.go('^', $stateParams, { reload: true, inherit: false, notify: false });
});
};
};
var CreateCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Create';
$scope.save = function () {
Todos.save($scope.todo, function () {
$state.go('^');
});
};
};
I would give an example (a draft) of HOW TO nest edit into detail. Well, firstly let's amend the templates.
The Detail template, contains full definition of the detail. Plus it now contains the attribute ui-view="editView". This will assure, that the edit, will "replace" the detail from the visibility perspective - while the edit scope will inherit all the detail settings. That's the power of ui-router
<section ui-view="editView">
<!-- ... here the full description of the detail ... -->
</section>
So, secondly let's move the edit state, into the detail
// keep detail definition as it is
.state('todo.details', {
url: '/{id:[0-9]*}',
views: {
'detailsColumn': {
controller: 'DetailsCtrl',
templateUrl: '/_todo_details.html'
}
}
})
// brand new definition of the Edit
.state('todo.details.edit', { // i.e.: url for detail like /todo/details/1/edit
url: '/edit',
views: {
'editView': { // inject into the parent/detail view
controller: 'EditCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
Having this adjusted state and template mapping, we do have a lot. Now we can profit from the ui-router in a full power.
We'll define some methods on a DetailCtrl (remember, to be available on the inherit Edit state)
var DetailsCtrl = function ($scope, $stateParams, Todos) {
$scope.id = $stateParams.id // keep it here
// model will keep the item (todos) and a copy for rollback
$scope.model = {
todos : {},
original : {},
}
// declare the Load() method
$scope.load = function() {
Todos
.get({ id: $stateParams.id })
.then(function(response){
// item loaded, and its backup copy created
$scope.model.todos = response.data;
$scope.model.original = angular.copy($scope.model.todos);
});
};
// also explicitly load, but just once,
// not auto-triggered when returning back from Edit-child
$scope.load()
};
OK, it should be clear now, that we do have a model with the item model.todos and its backup model.original.
The Edit controller could have two actions: Save() and Cancel()
var EditCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Edit';
// ATTENTION, no declaration of these,
// we inherited them from parent view !
//$scope.id .. // we DO have them
//$scope.model ...
// the save, then force reload, and return to detail
$scope.save = function () {
Todos
.update({ id: id })
.then(function(response){
// Success
$scope.load();
$state.go('^');
},
function(reason){
// Error
// TODO
});
};
// a nice and quick how to rollback
$scope.cancel = function () {
$scope.model.todos = Angular.copy($scope.model.original);
$state.go('^');
};
};
That should give some idea, how to navigate between parent/child states and forcing reload.
NOTE in fact, instead of Angular.copy() I am using lo-dash _.cloneDeep() but both should work
Huge thanks for Radim Köhler for pointing out that $scope is inherited. With 2 small changes I managed to solve this. See below code, I commented where I added the extra lines. Now it works like a charm.
var TodoApp = angular.module('TodoApp', ['ngResource', 'ui.router'])
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/api/todo');
$stateProvider
.state('todo', {
url: '/api/todo',
controller: 'ListCtrl',
templateUrl: '/_todo_list.html'
})
.state('todo.details', {
url: '/{id:[0-9]*}',
views: {
'detailsColumn': {
controller: 'DetailsCtrl',
templateUrl: '/_todo_details.html'
}
}
})
.state('todo.edit', {
url: '/edit/:id',
views: {
'detailsColumn': {
controller: 'EditCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
.state('todo.new', {
url: '/new',
views: {
'detailsColumn': {
controller: 'CreateCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
;
})
;
TodoApp.factory('Todos', function ($resource) {
return $resource('/api/todo/:id', { id: '#id' }, { update: { method: 'PUT' } });
});
var ListCtrl = function ($scope, $state, Todos) {
$scope.todos = [];
$scope.search = function () {
Todos.query(function (data) {
$scope.todos = $scope.todos(data); // No concat, just overwrite
if (0 < $scope.todos.length) { // Added this as well to avoid overindexing if no Todo is present
$state.go('todo.details', { id: $scope.todos[0].Id });
}
});
};
$scope.search();
};
var DetailsCtrl = function ($scope, $stateParams, Todos) {
$scope.todo = Todos.get({ id: $stateParams.id });
};
var EditCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Edit';
var id = $stateParams.id;
$scope.todo = Todos.get({ id: id });
$scope.save = function () {
Todos.update({ id: id }, $scope.todo, function () {
$scope.search(); // Added this line
//$state.go('^'); // As $scope.search() changes the state, this is not even needed.
});
};
};
var CreateCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Create';
$scope.save = function () {
Todos.save($scope.todo, function () {
$scope.search(); // Added this line
//$state.go('^'); // As $scope.search() changes the state, this is not even needed.
});
};
};
I might have faced a similar problem the approach i took was to use $location.path(data.path).search(data.search); to redirect the page then in the controller I caught the $locationChangeSuccess event. I other words I use the $location.path(...).search(...) as apposed to $state.go(...) then caught the $locationChangeSuccess event which will be fired when the location changes occurs before the route is matched and the controller invoked.
var TodoApp = angular.module('TodoApp', ['ngResource', 'ui.router'])
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/api/todo');
$stateProvider
.state('todo', {
url: '/api/todo',
controller: 'ListCtrl',
templateUrl: '/_todo_list.html'
})
.state('todo.details', {
url: '/{id:[0-9]*}',
views: {
'detailsColumn': {
controller: 'DetailsCtrl',
templateUrl: '/_todo_details.html'
}
}
})
.state('todo.edit', {
url: '/edit/:id',
views: {
'detailsColumn': {
controller: 'EditCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
.state('todo.new', {
url: '/new',
views: {
'detailsColumn': {
controller: 'CreateCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
;
})
;
TodoApp.factory('Todos', function ($resource) {
return $resource('/api/todo/:id', { id: '#id' }, { update: { method: 'PUT' } });
});
var ListCtrl = function ($scope, $state, Todos, todo.details) {
/*here is where i would make the change*/
$scope.$on('$locationChangeSuccess', function () {
$scope.search();
$route.reload();
});
$scope.todos = [];
$scope.search = function () {
Todos.query(function (data) {
$scope.todos = $scope.todos.concat(data);
});
};
$scope.search();
};
var DetailsCtrl = function ($scope, $stateParams, Todos) {
$scope.todo = Todos.get({ id: $stateParams.id });
};
var EditCtrl = function ($scope, $stateParams, $state, Todos, $location) {
$scope.action = 'Edit';
var id = $stateParams.id;
$scope.todo = Todos.get({ id: id });
$scope.save = function () {
Todos.update({ id: id }, $scope.todo, function () {
//here is where I would make a change
$location.path('todo.details').search($stateParams);
});
};
};
var CreateCtrl = function ($scope, $stateParams, $state, Todos, $location) {
$scope.action = 'Create';
$scope.save = function () {
Todos.save($scope.todo, function () {
//here is where I would make a change
$location.path('todo.details');
});
};
};
the $locationChangeSuccess event occurs before the route is matched and the controller invoked