This is my code:
angular.module("LearnRouter", ["ui.router"])
.config(['$stateProvider',
function(stateProvider) {
stateProvider.state('state1', {
controller: function($scope, $state) {
$scope.state3 = function() {
$state.go('state3');
}
},
template: "<h1><i>Template1</i><div ui-view><button type='button' ng-click=state3()>State 3</button></div></h1>",
availableOptions:["option1","option2","option3"]
});
stateProvider.state('state2', {
template: "<h1>Template2</h1>"
});
stateProvider.state('state3', {
parent: "state1",
template: "<h1>Template3<div>Names: <span ng-bind=names></span</div></h1>",
controller: function($scope, $state) {
$scope.names=$state.current.availableOptions;
},
onEnter:function($state){
console.log($state)
}
});
}
])
.service('namesRepo', function() {
this.fetch = function() {
return ['name1', 'name2', 'name3'];
}
})
.controller("IndexController", ["$scope", "$state",
function($scope, $state) {
$scope.state1 = function() {
$state.go('state1');
}
$scope.state2 = function() {
$state.go('state2');
}
}
]);
It's simple javascript representing usage of angular-ui-router.
I wanted to test whether the child state inherits custom fields from the parent state.
Obviously i came to the conclusion it doesn't or at least my results are showing this.
As seen from the code I pass $state to the onEnter function. Since the state is entered (or at least this is how i understand the things), current state must be state3. Even the console.log approve that understanding
So when i try to console.log($state.current) the result is something different. It gives me that the parent state is the current one.
onEnter and onExit callbacks are invoked while the transition is still in process. $state.current is not updated until the transition is fully complete. Once the transition is complete, $state.current is set to the "to" state, $stateChangeSuccess is called, and the controllers are invoked.
As such, $state.current contains the "from" state during onEnter and onExit.
Related
I have two states in my application. In each of these states I open a modal dialogs, that has their own controllers: parentCtrl and childCtrl. I wanna return to the parent modal in select(config) function and return config value to the parent state, just into parentCtrl.
$stateProvider.state('parent', {
url: "...",
onEnter: function ($stateParams, $state, $uibModal) {
$uibModal.open({
templateUrl: '...',
controller: function ($scope, $uibModalInstance) {
...
},
controllerAs: 'parentCtrl'
});
}
});
$stateProvider.state('parent.child', {
url: "...",
onEnter: function ($stateParams, $state, $uibModal) {
$uibModal.open({
templateUrl: '...',
controller: function ($scope, $uibModalInstance) {
this.select = function (config) {
debugger;
alert("Hall:"+ config.hallName+", configuration:"+ config.name+", configId: "+ config.id);
$uibModalInstance.close({data: config});
};
},
controllerAs: 'childCtrl'
}).result.finally(function () {
debugger;
$state.go('^');
});
}
});
For bootstrap modals, the modal scope will be a child of the controller's scope and in angular scope are chained.
So if you initialize your parent controller with :
$scope.modal = {};
$scope.modal.newData = function(data){};
You should be able to do in modal controller :
$scope.modal.newData (data);
Note : the intermediary object modal is because of the limit of scope inheritance, you may have not problem with this javascript but you may have with templating so i always use interdiary objects when playing around with scope inheritance.
EDIT : didn't see it was for 2 independant modals. The best would be to use what i post and to close and open again the parent modal from parent scope data in order to refresh it.
Otherwise you can emit/listen for events in angularjs using $scope/$rootScope.$on/$emit.
For this kind of stuff, use $rootScope.$on to listen, and $rootScope.emit to send event.
The result of your child modal can pass parameters.
.result.then(function (data) {
$state.go('^', data);
});
this data is the parameter you entered in the .close() operation. You can catch those params in your state config, through adding the following on your parent state definition
params: {
data: {}
}
I have an Angular app with UI router.
I am in a view where I have a ui-sref going to my route:
<a ng-href="#" ui-sref="app.myRoute">Next</a>
The route I am going to loads a controller that performs a long task on loading:
app.controller('MyRouteController', function($scope) {
this.load = function() {
// ... make a long service call
};
this.load();
});
The behavior that I see is that upon clicking the ui-sref link, the view does not change until after the load() method is finished executing. This is true even if wrapping the load() call in a $timeout.
How can I make the view change right away and make the load() call asynchronous to loading the view?
Thanks!
It should wait for load to finish while it is called synchronously. And it shouldn't (and it won't) while it is called with $timeout.
var app = angular.module('app', ['ui.router']);
app.config(function ($stateProvider) {
$stateProvider
.state('root', {
url:'/',
template: '<div ng-controller="AppController"></div>',
controller: function ($timeout) {
console.log('route controller')
$timeout(function () {
console.log('route controller timeout')
})
}
})
.state('otherwise', {
url: "*path",
template: '<a ui-sref="root">root</a>'
});
});
app.controller('AppController', function () {
console.log('app controller')
});
I have this situation
two files, both in the same app
var app = angular.module('myapp');
file one is the parent and I have:
app.controller("ControllerOne", ['$scope', '$http', '$modal',
function ($scope, $http, $modal) {
$scope.$on('refreshList', function (event, data) {
console.log(data);
});
$scope.openModal = function () {
var modalInstance = $modal.open({
templateUrl: '/SomeFolder/FileWithControllerTwo',
controller: 'ControllerTwo',
size: 'lg',
resolve: {
someParam: function () {
return "param"
}
}
});
}
}]);
file two is the child and I have:
app.controller("ControllerTwo", ['$scope', '$http', 'someParam',
function ($scope, $http, someParam) {
$scope.SaveSomething = function () {
$http.post(url, obj)
.success(function (data) {
$scope.$emit('refreshList', [1,2,3]);
}).error(function () {
});
};
}]);
Assuming that i can open the modal and I can "SaveSomething".
What I need to do to send some data from ControllerTwo to ControllerOne?
I already checked this post Working with $scope.$emit and .$on
but I cant't solve the problem yet.
Obs:
FileOne.js -> I have the ControllerOne (parrent) -> $on
FileTwo.js -> I have the ControllerTwo (child) -> $emit
Yes, I can hit the code inside $http.post.success condition
Assuming you are using angular-ui bootstrap (which has a $model), then the $scope in the model is a childscope of $rootScope.
According to $model documentation you can supply the ControllerOne $scope by using the scope option which will make the modal's $scope a child of whatever you supply. Thus:
var modalInstance = $modal.open({
templateUrl: '/SomeFolder/FileWithControllerTwo',
controller: 'ControllerTwo',
size: 'lg',
scope: $scope,
resolve: {
someParam: function () {
return "param"
}
}
});
Then you could emit to that using $scope.$parent.$emit(...). Strictly speaking, this creates a coupling in that it assumes that the user of the modal listens to the events.
If you don't want to inject your scope, they you could inject $rootScope and emit on that. But that would also send the event to every scope in the application.
This is assuming that you actually want to leave the modal open and send a message back to the parent controller. Otherwise, just use close() or dismiss() methods.
Context:
I'm using Angular and ui-router...
I have a parent controller "ParentCtrl" with a template "ParentTempl".
Within the ParentTempl there is a view for 2 states: add and edit.
I want call a function from the ParentCtrl "abstractUpdate" that changes its behavior based on which state is active.
Current Code:
app.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('add', {
template: "...",
abstractUpdate = function(object){
// do add things
}
})
.state('edit', {
template: "...",
abstractUpdate = function(object){
// do edit things
}
});
}
app.controller('ParentCtrl', function ($scope, $state) {
$scope.click = function(obj){
$state.current.abstractUpdate(obj);
}
}
Question:
The current version is working, but you think it is the best solution? Any suggestions?
Usually you would use a factory or service for something like this. That way you don't clog up your routing with application logic. You could just inject $state into your factory/service so you can handle things based on the state you're in:
angular.module('myApp').factory('MyService', [
'$state',
function ($state) {
return {
myMethod: function (obj) {
if (this.hasOwnProperty($state.current.name)) {
this[$state.current.name](obj);
}
},
add: function (obj) {
// do stuff
return obj;
},
edit: function (obj) {
// do stuff
return obj;
}
}
}
]);
Now you can use your service from any controller you want, just inject it:
angular.module('myApp').controller('myController', [
'$scope',
'MyService',
function ($scope, MyService) {
$scope.obj = {};
$scope.obj = MyService.myMethod(obj);
}
]);
It would be more clear to have two separate functions that each perform their separate tasks. It's a better programming habit, imo.
You can create a controller for each of your child views and still have ParentCtrl as a parent for each. Each child controller can have its own click handler which calls either the 'edit' or 'new' method on the parent (unless it makes more sense to put that code entirely or partially on each child controller).
I'm currently working on an app where a button triggers a method that will emit an event to elsewhere. This works great, however I also want to add a url to trigger this action.
So currently my button looks like this
<a class="addJob" ng-click="addNewJob()" ng-controller="AddJobController"></a>
But what I really want is it to just be
<a class="addJob" href="/new"></a>
Now, I can't figure out how to do the routing for this. It would mean that when I go to /new, the AddJobController should be triggered.
When I go directly to http://www.example.com/new, it should still load the page properly and trigger that action.
I don't want to create a separate page for this route as it is an essential part of the app flow.
(Think of it like when you create a new note in trello.com)
One Option
If you are willing to move to uiRouter, this is a common pattern.
Copied and pasted directly from the uiRouter FAQ
https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-open-a-dialogmodal-at-a-certain-state
$stateProvider.state("items.add", {
url: "/add",
onEnter: ['$stateParams', '$state', '$modal', '$resource', function($stateParams, $state, $modal, $resource) {
$modal.open({
templateUrl: "items/add",
resolve: {
item: function() { new Item(123).get(); }
},
controller: ['$scope', 'item', function($scope, item) {
$scope.dismiss = function() {
$scope.$dismiss();
};
$scope.save = function() {
item.update().then(function() {
$scope.$close(true);
});
};
}]
}).result.then(function(result) {
if (result) {
return $state.transitionTo("items");
}
});
}]
})
Second Option
The second options would be to launch the modal the constructor of your controller. I have included a modalFactory. This is a common pattern. It allows your modals to be reusable, and cleans up your controllers. The uiRouter example above should use the factory pattern as well to abstract the modal setup out of the state config.
This example should work with ngRouter.
app.controller('addJobModalController', ['modalFactory', function(modalFactory) {
modalFactory.addJob();
}]);
app.factory('modalFactory', ['$modal', function($modal) {
return {
addJob: function() {
return $modal.open({
templateUrl: 'views/addjob-modal.html',
controller: 'AddJobController',
size: 'md'
});
}
}
}]);
The addJob() method returns the modal's promise. If you want, you can store this promise in the factory to be returned by another method so that another controller or service can act on the result of the modal.