How to add optional parameters with AngularJS router? - javascript

I have two Angularjs broadcast options one is for add and other one is for edit for both routing i have different keys to redirect user to the page. For add state $stateParams processId is rendered but for edit state ChallengeKey id is undefined. So question is how to defined optional parameters with router so if its edit it can take challengeKey and route it to appropriate page.
so far tried code..
Ctrl.js
$scope.$on('addProcessChallenge', function (s,processId){
$location.path('/addEditChallenge/' + processId);
}
$scope.$on('editPrcChallenge', function (s,chalengeKey){
$location.path('/addEditChallenge/' + chalengeKey);
}
app.js
.state('app.addChallenge', {
url: '/addChallenge/:processId:/challengeKey',
templateUrl: 'views/processChallenge.html',
controller: 'Ctrl',
data: {
authenticate: true
},

Related

How to pass params from previous state to new state in angular?

I have 10 similar boxes in my dashboard each are showing different values for a specific device. When the user clicks on each, I have to direct them to new page which shows more information about that device.
the layout of the second page for all devices is the same. I just need to update the $scope.
What is a clean angular way to achieve this, preferably not adding query to the url?
I am using $stateProvider and tried to make it work with onEnter() but couldn't yet.
$stateParams should do the trick. To use it you need to specify the parameters when routing. For example:
(function(){
'use strict';
angular
.module('app')
.config(ApplicationConfig);
//set dependencies for ApplicationConfig
ApplicationConfig.$inject = ['$stateProvider'];
function ApplicationConfig($stateProvider){
//Define route states
$stateProvider
.state('main', {
abstract: true,
url: '/main',
templateUrl: 'pages/main/main.html',
controller: 'MainCtrl',
controllerAs: 'main',
cache: true,
params: {
user: null
}
});
}
Then you use $state.go('stateName', { param: param }), for example (following the previous example):
//Inside your original controller
function goToMainPage(param) {
$state.go('main', { user: param });
}
Finally, you access the parameter inside your destination controller by doing a $stateParams.param, or, in the previous example's case, $stateParams.user.

Typing in exact URL to visit specific partial Angular using ui-router

Pretty new to Angular, I am sure I'm missing something obvious here. I am using ui-router.
I want to provide a link to my clients so that they can click the URL link and visit the web app with the appropriate partial. I also want to be able to pass in parameters. Here's how I approached this (kind of hokey). This is in my main controller:
var pNumber = $location.search().number;
if (!(pNumber == null || pNumber == "")){
$state.go('view-ticket');
}
Here is my app.js:
$stateProvider
.state('home', {
url: "/",
templateUrl: 'partials/welcome-screen.html',
controller: 'mainPageController'
})
.state('submit-ticket', {
url: "/submit-ticket",
templateUrl: 'partials/ticket-submit.html',
controller: 'TicketSystemTestCtrl'
})
.state('view-ticket', {
url: "/view",
templateUrl: "partials/ticket-central.html",
controller: 'TicketCentralCtrl'
})
The logic is this: If the URL contains a param 'number' inject ticket-central.html partial.
However, when I run this in the debugger, it seems the first part of the code got executed before it loads the welcome-screen.html partial. How to solve this?
EDIT: I am trying to type this into the URL: http://localhost/techsupport/view and I want it to load the ticket-central.html partial into the main view. However, it won't work.
if i understand correctly all you want to do is to provide a possibility to 'deep-link' to the 'view-ticket' state.
for this search params are not the ideal solution as they are optional, just use path variables:
.state('view-ticket', {
url: '/view/:ticketNumber,
template: 'partials/ticket-central.html',
controler: 'TicketCentralCtrl'
})
also don't use the $location service if you don't really have to, have a look at $stateParams
here is a small plunkr with a welcome and a ticket state
launch the preview in a separate window to see how the url changes - you can also refresh on each page and the correct state will be loaded
https://plnkr.co/edit/r3UcYbfwET0OVwkd77Rv

use service property inside routeProvider

i am trying send service property catId with templateUrl request inside route
i have service formAction
myApp.factory('formAction', [function () {
var catId
return {
catId: catId,
}
}]);
route.js
function ($routeProvider) {
$routeProvider.when('/ad/step1', {
templateUrl: '/ad/place/step-one',
controller : 'Step1Controller'
}).when('/ad/step2', {
templateUrl: function () {
// i dont want pass catId as parametr
return '/ad/place/step-two?catId=' // formAction.catId.
},
controller : 'Step2Controller',
}).otherwise({
templateUrl: '/ad/place/step-one',
controller : 'Step1Controller'
});
}
when i go step2 it must be send catId with request
note: i dont want pass catId in route as parametr like:
templateUrl: function (param) {
return '/ad/place/step-two?catId=' + param.catId.
},
is there way i can do it?
So, as you're using the default (standard) angular routing, then we go to $routeProvider documentation where we can see the following:
templateUrl – {string=|function()=} – path or function that returns a
path to an html template that should be used by ngView.
If templateUrl is a function, it will be called with the following
parameters:
{Array.<Object>} - route parameters extracted from the current
$location.path() by applying the current route
Therefore, the parameters coming to templateUrl are fixed
It means that you can't inject your services into this function.
However, if you're using angular UI router (best angular routing module IMHO), then you can find that each state has an optional parameter 'templateProvider' which is a function that can be injected.
Hope it will help.

Can't navigate to ui-router state with URL

I'm working on a simple angular application using ui-router. I have a couple states for selecting and then editing information about a publisher. The relevant config:
.state('select-publisher', {
url: '/select-publisher',
templateUrl: '/Content/superadmin/src/templates/publishers/publishers.html'
})
.state('publisher', {
abstract: true,
url: 'publisher/{id:int}',
templateUrl: '/Content/superadmin/src/templates/publishers/publisher.html'
})
.state('publisher.details', {
url: '/details',
templateUrl: '/Content/superadmin/src/templates/publishers/details.html'
})
.state('publisher.ad-tags', {
url: '/ad-tags',
templateUrl: '/Content/superadmin/src/templates/publishers/ad-tags.html'
})
.state('publisher.native-ads', {
url: '/native-ads',
templateUrl: '/Content/superadmin/src/templates/publishers/native-ads.html'
})
Inside the select-publisher state I have a big list of available publishers. Each one of them is bound to an ng-click event that triggers the following function in my controller:
$scope.selectPublisher = function(publisher) {
publisherService.setSelectedPublisher(publisher);
$state.go('publisher.details', {id: publisher.Id});
};
This works just fine and takes me to the publisher.details state and renders the proper view. At this point the URL in my browser points to localhost:1337/superadmin#/publisher/39/details where 39 is the ID of the publisher that I selected.
The problem is, if I refresh this page or attempt to navigate directly to it by pasting the URL into the browser from another area of the application, I am ALWAYS taken back to the select-publisher state. I would like to be able to configure my states such that I am able to navigate to the details state (or any other state) based on URL.
Worth noting is that I do have a catch all route defined after all of my states:
$urlRouterProvider.otherwise('/select-publisher');
I'm assuming that for some reason this is being triggered but I can't reason as to why navigation works in my app using either $state.go as I have indicated in my controller as well as using ui-sref directive in my HTML templates but not through navigating directly to the URL.
Maybe it's because of missing slash url: /publisher/{id:int}

AngularJS Restful Routing

I'm trying to structure my app using the Restful/Ruby convension /<resource>/[method]/[id]. How I've done it previously when using a server-side MVC framework like CodeIgniter was to dynamically route based on the URI:
ex.
www.foo.com/bar/baz/1
The app would then use method baz in controller/class bar and return views/bar/baz.php (populated with data from bar->baz)
I would like to do the same in Angular, but I'm not sure if it supports this (and if it does, I'm not sure exactly how to go about it). At the moment I'm using $routeProvider's when method to specify each case. $location.path() looks like it might have what I need, but I don't think I can use it in app.js (within config()).
What I'd like to do is something like this:
.config([
'$routeProvider', function($routeProvider) {
$routeProvider
.when(//<resource> controller exists
resource+'/'+method, {
"templateURL": "views/" + resource + "/" + method + ".html",
"controller": resource
}
).otherwise({ "redirectTo":"/error" });
}
]);
And the router automatically calls the appropriate method.
EDIT Also, why does $routeProvider freak out when I specify when('/foo/bar', {…}) ?
EDIT 2 Per Lee's suggestion, I'm looking into doing something like this:
$routeProvider
.when(
'/:resource/:method/:id', {
"templateUrl": function(routeParams){
var path = 'views/'+routeParams.resource+'/';
return ( typeof routeParams.method === 'undefined' ) ?
path+'index.html' : path+routeParams.method+'.html';
},
"controller": RESOURCE
})
.otherwise({redirectTo: '/error'});
I noticed the following in $routeProvider's doc:
templateUrl – {string=|function()=} – path or function that returns a
path to an html template that should be used by ngView.
If templateUrl is a function, it will be called with the following
parameters:
• {Array.<Object>} - route parameters extracted from the current
$location.path() by applying the current route
Edit: The option to set templateUrl to a function is part of the unstable 1.1.2 build: #1963 (but it doesn't work as of 2013-02-07).
There is a dicussion about adding this functionality on AngularJS's Github: #1193 #1524, but I can't tell if it was actually implemented (in the docs from Dash quoted above, it looks like it has been, and the docs on the site haven't been updated yet).
EDIT 3 To clarify what I want to happen (per lee's request), in simplest terms, I would like to go to www.foo.com/index.html#/people
Angular should use controller people, automatically call its index method, and should serve up
./views/people/index.html
./views/people/map.html
Also, if I go to www.foo.com/index.html#/people/map
Angular should use the people controller again, but this time automcatically call its map method and serve up …map.html (because map was specified in the url)
./views/people/index.html
./views/people/map.html
Then, if I go to
www.foo.com/index.html#/widgets
Angular should serve up
./views/widgets/index.html
./views/widgets/details.html
The code for the router should be very generic—I shouldn't have to specify a .when() for every route.
Thinking about this a little more. You could just have a single controller for those generic CRUD/REST type operations. Then load the templates using the resource and view parameters.
Create
#/foo/create/0
This has it's own form template "/views/foo/create.html" and the 0 os just there for a placeholder.
on submit you would call a method on the controller ng-click="save()" which would post to the server at POST "/rest/foo".
Read
#/foo/view/1
Again the template "/views/foo/view.html" is just a view of the data
You can call a service method to get the data from your server using GET "/rest/foo/1"
Update
-#/foo/edit/1
Could use the same template as create or you could use a different one "/views/foo/edit.html" if you like.
Also pull the data using GET "/rest/foo/1"
Submit the data using PUT "/rest/foo/1"
Delete
#/foo/delete/1
service method would call DELETE "/rest/foo/1"
I don't think you want a hash for this, but you could use one because the controller could actually do a verification or anything you like to confirm the deletion. Maybe have a view called "/views/foo/delete.html" that asks if you want to delete the record. Then you could have ng-click="delete(itemid)" on a button somewhere that deletes the item via ajax.
All this could be done using a single controller/service and dynamically generating the service and view urls.
Anything that's custom you would need a custom controller and custom routes and service methods for. I could probably throw together an example, but not tonight.
Here is a project on github that does something close to what you are asking
EDIT:
I discovered something interesting that had not occurred to me before. If you leave out the controller in the route it will use the controller specified in the template. So as long as all the templates that you use for a given controller have ng-controller="resource" then it will load that controller for the template as expected. Of course with the current implementation of routes there are no optional parameters, so if you have two or three parameters you would need to specify a separate route. Biggest problem is it appears to call the controller method twice. I am guessing this is because there are two views with the same controller. However one view should replace the other so there should not be two calls. This seems like a bug to me. I also found some discussion of a possible new routing system in the works that may meet your needs, but it may be pretty far off: https://github.com/angular-ui/router/issues?page=1&state=open. The sample on github is now using the following method so you can browse that if you like.
var restrouteApp = angular.module('restrouteApp', [])
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/:ctrl/:method', {
templateUrl: function(rp){
if(!rp.method) {rp.method = 'index';}
console.log('route one');
return 'views/'+rp.ctrl+'/'+rp.method+'.html';
}
})
.when('/:ctrl/:method/:id', {
templateUrl: function(rp){
if(!rp.method) {rp.method = 'index';}
console.log('route two');
return 'views/'+rp.ctrl+'/'+rp.method+'.html';
}
})
.otherwise({
redirectTo: '/resource1/'
});
}]);
And the templates:
<div ng-controller="resource1">
<h1> resource1/one.html </h1>
<div>{{r1data.selected}}</div>
</div>
Now in your controller you can do this to call the method dynamically.
restrouteApp.controller('resource1', function($scope,$routeParams,$log,Resource1Service) {
$log.info('new resource1');
$scope.controllername = $routeParams.ctrl;
$scope.r1data= Resource1Service.shared;
$scope.index = function(){
Resource1Service.index().then(function(){
//when the service returns
});
}
$scope.one = function(){
$scope.r1data.selected = $scope.r1data.resources[0];
}
$scope.two= function(){
$scope.r1data.selected = $scope.r1data.resources[1];
}
//call the specified method of this controller
$scope[$routeParams.method]();
});
/EDIT
To conform to existing routing systems like Rails, the ability to define the method in the route is now available.
I created a super simple solution that allows routes to call a method based on the route definition and a directive in the view. I think ui-router is not conventional and is too complicated for a such a "should be" core feature.
The project is called ngMethod and is located at: https://github.com/jzumbrun/ng-method.
An example of its use is: https://github.com/jzumbrun/chrome-apps-angularjs-bootstrap
So if I have a route like so:
$routeProvider.
when('/contacts/new', {
controller: 'ContactsController',
method: 'new',
templateUrl: $configProvider.template('contacts/form.html'),
});
$routeProvider.
when('/contacts/:id/edit', {
controller: 'ContactsController',
method: 'edit',
templateUrl: $configProvider.template('contacts/form.html'),
});
and I have ng-method in the contacts/form template:
<div class="col-lg-12" ng-method>
<form role="form">
...
Then the ng-method will call either $scope.edit() or $scope.new() in the ContactsController.
Than the contacts/form template can be shared, and depending on the route call the correct method
to load the data. This style is now more "Angularjs" and the loading the code is much like angular calling to modules and controllers.
The full directive that makes this happen is less than 20 lines of code:
app.directive('ngMethod', ['$route', function($route) {
return {
// Restrict it to be an attribute in this case
restrict: 'A',
// responsible for registering DOM listeners as well as updating the DOM
link: function(scope, element, attrs) {
// Call method without params. Use $routeParams
if(angular.isFunction(scope[attrs.ngMethod])){
scope[attrs.ngMethod]();
// default to the route method if attrs.ngMethod is empty
} else if(angular.isObject($route.current)
&& angular.isString($route.current['method'])
&& angular.isFunction(scope[$route.current['method']])){
scope[$route.current['method']]();
}
}
};
}]);
This is now possible with ui-router 0.2.8:
$stateProvider
.state('base', {
url: '/:resource/:collection/:id',
controllerProvider: function( $stateParams )
{ // assuming app.controller('FooCtrl',[…])
return $stateParams.collection + 'Ctrl';
},
templateUrl: function( $stateParams )
{
return '/partials/' + $stateParams.collection + '.html';
}
});
But in order to take advantage of $state.includes() on nav menus, this would probably be better:
$stateProvider
.state('base.RESOURCE_NAME1', {
url: '/:collection/:id',
controllerProvider: function( $stateParams )
{ // assuming the convention FooCtrl
return $stateParams.collection + 'Ctrl';
},
templateUrl: function( $stateParams )
{
return '/partials/' + $stateParams.collection + '.html';
}
}).state('base.RESOURCE_NAME2', {
url: '/:collection/:id',
controllerProvider: function( $stateParams )
{ // assuming the convention FooCtrl
return $stateParams.collection + 'Ctrl';
},
templateUrl: function( $stateParams )
{
return '/partials/' + $stateParams.collection + '.html';
}
});
The above could be simplified with a loop to build the states from an array of resources ($stateProvider supports adding states basically whenever):
var resources = [ 'r1', 'r2', '…' ];
for ( var r = resources.length-1; r >=0; r-- )
{
var name = resources[r];
$stateProvider.state('base.'+name, {
…
});
}
Caveat ui-router doesn't not really support optional state parameters (planned for v0.4)

Categories

Resources