Dynamic parameters with angular ui router - angularjs

I am wondering how to include parameters when changing state and sending the request to get the template from the backend.
Here is my app:
angular.module('questionnaireApp', ['ngAnimate', 'ui.router', 'ui.bootstrap'])
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('questionnaire', {
url: '/questionnaire',
templateUrl: 'questionnaire/questionnaire.html',
controller: 'questionnaireCtrl'
})
.state('questionnaire.receiver_name', {
url: '/receiver_name',
templateUrl: 'questionnaire/receiver_name.html'
})
.state('questionnaire.location', {
url: '/location',
templateUrl: 'questionnaire/location.html'
})
.state('poem', {
url: '/poem',
templateUrl: 'questionnaire/poem.html',
controller: 'questionnaireCtrl'
});
$urlRouterProvider.otherwise('/questionnaire/receiver_name');
}])
.controller('questionnaireCtrl', ['$scope', '$http', '$state', function($scope, $http, $state) {
$scope.formData = {};
}]);
I am saving user input in $scope.formData. I need to include it in my request to be able to render questionnaire/poem.html.
Something like:
.state('poem', {
url: '/poem',
templateUrl: 'questionnaire/poem' + $scope.formData + '.html',
controller: 'questionnaireCtrl'
});
How can I do that?
Or is there any variant that can help me send the formData to my backend so that it can render the poem.html page properly?

You can do it by making the templateUrl a function. Take a look at this example, taken from ui-router documentation ( https://github.com/angular-ui/ui-router/wiki ):
$stateProvider.state('contacts', {
templateUrl: function ($stateParams){
return '/partials/contacts.' + $stateParams.filterBy + '.html';
}
})
In the above example, we get $stateParams as an argument (note, it's not an injection, more details on the doc site) and use the parameter "filterBy" as part of the template url. In your case, it's formData.
Note 1: You must pass a string. If formData is an object, you can't use it as part of the templateUrl, and you probably won't be able to even pass it as part of the stateParams. maybe you want a specific field from it? Something like 'formType'?
Note 2: As mb21 mentioned, all of this has nothing to do with the backend. if you want to send the form data, do a REST call. Routing should involve only the client side.

questionnaire/poem.html should be an Angular html template file, nothing dynamically generated by the server.
To save your data, use Angular's http service to do a post request to the server that wants the data.
If you have the data on PageA with ControllerA and need to have it on PageQ with ControllerQ, you should share the data using a factory between the two controllers. No need to involve the server.

I finally managed to achieve my goal: retrieve the poem with one HTTP request only.
To do that:
I store the HTML templates inside the Angular app so that it only needs to retrieve JSON data
form data is sent with regular http post
poem data is sent back in JSON format
the poem is displayed thanks to Angular data binding
Angular code:
angular.module('thePoetApp').config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$stateProvider
// route to show our basic form (/questionnaire)
.state('questionnaire', {
url: '/questionnaire',
templateUrl: 'questionnaire/questionnaire.html',
})
// nested states
// each of these sections will have their own view
// url will be nested (/questionnaire/receiver_name)
.state('questionnaire.receiver_name', {
url: '/receiver_name',
template: '<div class="col-md-3 text-left ng-scope"></div><div class="form-container col-md-6 text-center ng-scope"><div class="form-group"><label for="receiver_name">Receiver Name:</label><input name="receiver_name" ng-model="formData.receiver_name" receiver_name="receiver_name" required="" type="text" class="ng-pristine ng-invalid ng-invalid-required"></div><div class="form-group"><label for="receiver_sex">Receiver Sex:</label><input name="receiver_sex" ng-model="formData.receiver_sex" receiver_sex="receiver_sex" required="" type="radio" value="male" class="ng-pristine ng-invalid ng-invalid-required">Male<input name="receiver_sex" ng-model="formData.receiver_sex" receiver_sex="receiver_sex" required="" type="radio" value="female" class="ng-pristine ng-invalid ng-invalid-required">Female</div></div><div class="col-md-3 text-right ng-scope"><a class="btn btn-primary" ui-sref="questionnaire.location">Next Step</a></div>',
})
// url will be /questionnaire/location
.state('questionnaire.location', {
url: '/location',
template: '<div class="col-md-3 text-left ng-scope"><a class="btn btn-primary" ui-sref="questionnaire.receiver_name">Previous Step</a></div><div class="form-container col-md-6 text-center ng-scope"><div class="form-group"><label for="location">Location:</label><input location="location" name="location" ng-model="formData.location" required="" type="text" class="ng-pristine ng-invalid ng-invalid-required"></div></div><div class="col-md-3 text-right ng-scope"><a class="btn btn-primary" ui-sref="questionnaire.relationship">Next Step</a></div>'
})
// url will be /questionnaire/relationship
.state('questionnaire.relationship', {
url: '/relationship',
template: '<div class="col-md-3 text-left ng-scope"><a class="btn btn-primary" ui-sref="questionnaire.location">Previous Step</a></div><div class="form-container col-md-6 text-center ng-scope"><div class="form-group ng-scope" ng-controller="RelationshipsTypeaheadCtrl"><label for="relationship">Relationship:</label><input autocomplete="off" name="relationship" ng-model="formData.relationship" relationship="relationship" required="" type="text" typeahead-editable="false" typeahead="suggestion for suggestion in relationships($viewValue)" class="ng-pristine ng-invalid ng-invalid-required"><ul class="dropdown-menu ng-isolate-scope"><!-- ngRepeat: match in matches --></ul></div></div><div class="col-md-3 text-right ng-scope"><a class="btn btn-primary" ui-sref="questionnaire.trait">Next Step</a></div>'
})
// url will be /questionnaire/trait
.state('questionnaire.trait', {
url: '/trait',
template: '<div class="col-md-3 text-left ng-scope"><a class="btn btn-primary" ui-sref="questionnaire.relationship">Previous Step</a></div><div class="form-container col-md-6 text-center ng-scope"><div class="form-group ng-scope" ng-controller="TraitsTypeaheadCtrl"><label for="trait">Trait:</label><input autocomplete="off" name="trait" ng-model="formData.trait" trait="trait" required="" type="text" typeahead-editable="false" typeahead="suggestion for suggestion in traits($viewValue)" class="ng-pristine ng-invalid ng-invalid-required"><ul class="dropdown-menu ng-isolate-scope"><!-- ngRepeat: match in matches --></ul></div></div><div class="col-md-3 text-right ng-scope"><a class="btn btn-primary" ui-sref="questionnaire.message">Next Step</a></div>'
})
// url will be /questionnaire/message
.state('questionnaire.message', {
url: '/message',
template: '<div class="col-md-3 text-left ng-scope"><a class="btn btn-primary" ui-sref="questionnaire.trait">Previous Step</a></div><div class="form-container col-md-6 text-center ng-scope"><div class="form-group ng-scope" ng-controller="MessagesTypeaheadCtrl"><label for="message">Message:</label><input autocomplete="off" name="message" ng-model="formData.message" message="message" required="" type="text" typeahead-editable="false" typeahead="suggestion for suggestion in messages($viewValue)" class="ng-pristine ng-invalid ng-invalid-required"><ul class="dropdown-menu ng-isolate-scope"><!-- ngRepeat: match in matches --></ul></div></div>'
})
.state('poem', {
url: '/poem',
template: '<div class="poem"><p>{{ poem.title }}</p><p><div>{{ poem.intro_verse.line_one}}</div><div>{{ poem.intro_verse.line_two}}</div><div>{{ poem.intro_verse.line_three}}</div><div>{{ poem.intro_verse.line_four}}</div><div>{{ poem.intro_verse.line_five}}</div></p><p><div>{{ poem.trait_verse.line_one}}</div><div>{{ poem.trait_verse.line_two}}</div><div>{{ poem.trait_verse.line_three}}</div><div>{{ poem.trait_verse.line_four}}</div><div>{{ poem.trait_verse.line_five}}</div></p><p><div>{{ poem.message_verse.line_one}}</div><div>{{ poem.message_verse.line_two}}</div><div>{{ poem.message_verse.line_three}}</div><div>{{ poem.message_verse.line_four}}</div><div>{{ poem.message_verse.line_five}}</div></p><div class="text-center"><a class="btn btn-warning" ui-sref="questionnaire.receiver_name">Restart</a></div></div>'
});
// catch all route
// send users to the receiver_name page
$urlRouterProvider.otherwise('/questionnaire/receiver_name');
Angular controller:
postParams =
{"questionnaire":
{
"receiver_name": $scope.formData.receiver_name,
"receiver_sex": $scope.formData.receiver_sex,
"location": $scope.formData.location,
"relationship": $scope.formData.relationship,
"trait_category": $scope.formData.trait,
"message_category": $scope.formData.message
}
}
// send post request
$http({
method : 'POST',
url : 'api/questionnaire/poem',
data : $.param(postParams), // pass in data as strings
headers : { 'Content-Type': 'application/x-www-form-urlencoded' } // set the headers so angular passing info as form data (not request payload)
})
.success(function(data) {
if (data.success) {
$scope.poem = data.poem;
$scope.formData = {};
$state.go('poem');
}
});
$scope.poem = data.poem' sets the poem data so that {{ poem.title }} and so on get replaced when going to the poem state.

Related

Angular JS not getting scope value from HTML to controller

I am trying to get the ng-model value from html to controller and getting undefined.
<div class="row">
<div class="form-group col-md-12">
<label class="col-md-2 control-lable" for="uname">User Name</label>
<div class="col-md-7">
<input type="text" ng-model="login.user.name" id="uname" class="username form-control input-sm" placeholder="User name" required ng-minlength="3"/>
</div>
</div>
</div>
<button type="button" ng-click="login.getUser(login.user.name)" class="btn btn-warning btn-sm" ng-disabled="myForm.$pristine">Login</button>
In Controller I am using the below code,
function getUser(name){
console.log('User Name --> '+$scope.login.user.name+" "+$scope.name+" "+self.user.name +$scope.userName+" "+name);
return LoginService.getUserByName(name);
}
In app.js I am using controller,
app.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/',
templateUrl: 'partials/list',
controller:'UserController',
controllerAs:'ctrl',
resolve: {
users: function ($q, UserService) {
console.log('Load all users');
var deferred = $q.defer();
UserService.loadAllUsers().then(deferred.resolve, deferred.resolve);
return deferred.promise;
}
}
}).state('login', {
url: '/login',
templateUrl: 'partials/login',
controller: 'loginController',
controllerAs:'login'
});
$urlRouterProvider.otherwise('/');
}]);
I am getting undefined in console. Please suggest why I am not able to receive the value.
As i see in your code you using controllerAs syntax. so in your controller probably using var self = this;
to access models in controller use this
console.log('User Name --> '+self.user.name);

angularjs and ui-router - controller not working

The full source code.
I don't understand why $scope not working in my LoginGuideCtrl controller. I try click on login button and should show a <p> with new data but the $scope is not updating...
Don´t forget I'm trying to achieve a modular design.
I have the following code:
guides.js
var guides = angular.module('main.guides', ['ui.router']).config(function ($stateProvider) {
$stateProvider.
state('guides.login', {
url: '/login',
templateUrl: 'modules/guides/views/login.html',
controller: 'LoginGuideCtrl'
}).
...
state('guides.mobile', {
url: '/web',
template: '<div>guildes mobile</div>',
controller: 'ListCtrl'
});
});
controller.js
var guides = angular.module('main.guides');
guides.controller('IndexCtrl', function() {
console.log('Index');
})
.controller('LoginGuideCtrl', function($scope) {
console.log('feck');
$scope.checkLogin = function(){
$scope.message = "Welcome "+$scope.name+"!"
};
})
.controller('ListCtrl', function() {
console.log('List');
})
login.html
<div class="form-group col-sm-2">
<label for="usr">Name:</label>
<input type="text" class="form-control" id="usr" ng-model="name">
</div>
<div class="form-group col-sm-2">
<label for="pwd">Password:</label>
<input type="password" class="form-control" id="pwd" ng-model="password">
</div>
<button type="button" class="btn btn-default" ng-click="checkLogin()">Login</button>
<p ng-model="message"></p>
ng-model is used with <input> tags to capture user input, by two way binding with your model value.
Since a <p> tag does not collect user input, it won't work with ng-model. Instead just do a one way binding with the value using the curly brackets:
<p>{{message}}</p>

ui-router $stateProvider.state.controller don't work

The SignupCtrl controller is not binding to signup view. Even when i press the submit button it don't work. But when i place ng-controller=SignupCtrl in the form tag it works. Just wondering why ui-router state parameter controller was not working.
index.html
<html class="no-js" ng-app="mainApp" ng-controller="MainCtrl">
<head> ....
</head>
<body class="home-page">
<div ui-view="header"></div>
<div ui-view="content"></div>
<div ui-view="footer"></div>
...
signup.html
<div class="form-container col-md-5 col-sm-12 col-xs-12">
<form class="signup-form">
<div class="form-group email">
<label class="sr-only" for="signup-email">Your email</label>
<input id="signup-email" type="email" ng-model="user.email" class="form-control login-email" placeholder="Your email">
</div>
<!--//form-group-->
<div class="form-group password">
<label class="sr-only" for="signup-password">Your password</label>
<input id="signup-password" type="password" ng-model="user.password" class="form-control login-password" placeholder="Password">
</div>
<!--//form-group-->
<button type="submit" ng-click="createUser()" class="btn btn-block btn-cta-primary">Sign up</button>
<p class="note">By signing up, you agree to our terms of services and privacy policy.</p>
<p class="lead">Already have an account? <a class="login-link" id="login-link" ui-sref="login">Log in</a>
</p>
</form>
</div><!--//form-container-->
app.js
angular
.module('mainApp', [
'services.config',
'mainApp.signup'
])
.config(['$urlRouterProvider', function($urlRouterProvider){
$urlRouterProvider.otherwise('/');
}])
signup.js
'use strict';
/**
* #ngdoc function
* #name mainApp.signup
* #description
* # SignupCtrl
*/
angular
.module('mainApp.signup', [
'ui.router',
'angular-storage'
])
.config(['$stateProvider', function($stateProvider){
$stateProvider.state('signup',{
url: '/signup',
controller: 'SignupCtrl',
views: {
'header': {
templateUrl: '/pages/templates/nav.html'
},
'content' : {
templateUrl: '/pages/signup/signup.html'
},
'footer' : {
templateUrl: '/pages/templates/footer.html'
}
}
});
}])
.controller( 'SignupCtrl', function SignupController( $scope, $http, store, $state) {
$scope.user = {};
$scope.createUser = function() {
$http({
url: 'http://localhost:3001/users',
method: 'POST',
data: $scope.user
}).then(function(response) {
store.set('jwt', response.data.id_token);
$state.go('home');
}, function(error) {
alert(error.data);
});
}
});
There is a working plunker. Firstly, check this Q & A:
Are there different ways of declaring the controller associated with an Angular UI Router state
Where we can see, that
controller does not belong to state. It belongs to view!
This should be the state definition:
$stateProvider.state('signup',{
url: '/signup',
//controller: 'SignupCtrl',
views: {
'header': {
templateUrl: 'pages/templates/nav.html'
},
'content' : {
templateUrl: 'pages/signup/signup.html',
controller: 'SignupCtrl',
},
'footer' : {
templateUrl: 'pages/templates/footer.html'
}
}
});
Check it here
You need a template to bind a controller.
In the docs ui-router Controllers
Controllers
You can assign a controller to your template. Warning: The controller
will not be instantiated if template is not defined.

Dynamic links and "could not resolve from state" error in AngularJS

I am trying give dynamic link from my models which names are "aracData.planlama".
There is no problem on this. But when I click the link I get "could not resolve from state" error on console.
<div class="form-group">
<div class="radio">
<label>
<input type="radio" ng-model="aracData.planlama" value="sifirmi">
Araç satın alınmış durumda
</label>
</div>
<div class="radio">
<label>
<input type="radio" ng-model="aracData.planlama" value="satin-alinacak">
Satın alınması planlanıyor
</label>
</div>
<div class="form-group row">
<div class="col-xs-6 col-xs-offset-3">
<a ui-sref="form.{{aracData.planlama}}" class="btn btn-block btn-info" ng-class="{disabled:aracData.planlama == undefined}">
Sonraki adım <span class="glyphicon glyphicon-circle-arrow-right"></span>
</a>
</div>
And theese are my routing codes:
app.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('form', {
url: '/form',
templateUrl: 'form.html',
controller: 'formController'
})
.state('form.profile', {
url: '/profile',
templateUrl: 'form-profile.html'
})
.state('form.interests', {
url: '/interests',
templateUrl: 'form-interests.html'
})
.state('form.sifirmi', {
url: '/sifirmi',
templateUrl: 'sifir-mi-ikinci-el-mi.html'
})
.state('form.alindimi', {
url: '/alindimi',
templateUrl: 'alindi-mi.html'
})
.state('form.payment', {
url: '/payment',
templateUrl: 'form-payment.html'
});
$urlRouterProvider.otherwise('/form/profile');
})
How can I fix this?
You can't pass an expression to ui-sref for the state name. Instead, you can do this in the controller.
<a ng-click="go(aracData.planlama)">
Inject the $state service into your controller, then do:
$scope.go = function(sub) {
$state.go('form.'+sub);
};
This will probably also work:
$scope.go = $state.go; // may need to do $state.go.bind($state);
<a ng-click="go('form'+aracData.planlama)">
Click here for GitHub discussion about dynamic state name for ui-sref.

Angular-ui router doesn't update links

I am building a basic rss reader, Ihave a basic form which uploads a series of url into a dropdown menu, when a user chooses a specific one, I call an api to access it.
My form is the following:
<div class="panel-body" ng-controller="NewsCtrl">
<form class="form-inline" role="form">
<div class="input-group">
<div class="input-group-btn">
<button class="btn btn-info" type="button">{{loadButtonText}}</button>
<button class="btn btn-info dropdown-toggle" type="button" data-toggle="dropdown"><span class="caret"></span><span class="sr-only">Toggle-dropdown</span></button>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="rss in RSSList">
{{rss.Title}}
</li>
</ul>
<input type="text" class="form-control" autocomplete="off" placeholder="This is where your feed's url will appear" data-ng-model="url">
</div>
</div>
</form>
My routes/states are defined as follows:
var myApp = angular.module('myApp',['ngSanitize','ui.router', 'myAppControllers', 'myAppFactories']);
myApp.config(['$stateProvider','$urlRouterProvider','$locationProvider',
function ($stateProvider,$urlRouterProvider,$locationProvider) {
$stateProvider
.state('home', {
url:'/home',
templateUrl: 'views/partials/partial-home.html'
})
.state('app', {
url:'/api',
templateUrl: 'views/partials/partial-news.html',
controller: 'NewsCtrl'
});
$urlRouterProvider.otherwise('/home');
$locationProvider
.html5Mode(true)
.hashPrefix('!');
}]);
And my controller is the following:
var myApp = angular.module('myAppControllers',[]);
myApp.controller('NewsCtrl', ['$scope','MyService', function($scope, Feed){
$scope.loadButtonText = 'Choose News Feed ';
$scope.RSSList = [
{Title: "CNN ",
url: 'http://rss.cnn.com/rss/cnn_topstories.rss'},
{Title: "CNBC Top News ",
url: 'http://www.cnbc.com/id/100003114/device/rss/rss.html'},
];
//Loading the news items
$scope.loadFeed = function (url, e) {
$scope.url= url;
Feed.parseFeed(url).then(function (res) {
$scope.loadButtonText=angular.element(e.target).text();
$scope.feeds=res.data.query.results.item;
});
}
}]);
My problem arised when I changed to use ui-router, I know I have to change this line
{{rss.Title}}
by using ui-sref, but just changing to <a ui-sref="loadFeed(rss.url, $event);">{{rss.Title}}</a>
still doesn't upload my urls into my NewsCtrl controller, any clues?
Bonus question: When I insert a console.log just before $scope.loadButtonText and I open the developer console, it seems to upload NewsCtrl 2 times, why is that?
ui-sref="app"
Should work.
sref stands for "state" reference, instead of the familiar "hyperlink" reference. So it will look for a state defined with .state();
Alternatively, you can go with a good old fashioned url,
href="#/api"
This would have the same effect. sref is recommended by project developers.

Resources