1

I want to implement 'edit' feature to any book, but I can't get my book.

How it works now:

  1. I click on the any record (which is <tr>).
  2. I am being redirected to the books_edit state
  3. This 'edit' page must have all the data in form of current book (but it doesn't).

So, the question is: How can I pass book from the books state to books_edit state and submit it correctly?

HTML piece:

<tr ng-click="bookCtrl.editBook(book)" ng-repeat="book in bookCtrl.books">
      <td>{{ book.name }}</td>
      <td>{{ book.author }}</td>
      <td>{{ book.price }}</td>
      <td>{{ book.pubdate | date }}</td>
      <td>{{ book.coverUrl }}</td>
      <td>{{ book.pagesCount}}</td>
    </tr>

States:

  .state('books_new', {
    url: '/books/new',
    templateUrl: 'books/book_new.html',
    controller: 'BookCtrl as bookCtrl'
  })
  .state('books_edit', {
    url: '/books/edit',
    templateUrl: 'books/book_edit.html',
    controller: 'BookCtrl as bookCtrl'
  })
  .state('books', {
    url: '/books',
    templateUrl: 'books/books.html',
    controller: 'BookCtrl as bookCtrl'
  })

Controller's methods:

  editBook: function(book) {
    if (book) {
      console.log(book);  // logs correct book
      $state.go('books_edit'); // tried to send `book` as a parameter, didn't work
    }
  },

  submitBook: function(book) {
    if (book) {
      console.log(book);
      return books.$save(book).then(function(data) {
        $state.go('books');
      });
    }
  }

Edit snippet:

<form class="container col-lg-3" ng-submit="bookCtrl.submitBook(book)">
    <div class="input-group">
      <label class="col-sm-2 control-label">Назва:</label>
      <input type="text" ng-model="book.name" class="form-control">

I've tried to send book as a parameter in state, but no result.

Fleeck
  • 1,036
  • 2
  • 8
  • 21

5 Answers5

3

The best way to handle this, is to be 'stateless'. This way a user can bookmark the edit page, and reload the page without requiring any state to be present in the app.

Pass the id of the book you want to edit as a url parameter to the edit state, like so:

state config:

  .state('books_edit', {
    url: '/books/edit/:bookId',
    templateUrl: 'books/book_edit.html',
    controller: 'BookCtrl as bookCtrl'
  })

controller:

$state.go('books_edit', {bookId: book.id});

In the edit controller, fetch the book using the id from the url, using the $stateParams service:

angular.module('myapp').controller('BookCtrl', function($scope, $stateParams){
    //fetch the book id from the url params
    var bookId = $stateParams.bookId;
    //now get the book with the given id
});

I would advise to use a separate controller for the edit functionality, i.e. do not use 'BookCtrl' for every view.

fikkatra
  • 5,605
  • 4
  • 40
  • 66
  • That looks like the solution, I'll try it right now. – Fleeck Apr 07 '16 at 11:09
  • Hm, now I have proper `$id` in the url, sending both `{book: book, bookId: book.$id}` as parameters, but still I can't retrieve them – Fleeck Apr 07 '16 at 11:19
  • you cannot pass 'book', because that is a complex object. Only simple variables like strings and numbers can be passed. Try just using {bookId: book.$id} – fikkatra Apr 07 '16 at 11:22
  • aw, that's the problem. Yeah, `$stateParams` has only `book.$id` key. But how can I get other fields? I mean, I need `$id` only for the url. But to edit the form, I need other keys. – Fleeck Apr 07 '16 at 11:22
  • Once you get the id, fetch the book with the id (probably from a service, or from the backend). The point is to NOT pass the entire book object to the edit view, because this way you won't be able to directly navigate to the edit view (because upon a page load, there is no 'book' object). By passing the id in the url, you make sure your url is 'bookmarkable' and directly navigatable. This is what I mean by being stateless: not requiring any state to be present when you navigate to a page. – fikkatra Apr 07 '16 at 12:26
1

Define state parameters as following

$stateProvider.state('books_edit', {url: '/books/:bookId',params: {obj: null},templateUrl: 'books/books_edit.html',controller: 'booksCtrl'})

when calling pass parameter like this:

$state.go('books_edit',{obj: myobj});

In controller you can receive parameter using

$state.params.obj

Hope it helps.

Fleeck
  • 1,036
  • 2
  • 8
  • 21
Gurpinder
  • 634
  • 1
  • 5
  • 18
  • Well, unfortunately it doesn't see the `obj`.For instance, I'm sending `{bookId: book.$id, data: book}` as parameters. `books` state has `params: {data: null}`. But `$stateParams` shows, that only `bookId` is available. Any ideas? :( – Fleeck Apr 07 '16 at 11:55
  • Okay, finally I got it. I had to add a `params: {data:null}` to the `books_edit` state, not the initial. – Fleeck Apr 07 '16 at 11:58
  • Yes, You had to pass parameter while defining route.In your case put your hold book object and you can pass it with book key and now there is no need to pass book id separately because it is already in passed book object. Hope it helps – Gurpinder Apr 07 '16 at 12:05
  • Argh, it still doesn't work! Though `books_edit` state sees the `$stateParams.data`, `ng-model=$stateParams.data.name` doesn't work! – Fleeck Apr 07 '16 at 12:10
  • I am not getting you. Will you elaborate? – Gurpinder Apr 07 '16 at 12:12
  • When I'm trying to `console.log($stateParams.data)` within the `submitBook()` function, I can see current book => `data` has been passed successfully. But when I'm trying to use it in the view `ng-model` - it shows nothing. – Fleeck Apr 07 '16 at 12:16
  • Okay, I figured it out :) I'll edit the question. Thanks for your help! – Fleeck Apr 07 '16 at 12:20
  • Have you got the solution? – Gurpinder Apr 07 '16 at 12:22
  • if possible please correct my answer as well. Because the approach is same using params: {obj: null}. Thanks for your co-operation – Gurpinder Apr 07 '16 at 12:57
0

You can use a service to reach this. Create a service where you can set/get the value and inject in both controllers. The service looks like this:

app.service('bookService', function() {
  var books = [];

  var addBook = function(obj) {
      books.push(newObj);
  };

  var getBook = function(){
      return books;
  };

  return {
    addBook: addBook,
    getBook: getBook
  };

});

And, in controller:

editBook: function(book) {
    if (book) {
      // ensure to inject productService in controller
      bookService.addBook(book)
      console.log(book);  // logs correct book
      $state.go('books_edit'); // tried to send `book` as a parameter, didn't work
    }
  },

In book_edit controller:

.....
// ensure to inject productService in controller
$scope.book = bookService.getBook(book)
....

You can also use $broadcast, read more: On and broadcast in angular

Hope it helps

Community
  • 1
  • 1
Ricardo Pontual
  • 3,749
  • 3
  • 28
  • 43
  • This solution is not stateless and always requires you to visit the 'overview' page before visiting the 'edit' page. It won't work if you directly navigate to the 'edit' url – fikkatra Apr 07 '16 at 11:02
  • I am using service right now. The problem is, that I can't pass my book between the states. Thanks for the answer, but I guess It won't solve it. – Fleeck Apr 07 '16 at 11:03
0

Try passing it in state.go as something like this "books/" and then use state params to retrieve it.

state('books_edit', {
    url: '/books/edit:bookID',
    templateUrl: 'books/book_edit.html',
    controller: 'BookCtrl as bookCtrl'
  })

  submitBook: function(bookID) {
    if (bookID) {
      console.log(bookID);
      return books.$save(bookID).then(function(data) {
        $state.go('books/'+<bookID>);
      });
    }
  }

in the Controller

editBook: function($scope, $stateParams) {
     $scope.bookID = $stateParams.bookID;
  }
Mukund Gandlur
  • 861
  • 1
  • 12
  • 35
0

Thanks @fikkatra and @Gurpinder for helping with this! The complete solution is following:

  1. Add this to the books_edit state: params: {data: null}

  2. In the editBook() function send parameters to the next state:

    $state.go('books_edit',{bookId: book.$id, data: book});

  3. Add this to the bookCtrl - bookCtrl.currentBook = $state.params.data;

  4. Change ng-model in the view to bookCtrl.currentBook.KEY_NAME

Community
  • 1
  • 1
Fleeck
  • 1,036
  • 2
  • 8
  • 21