3

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?

fkoessler
  • 6,932
  • 11
  • 60
  • 92

3 Answers3

2

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.

haimlit
  • 2,572
  • 2
  • 22
  • 26
0

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.

Community
  • 1
  • 1
mb21
  • 34,845
  • 8
  • 116
  • 142
  • Thanks for the clarification. The poem page can only be dynamically generated from the server, it is built based on the answers to the questionnaire, this build involves database processing and can't be done client side. I manage to post my form successfully with the http service and I can even include html in the http response, but I don't know how to inject it in the poem state. – fkoessler Jul 08 '14 at 11:40
  • Your server should provide a REST API that sends JSON to the Angular client via the $http service. If you generate the HTML on the server, why use Angular at all? – mb21 Jul 08 '14 at 12:15
  • Doesn't angular ui-router load the html from the server already? Any chance we could have a quick chat together? – fkoessler Jul 08 '14 at 12:30
  • no, routing in angular has nothing to do with the server: see https://docs.angularjs.org/tutorial/step_07 – mb21 Jul 08 '14 at 16:02
  • With the code above, angular ui-router retrieves the html for each state from the server at the specified url, for example `questionnaire/location.html`. It then injects this html in the view container. – fkoessler Jul 09 '14 at 07:41
0

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.

fkoessler
  • 6,932
  • 11
  • 60
  • 92