I want to implement 'edit' feature to any book, but I can't get my book.
How it works now:
I click on the any record (which is <tr>).
I am being redirected to the books_edit state
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.
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.
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.
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
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;
}
Thanks #fikkatra and #Gurpinder for helping with this! The complete solution is following:
Add this to the books_edit state:
params: {data: null}
In the editBook() function send parameters to the next state:
$state.go('books_edit',{bookId: book.$id, data: book});
Add this to the bookCtrl - bookCtrl.currentBook = $state.params.data;
Change ng-model in the view to bookCtrl.currentBook.KEY_NAME
Related
I'm new to Ionic. I write code for list. List is working perfectly but when click on any list-item it's not showing any data.
It showing me this error "Cannot GET /pilliondetails/1" how can i solve this?
app.factory('myService', function() {
var savedData = {}
function set(data) {
savedData = data;
console.log(savedData);
}
function get() {
return savedData;
}
return {
set: set,
get: get
}
})
PillionList Controller:
.controller('PillionListCtrl',function($scope,$ionicHistory,myService){
$scope.myGoBack = function() {
$ionicHistory.goBack();
};
$scope.pillions = [];
var promise=myService.get();
$scope.pillions=myService.get();
})
PillionDetail Controller:
.controller('PillionDetailCtrl',function($scope, $ionicHistory, $stateParams, myService)
{
$scope.myGoBack = function() {
$ionicHistory.goBack();
};
var promise=myService.get($stateParams.requestId);
console.log(promise);
})
PillionList.html :Showing list pf Pillions
<ion-list>
<ion-item data-ng-repeat="pillion in pillions">
<div class="list list-inset">
{{pillion.request_departure_date}}-{{pillion.request_departure_time}}
{{pillion.request_from}} >> {{pillion.request_to}}
{{pillion.user_first_name}} {{pillion.user_last_name}}
<a ui-sref="pilliondetails({pillionId:pillion.request_id})" nav-direction="enter">
<h2>More Details...</h2>
</a>
</div>
</ion-item>
</ion-list>
my app.js
.state('pillionlist', {
url: '/pillionlist',
templateUrl: 'templates/pillionlist.html',
controller: 'PillionListCtrl'
})
.state('pilliondetails', {
url: '/pillionlist/:pillionId',
templateUrl: 'templates/pilliondetails.html',
controller: 'PillionDetailCtrl'
})
Its redirecting to pillionDetail view but not showing data.
Please help.
The first thing i noticed is
ui-sref="pilliondetails({pillion.request_id})"
it should be key-value pair like this
ui-sref="pilliondetails({ your_id : pillion.request_id})"
and in stateProvider, the url of details page should contain parameter. for eg.
url : '/pilliondetails/:your_id'
I am new to AngularJs.
I have an application which shows list of records in a Table.
eg:
EmpNo Name
1 AAAA
2 BBBB
3 CCCC
Now I want to click on EmpNo in this table and I should see a pop up (new Html Template) which should have all the other employee records associated with the selected EmployeeNo.
eg:
if a person clicks on EmpNo: 1 the data in pop should be:
EmpNo: 1
Name: AAAA
Unit: XYZ
Manager's Name: LLLL
An example would be of great help.
Thanks in advance.
Below is some sample code from my application
HTML:
<a href ui-sref="stateTruckDetails({transferSessionId: x.transferSessionId})">{{x.location}}</a>
JavaScript: for application state
app.config(function($stateProvider,TRANSFER_COMPONENT_PATH)
{
$stateProvider
.state("stateTruckDetails",
{ url: "/transfers/truckDetails/:transferSessionId",
controllerAs: "truckDetailsCtrl",
controller: "TruckDetailsController",
params: { tansferSessionId: $stateProvider.transferSessionId }
});
});
JavaScript for my controller which my pop up needs to use.
app.controller('TruckDetailsController', function (transferService, $scope, dataShare, $stateParams)
{
//The below is api call. Ideally this number 13 (ID) should come from the details page.
ctrl.GetTruckDetails = transferService.getTruckDetails(13).then(function (d)
{
$scope.data = d;
angular.forEach($scope.data, function (value, index)
{
console.log(index);
});
});
}; }); }
You should first bind each row to call a function you will have to add in your controller for example openDetails. your html would look something like:
<tr ng-repeat="item in items"><td ng-click="openDetails(item)">Name</td></tr>
in your controller you will have to create this method:
$scope.openDetails = function(item) {
//open here popup
}
There are several ways to open a popup from angularjs.
1 - The best way is to use bootstrap together with angularjs - https://angular-ui.github.io/bootstrap/ seach there for 'Modal' example
2 - But there is an alternative - http://jsfiddle.net/alexsuch/RLQhh/
3 - And the last and desperate is to use jquery inside angularjs.
Thanks Diana for your valuable suggestions. However, I was able to resolve it.
Here's the plunkr that I created.
http://plnkr.co/edit/i1d3P8keTcMwWQ0sIn9h
Here's the key to it. I used states for displaying both the parent and child pages.
app.config(function($stateProvider) {
$stateProvider.state("managerState", {
url: "/ManagerRecord",
controller: "myController",
templateUrl: 'index.html'
})
.state("employeeState", {
url: "empRecords",
parent: "managerState",
params: {
empId: 0
},
onEnter: [
"$modal",
function($modal) {
$modal.open({
controller: "EmpDetailsController",
controllerAs: "empDetails",
templateUrl: 'empDetails.html',
size: 'sm'
}).result.finally(function() {
$stateProvider.go('^');
});
}
]
});
});
I have an array with JSON objects in my $scope.users which is working correctly. I'm now trying to set up a "detail" page for an individual user by filtering the users list to get a single user.
I've been close for an excruciating amount of time, and for some reason I can't find documentation or any examples of how to do what I'm doing. Maybe this isn't the correct way to do this in Angular and/or maybe I'm not searching for the right thing?
I've tried getting the object based on userId from the list in the controller, and I've tried passing a resolve in the state, but I didn't get either method to work. What is the best way to go about getting the single user with id of $stateparams.userId?
Everything else is working correctly (i.e. all the routing, getting the users on #/users).
// routing section of app.js
// Routing
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
templateUrl: 'static/partials/main/home.html'
})
.state('users', {
url: '/users',
templateUrl: 'static/partials/accounts/users.html',
controller: 'UserController'
})
.state('profile', {
url: '/users/{userId}',
templateUrl: 'static/partials/accounts/profile.html',
controller: 'UserController'
});
// controller.js
var rlGlobalControllers = angular.module('rlGlobalApp.controllers', []);
rlGlobalControllers.controller('UserController', function($scope, $stateParams, $filter) {
var userId = $stateParams.userId;
$scope.users = [
{
'id': 1,
'name': 'Shadrack'
},
{
'id': 2,
'name': 'JP'
},
{
'id': 3,
'name': 'Adam'
}
];
// $scope.user = ???
});
# profile.html
<div ng-include src="'/static/partials/shared/header.html'"></div>
<div class="container">
<div class="row">
<p>users data: {{ users }}</p>
<p>user data: {{ user }}</p>
</div>
</div>
User Array.prototype.filter method to find objects satisfying criteria:
$scope.user = $scope.users.filter(function(user) {
return user.id === userId;
})[0];
This is the most natural way to solve it. If however your users array is very large (like millions, for example (hope it's not)) then for-loop-break solution of juunas is preffered as more effective.
You could just do this in the controller:
for(var i = 0; i < $scope.users.length; i++){
if($scope.users[i].id === userId){
$scope.user = $scope.users[i];
break;
}
}
var output = [],
keys = [];
Array_Value.forEach(function (item) {
var key = item["Field name"];
if (keys.indexOf(key) === -1) {
keys.push(key);
output.push(item);
}
});
this.Title = output;
Instead of Array_value and field name, give your data.
Here is a fiddle: http://jsfiddle.net/a0eLhcbm/
I have a simple setup:
<div ng-app="demo" ng-controller="PageController">
{{ page.time }}
<div ng-controller="UsernameController">
{{ user.name }}
</div>
</div>
There is a function that will, say, get the user.name from somewhere else using ajax, and that function belongs to controller PageController.
Question: Is there anyway I can make the {{ user.name }} within the UsernameController to update itself as soon as the controller PageController receives the information?
Here is my javascript setup:
var app = angular.module( 'demo', [] );
function_that_fetches_for_username = function() {
//Some function that fetch for username asynchronously
};
app.controller( 'PageController', function ( $scope ) {
//Initial data
$scope.page = {};
$scope.page.time = Date();
function_that_fetches_for_username();
//How can I make the UsernameController to update its view from this Controller as soon as this controller receives the information?
});
app.controller( 'UsernameController', function( $scope ) {
//Initial data
$scope.user = {};
$scope.user.name = "";
//How can I automatically updated the $scope.user.name in view as soon as the PageController receives the information about the username?
});
There are probably a lot of ways to solve this problem, my share to this is to use either of the two below:
[1] Create a service that you can share to any part of your application (Controllers, Services, Directives, and Filters).
In relation to your problem, you can simply create a User service that can be shared across your controllers. The solution below assumes that the function_that_fetches_for_username() is a service UserResource that has a method get() that simulates fetching data from a server. The User service is an empty object that is shared across all your controllers.
DEMO
JAVASCRIPT
angular.module('demo', [])
.service('UserResource', function($timeout) {
this.get = function() {
return $timeout(function() {
return {
id: 'w3g45w34g5w34g5w34g5w3',
name: 'Ryan'
};
}, 2000);
};
})
.service('User', function() {
return {};
})
.controller('PageController', function($scope, UserResource, User) {
$scope.page = {
time: Date()
};
UserResource.get().then(function(data) {
angular.extend(User, data);
});
})
.controller('UsernameController', function($scope, User) {
$scope.user = User;
});
HTML
<div ng-app="demo" ng-controller="PageController">
{{ page.time }}
<hr>
<div ng-controller="UsernameController">
<div ng-if="user.name">
{{ user.name }}
</div>
<div ng-if="!user.name" style="color: red">
Waiting for Response...
</div>
</div>
</div>
[2] Use the controllerAs syntax for declaring controllers. Use this type of notation for child controllers to access parent controllers using their aliases.
DEMO
JAVASCRIPT
angular.module('demo', [])
.service('UserResource', function($timeout) {
this.get = function() {
return $timeout(function() {
return {
id: 'w3g45w34g5w34g5w34g5w3',
name: 'Ryan'
};
}, 2000);
};
})
.controller('PageController', function(UserResource) {
var ctrl = this;
ctrl.page = {
time: Date()
};
ctrl.user = {};
UserResource.get().then(function(data) {
angular.extend(ctrl.user, data);
});
})
.controller('UsernameController', function() {
this.getUser = function(user) {
console.log(user);
};
});
HTML
<div ng-app="demo" ng-controller="PageController as PC">
{{ PC.page.time }}
<hr>
<div ng-controller="UsernameController as UC">
<div ng-if="PC.user.name">
{{ PC.user.name }}
</div>
<div ng-if="!PC.user.name" style="color: red">
Waiting for Response...
</div>
<button type="button" ng-click="UC.getUser(PC.user)"
ng-disabled="!PC.user.name">
Access user from Page Controller
</button>
</div>
</div>
You can do one of these for sharing the same value through multiple controllers:
Promote the value to a higher level scope all the interested controllers have access to. Controllers will get it through scope inheritance because angular automatically searches the value through the scope hierarchy.
Whoever gets the value broadcasts it through the higher level scope all the controllers have access to. All the controllers listening for this broadcast will get the value.
you can define your user in pageController(that is parent controller to UsernameController) now whenever you change it in pageController it will also be updated in usernameController
second solution is to have ng-view in parent, and in route use controller for UsernameController
index file
<div ng-app="demo" ng-controller="PageController">
{{ page.time }}
<ng-view></ng-view>
</div>
user.html
<div ng-controller="UsernameController">
{{ user.name }}
</div>
route codee
.when("/user",{
controller:"usernameController",
templateUrl : 'user.html'
})
Third solution is to make a service
.factory("userFactory",function(){
var user = {};
return{
setUser : function(usern){
user = usern;
},
getUser : function(usern){
return user;
},
}
})
now you can get user from service and set to service .
I got this directive:
.directive('studentTable', [function() {
return {
restrict: 'A',
replace: true,
scope: {
students: "=",
collapsedTableRows: "="
},
templateUrl: 'partials/studentTable.html',
link: function(scope, elem, attrs) {
...
}
}
}
Template:
<table class="table">
<thead>
<tr>
<th><b>Name</b></th>
<th><b>Surname</b></th>
<th><b>Group</b></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="student in students track by $index">
<td>{{ student.name }}</td>
<td>{{ student.surname }}</td>
<td>{{ student.group }}</td>
</tr>
</tbody>
</table>
Use directive in my html like this:
<div student-table students="students"
collapsedTableRows="collapsedTableRows"></div>
And the parent controller:
.controller('SchoolController', ['$scope', 'User', function($scope, User){
$scope.students = [];
$scope.collapsedTableRows = [];
$scope.search = function(value) {
if(value) {
var orgId = $state.params.id;
var search = User.searchByOrg(orgId, value);
search.success(function (data) {
$scope.students = data;
$scope.collapsedTableRows = [];
_(data).forEach(function () {
$scope.collapsedTableRows.push(true);
});
});
}
}
}])
Now at the beginnig, the table is empty, because no users in students array. After I click search, and get list of students object, I put them to scope variable, but the directive does not update, neither it find change in model (scope.$watch('students',...). What am I missing?
P.S. If I simulate the data using $httpBackend, directive works as it should.
Please make sure that data object returning array of student because somtimes you have to use data.data that simple demo should helps you:
http://plnkr.co/edit/UMHfzD4oSCv27PnD6Y6v?p=preview
$http.get('studen.json').then(function(students) {
$scope.students = students.data; //<-students.data here
},
function(msg) {
console.log(msg)
})
You should try changing the controller this way
...
$scope.$apply(function() {
$scope.students = data;
})
...
This will start a digest loop, if it's not already in progress.
Another form that will do almost the same thing is this:
...
$scope.students = data;
$scope.$digest()
...
PS:
The first method is just a wrapper that execute a $rootScope.$digest() after evaluating the function, considering that a $digest evaluates the current scope and all it's children calling it on the $rootScope is pretty heavy.
So the second method should be preferred if it works.