So, I have a select input within a angular repeated item. I want to use a button to assign rooms to a person. As well as maintain if a person has rooms selected, it will show that it is already assign within the selection input.
Here my Firebase Data:
{
"-JLtuRmYiTDLTnZPj" : {
"last_name" : "NameOne",
"person_id" : 1,
"is_in" : false
},
"-JLtuRBlyWhHV28zd" : {
"last_name" : "NameTwo",
"person_id" : 2,
"is_in" : false
},
"-JLtuQxlMn25APuKT" : {
"last_name" : "NameThree",
"person_id" : 3,
"is_in" : false
}
}
Here is my code:
<table>
<tbody>
<tr ng-repeat="(name, person) in people | orderBy: 'doctor_id'" ng-model="person">
<td>{{person.person_id}}</td>
<td>{{person.last_name}}</td>
<td><strong>{{person.is_in}}</strong></td>
<td>Assign Rooms: <select multiple class="form-control" ng-model="person.room_id" ng-options="r.room_id as r.name for (key, r) in rooms"></select></td>
<td><button type="button" href="" class="btn btn-primary" ng-click="assignRooms(name)">Assign Rooms</button></td>
</tr>
</tbody>
</table>
And Finally my JS:
angular.module('app', ['ngRoute', 'firebase']);
$scope.people = $firebase(new Firebase("https://<FB-NAME>.firebaseio.com/dev/0/people"));
$scope.rooms = $firebase(new Firebase("https://<FB-NAME>.firebaseio.com/dev/0/rooms"));
//Assigns Rooms to each doctor.
$scope.assignRooms = function(name) {
$scope.people.$child(name).$update({
"room_id": $scope.person.room_id,
"is_in": true
});
}
The app is being called correctly. I can see the scopes when I use Angular JS Batarang. When I click the Assign Rooms button I get the error:
Cannot read property 'room_id' of undefined
from this: $scope.person.room_id.
I am sure it's something obvious I am missing. But any help is greatly appreciated.
Thank You!
You need to use person. You can not access the ng-repeat scope within the function.
<td>
<button type="button"
href=""
class="btn btn-primary"
ng-click="assignRooms(name,person)">Assign Rooms</button>
</td>
$scope.assignRooms = function(name, person) {$scope.assignRooms = function(name) {
$scope.people.$child(name).$update({
"room_id": person.room_id,
"is_in": true
});
}
Related
Hi I'm using angularJs in my client side. I have a view where a user can add and remove item like this:
app.controller('demoController', function($scope) {
// initial items
$scope.items = [
'item one',
'item two',
'item three'
];
// add an item
$scope.add = function() {
$scope.items.push($scope.input);
};
// remove an item
$scope.remove = function(index) {
$scope.items.splice(index, 1);
};`enter code here`
});
When the user finish, I want he clicks on a button. And after I will send all the items added and removed to a server to update database. I can't do this on each click because I need all the information to fill an email in my server part. I know how to remove and add item but I don't how to found removed items and add items and send them to the server. Please any one know how I can do this?
Thanks a lot.
You can do it with only using 1 array. You just have to create a new property and set it to true - if removed -, or false otherwise.
Then in your back-end you can get all the removed items accessing this property.
See the example below:
(function() {
'use strict';
angular
.module('app', [])
.controller('demoController', demoController);
demoController.$inject = ['$scope'];
function demoController($scope) {
// initial items
$scope.items = [
{
"name":"item one",
"removed":false
},
{
"name":"item two",
"removed":false
},
{
"name":"item three",
"removed":false
}
];
// add an item
$scope.add = function() {
$scope.items.push({
"name": $scope.input,
"removed": false
});
};
// remove an item
$scope.remove = function(index) {
$scope.items[index].removed = !$scope.items[index].removed;
};
}
})();
<!DOCTYPE HTML>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" />
</head>
<body ng-controller="demoController">
<table class="table table-hover">
<thead>
<tr>
<td>Name</td>
<td>Removed?</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items track by $index">
<td ng-bind="item.name"></td>
<td ng-bind="item.removed"></td>
<td>
<button type="button" class="btn btn-danger btn-sm" ng-click="remove($index)">Remove item</button>
</td>
</tr>
</tbody>
</table>
<hr>
<input type="text" class="form-control" ng-model="input" placeholder="Type to add">
<button type="button" class="form-control btn btn-primary btn-sm" ng-click="add()">Add item</button>
</body>
</html>
Note: If you don't want to show the removed items, you can simply check in your tr:
<tr ng-repeat="item in items track by $index" ng-if="!item.removed">
Now if you want to send both added and removed you have to actually store the removed ones somewhere either in the object itself with a flag like #developer033 suggested or either in an other object.
For me it is better to store all added and removed elements in one object. Now you do not need to click a button and send the change on every add or remove. When you are done with adding and removing you can just simply send the whole object with AJAX request to the server where you can do your logic:
function demoController($scope, $http, $q) {
$scope.submitItems = function(){
sendItems($scope.items).then(function () {
alert("Successfully deleted PT");
}, function (error) {
alert(error);
});
};
// ....
var sendItems = function (items) {
var request = $http({
url: _SERVER + 'edit/sendItems', // for example
method: 'POST',
data : items
params: {
}
});
return request.then(function (data) {
return $q.when(data);
}, function (error) {
return $q.reject(error);
});
}
// ....
}
It is a good practise to have a service from where you call the server and where this method sendItems should be. But we try to keep at as simple as possible.
Now in your rest controller in Spring you have to specify #RequestBody param:
#RequestMapping(value = "/sendItems", method = RequestMethod.POST)
public String editProductParameters(#RequestBody ArrayList<Item> items) {
//your logic goes here
return "Success"
}
Where the Item.class should consist the fields: String name and boolean remove also should have a deffault constructor(deffault constructur is specified if there are none implementations of constructurs in the class or if there is a constructor with no arguments) also create getters and setter about the two fields. Thouse are the requirements that are needed to pass the whole array of objects($scope.items) from the client to the server with default mapping.
Good luck
I need to get the current object out of an ng-repeat on ng-click, I can't use $index because I'm using orderBy and therefore it gives me the wrong index relative to the scope object. Idealy I want to be able to click on the object (thumbnail) and have $scope.activePerson gain all that objects values.
My data is structured as follows:
[
{
'name': 'john'
},
{
'name': 'toby'
},
{
'name': 'sarah'
}
]
This is very much simplified, my real objects have 30+ KV pairs and subobjects. There are 10 objects that I'm repeating from (in batches of 4).
My current HTML is:
.item.fourth(ng-repeat="person in people | orderBy:'-name' " ng-show="$index <= 3" nid="{{person.guid}}"
Thanks
It's just person in ng-repeat="person in people";
I'm not sure what kind of markdown you're using, you definitely don't have html there, but you want something like:
<div
ng-repeat="person in people | orderBy:'-name' "
ng-show="$index <= 3"
nid="{{person.guid}}"
ng-click="activePerson = person">
</div>
Note that ng-repeat creates a child scope, so you'll want to have activePerson already set in the parent scope.
You can just use orderBy and copy the current object from ng-repeat, see this plunkr. Relevant code:
Controller
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.stuff = [
{
'name': 'john'
},
{
'name': 'toby'
},
{
'name': 'sarah'
}
];
$scope.setActivePerson = function (obj) {
$scope.activePerson = obj;
};
});
View
<body ng-controller="MainCtrl">
<div ng-repeat="thing in stuff | orderBy: 'name'">
<input type="radio" ng-click="setActivePerson(thing)" />
{{ thing.name }}
</div>
<br />
<div ng-model="activePerson">Active: {{ activePerson.name }}</div>
</body>
My select is populating with the contents of the model, but when I select an option, the model does not update.
I'm using ng-options, not ng-repeat and my ng-model is an object on the parent scope, not a primitive, so I think I've avoided the "child-scope" issues I've seen on similar posts. I've recreated the problem on jsfiddle:
http://jsfiddle.net/bobweil/wfdjrej5/
When the user clicks on a row in the table, a small form shows up below that row, permitting a new status value to be selected for that row for posting to the backend service.
Here's my javascript:
angular.module('myApp', [])
.controller('TaskCtrl', function HomeController($scope, $filter) {
$scope.statusMasters = [{
"Id": 1,
"DisplayOrder": 100,
"Text": "Review"
}, {
"Id": 2,
"DisplayOrder": 200,
"Text": "New"
}, {
"Id": 3,
"DisplayOrder": 300,
"Text": "Working"
}, {
"Id": 4,
"DisplayOrder": 400,
"Text": "Complete"
}]
$scope.tasks = [{
"taskId": 1000,
"Descr": "My first task",
"statusId": 1
}, {
"taskId": 2000,
"Descr": "My second task",
"statusId": 1
}, {
"taskId": 3000,
"Descr": "My third task",
"statusId": 1
}];
$scope.selectedTask = null;
$scope.newTaskStatus = {};
$scope.opGroup = "A";
$scope.selectTask = function (thisTask) {
$scope.selectedTask = thisTask;
$scope.newTaskStatus = {};
$scope.newTaskStatus.taskId = thisTask.taskId;
$scope.newTaskStatus.statusId = thisTask.statusId;
};
$scope.isSelected = function (thisTask) {
if (thisTask.hasOwnProperty('taskId')) {
return $scope.selectedTask.taskId === thisTask.taskId;
} else return false;
};
});
And here's my html:
<div ng-controller="TaskCtrl">
<table class="table table-bordered">
<thead>
<tr>
<th>Task #</th>
<th>Description</th>
<th>StatusId</th>
<th>Status Text</th>
</tr>
</thead>
<tbody ng-repeat="item in tasks" ng-click="selectTask(item)" ng-switch on="isSelected(item)">
<tr>
<td>{{item.taskId }}</td>
<td>{{item.Descr}}</td>
<td>{{item.statusId}}</td>
<td>{{statusMasters[item.statusId - 1].Text}}</td>
</tr>
<tr ng-switch-when="true">
<td colspan="10">
<div>Debug: contents of new task status object: <pre>{{newTaskStatus | json}}</pre>
</div>
<label>Select a new status for task {{newTaskStatus.taskId}}:</label>
<select ng-model="newTaskStatus.taskId" ng-show="(opGroup == 'A')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
<select ng-model="newTaskStatus.taskId" ng-show="(opGroup == 'B')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
</td>
</tr>
</tbody>
</table>
Part of the issue is you are trying to set a click event on the tbody, you need to set the ng-click on the row (tr).
Secondly, unless it is needed for another reason, I wouldn't duplicate the values from the "selectedTask" into a "newTaskStatus" when you are planning on changing the status and sending back that value, it can all be done with one object on the scope.
Third, you could clean up your .js a little by changing the 'ng-switch on' to do the check if it is selected. It replaces an entire function with a comparison.
I would do something like this.
<tbody ng-repeat="item in tasks" ng-switch on="selectedTask.taskId == item.taskId">
<tr ng-click="selectTask(item)">
<td>{{item.taskId }}</td>
<td>{{item.Descr}}</td>
<td>{{item.statusId}}</td>
<td>{{statusMasters[item.statusId - 1].Text}}</td>
</tr>
<tr ng-switch-when="true">
<td colspan="10">
<div>Debug: contents of new task status object: <pre>{{selectedTask | json}}</pre>
</div>
<label>Select a new status for task {{selectedTask.taskId}}:</label>
<select ng-model="selectedTask.statusId" ng-show="(opGroup == 'A')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
<select ng-model="selectedTask.statusId" ng-show="(opGroup == 'B')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
</td>
</tr>
</tbody>
With the .js I would remove the unnecessary items:
$scope.selectedTask = null;
$scope.opGroup = "A";
$scope.selectTask = function (thisTask) {
$scope.selectedTask = thisTask;
};
$scope.isSelected = function (thisTask) {
if (thisTask.hasOwnProperty('taskId')) {
return $scope.selectedTask.taskId === thisTask.taskId;
} else return false;
};
I forked your jsfiddle here to demonstrate what I mean. Good Luck!
It probably goes without saying that I'm quite new to angular as I'm trying to accomplish a relatively simple task, and I've come here for some help
I'm recreating our company's password vault using angular.
Here is what I am trying to accomplish.
The page loads with a list of accounts. Most the information is visible except for the password. I have a button that when clicked, hides the button, queries the database, logs who queried password, and displays the password to the user. The passwords are clear text because they aren't passwords for client accounts or anything sensitive, it exists for our employees to reference how/where to login to various websites we use for day to day business.
My HTML looks as follows:
<tr ng-repeat="account in resp.PasswordVaultAccounts">
<td>{{account.Name}}</td>
<td>{{account.Username}}</td>
<td><button ng-click="showPassword(account.AccountId);" class="btn btn-primary">View</button><span></span></td>
<td>{{account.Comments}}</td>
</tr>
My scope controller looks as follows
$scope.showPassword = function (accountId) {
passwordVaultData.getAccountPassword(accountId)
.$promise
.then(function (r) {
//success
}, function (r) {
//fail
});
}
My showPassword() method works and returns the correct password, but I can't figure out how to hide the button and display the password.
Using the ng-show and ng-hide directives against the password on the account object should suffice for modifying the UI
<tr ng-repeat="account in resp.PasswordVaultAccounts">
<td>{{account.Name}}</td>
<td>{{account.Username}}</td>
<td>
<button ng-hide="account.Password" ng-click="showPassword(account.AccountId);" class="btn btn-primary">View</button>
<span ng-show="account.Password">{{account.Password}}</span>
</td>
<td>{{account.Comments}}</td>
</tr>
As for the promise resolution, you want the getAccountPassword to return a promise, I will make an assumption about it's content below
function getAccountPassword(account) {
var deferred = $q.defer();
$http.get('api/vault/' + account.AccountId).then(function(r) {
deferred.resolve(r);
}, function(r) {
deferred.reject(r);
});
return deferred.promise;
}
$scope.showPassword = function (account) {
getAccountPassword(account.AccountId).then(function(password) {
account.Password = password;
}, function(password) {
account.Password = undefined; // some type of error handling here
});
}
Because the promise is executed in the context of an $http call, the digest cycle will run and the elements will be shown based on whether password is populated.
You can accomplish it by either ng-if or ng-show/hide:
Quick sample below:
<tr ng-repeat="account in resp.PasswordVaultAccounts">
<td>{{account.Name}}</td>
<td>{{account.Username}}</td>
<td>
<button ng-if="!account.password" ng-click="showPassword(account);" class="btn btn-primary">View</button><span></span></td>
<span ng-if="account.password">{{password}}</span>
<td>{{account.Comments}}</td>
</tr>
$scope.showPassword = function (account) {
account.password = passwordVaultData.getAccountPassword(account.AccountId)
.$promise
.then(function (r) {
//success
}, function (r) {
//fail
});
}
Please see demo below
var app = angular.module('app', []);
angular.module('app').
controller('firstCtrl', function($scope) {
$scope.resp = {
PasswordVaultAccounts: [{
AccountId: 1,
URL: "bbc.co.uk",
Username: "Jack",
Comments: "White"
}, {
AccountId: 2,
URL: "bbc.co.uk",
Username: "Mike",
Comments: "Green"
}, {
AccountId: 3,
URL: "bbc.co.uk",
Username: "Tim",
Comments: "Red"
}
]
}
$scope.showPassword = function(account) {
//call you backend and on sucess add that :
// passwordVaultData.getAccountPassword(account.accountId)
// .$promise
// .then(function (r) {
account.showpass = true;
account.pass = account.Username + " password is *****"
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="firstCtrl">
<table>
<tr ng-repeat="account in resp.PasswordVaultAccounts">
<td>{{account.Name}}
</td>
<td>{{account.Username}}</td>
<td>
<button ng-click="showPassword(account);" class="btn btn-primary" ng-hide="account.showpass">View</button>
<span ng-show="account.showpass">{{account.pass}}</span>
</td>
<td>{{account.Comments}}</td>
</tr>
</table>
</div>
</body>
My code is like this
<body ng-app="myApp" ng-controller="MainCtrl">
<div>Name only
<input ng-model="search.name" />
<br />
<table id="searchObjResults">
<tr>
<th>Name</th>
<th>Phone</th>
</tr>
<tr ng-repeat="friendObj in friends | filter:search:strict | limitTo:1">
<td>{{friendObj.name}}</td>
<td>{{friendObj.phone}}</td>
</tr>
</table>
</div>
<div>
<button type="button" id="btn_submit" ng-click="submitForm()">Get rates</button>
</div>
angular.module('myApp', []).controller('MainCtrl', ['$http', '$scope', function ($http, $scope) {
$scope.friends = [{
name: 'John',
phone: '555-1276'
}, {
name: 'Mary',
phone: '800-BIG-MARY'
}, {
name: 'Mike',
phone: '555-4321'
}, {
name: 'Adam',
phone: '555-5678'
}, {
name: 'Julie',
phone: '555-8765'
}, {
name: 'Juliette',
phone: '555-5678'
}];
$scope.submitForm = function () {
// i want to get the data here
};
}]);
As you can see at a time only one friend will be active on my screen. when I press my submit button, I want that data (filtered single row) to be the only value on my current $scope.friends so that I can send it to an external service as the selected data. Can any one point out what i need to do here
Fiddle
Note: I can't change the position of this button.
Why not make your button part of the table row, since there will only ever be one? Here is a JSFiddle showing it working in that fashion.
The ng-click function handler for the button can then simply take a parameter that is the actual friendObj you are interested in:
<button type="button" ng-click="submitForm( friendObj )">Get rates</button>
EDIT: There is actually a way to do this if you can't move the button; make the ng-repeat operate over a NEW array, which will be accessible outside of the ng-repeat. So your ng-repeat statement becomes:
<tr ng-repeat="friendObj in newArray = (friends | filter:search:strict | limitTo:1)">
And then your button can simply reference the one-element array:
<button type="button" ng-click="submitForm( newArray )">Get rates</button>
Updated Fiddle here :-)
Try this:
$scope.submitForm = function () {
var data = $filter('filter')($scope.friends, $scope.search.name);
};
Fiddle here.
If you put the filter in the controller instead of the view, you could set a variable like $scope.result that the submitForm function could use. For example, in your HTML, you could add an ng-change directive to your search field like so:
<input ng-model="search.name" ng-change="updateResult()" />
Then, instead of using ng-repeat, you'd use ng-show to show the one result, or hide the row if there is no result:
<tr ng-show="result">
<td>{{result.name}}</td>
<td>{{result.phone}}</td>
</tr>
Then in your controller:
$scope.search = {name: ''};
$scope.updateResult = function() {
$scope.result = $filter('filter')($scope.friends, $scope.search.name)[0];
}
$scope.updateResult();
// ...
$scope.submitForm = function() {
// $scope.result can be used here
}
EDIT: The advantage of this approach is it's a bit DRYer because you don't re-filter inside submitForm. MarcoS's approach has the advantage of being a lot shorter!