So I create a cached json object within an array with the following method in Angular:
$scope.saveClaim = function() {
//always set isOffset to false - empty string does not work for non-string objects in web api when field is required
$scope.claimInfo.isOffset = false;
$scope.claimsSubmit.push($scope.claimInfo);
//clears scope so form is empty
$scope.claimInfo = {
id: "",
benefitId: "",
isSecIns: "",
isNoResId: "",
expenseTypeId: "",
fromDate: "",
toDate: "",
provider: "",
who: "",
depId: "",
age: "",
amount: "",
comments: "",
isOffset: ""
};
}
The idea is the user fills out a form, and at the end either selects to add another claim or submit a claim (the object). After each time the form is filled and user selects file or add another, the form clears and the user then enters more data. The results is an array of object(s) that look like:
[
{
"id": "",
"benefitId": "",
"isSecIns": "",
"isNoResId": "",
"expenseTypeId": "",
"fromDate": "",
"toDate": "",
"provider": "",
"who": "",
"depId": "",
"age": "",
"amount": "",
"comments": "",
"isOffset": false
}
]
If more than one claim is entered, then we get multiple objects with same properties.
Each claim is then displayed with limited data in info boxes that display only 3-4 of the properties.
So I am trying to figure best way to do 3 things. First, add a unique "id" to each object. Second, if the delete icon in the info box selected, then remove that object from the array and if the "edit" icon is selected in the info box, then all the relative properties that that object in the array is populated back to the form.
Googling best tries for this, but not sure how I can work with the json objects this for for now. Can some of you help me on this?
Thanks much.
Hard to give the best way. Probably comes down to your style and preferences. But here is one way to do it, to get you going.
Define your model. It will contain the claim that is bound to the form and an array of added claims.
$scope.viewModel = {
claim: {},
claims: []
};
Add a function that assigns a claim object with default values:
var resetClaim = function() {
$scope.viewModel.claim = {
name: '',
city: ''
};
};
resetClaim();
The form elements will use ng-model:
<input type="text" model="viewModel.claim.name">
We will use ng-repeat to show the added claims:
<tr ng-repeat="claim in viewModel.claims">
Our form will have two buttons:
<button type="submit" ng-click="saveClaim()">Save Claim</button>
<button type="button" ng-click="cancel()">Cancel</button>
The cancel button will just reset the form.
The saveClaim function will look like this:
$scope.saveClaim = function() {
if (!isValidClaim()) return;
$scope.viewModel.claim.id ? updateClaim() : saveNewClaim();
resetClaim();
};
The isValidClaim function just checks if we have entered the requied fields. You could use form validation for this instead.
In this solution when saving a claim it could either be a new claim or an existing one that we have edited, and what we will do in the two cases will differ, so we need a way to tell what we are doing. Here we just check if it has an id. If it hasn't - it's a new claim. If it has, it's an existing.
To save a new claim we will do the following:
var saveNewClaim = function() {
var newClaim = angular.copy($scope.viewModel.claim);
newClaim.id = id++;
$scope.viewModel.claims.push(newClaim);
};
Note that it's important that we use for example angular.copy to create a new copy of the claim that is bound to the view. Otherwise we would just push a reference to the same object to the claims array which is not good since we want to reset one of them.
In this example id is just a variable starting at 0 that we increment each time we create a new claim.
Each element in our ng-repeat will have an edit and a remove icon:
<tr ng-repeat="claim in viewModel.claims">
<th>{{claim.id}}</th>
<td>{{claim.name}}</td>
<td>{{claim.city}}</td>
<td><i class="glyphicon glyphicon-edit" ng-click="editClaim(claim)"></i></td>
<td><i class="glyphicon glyphicon-remove" ng-click="removeClaim(claim)"></i></td>
</tr>
The removeClaim function simply takes a claim and removes it from the array:
$scope.removeClaim = function(claim) {
var index = $scope.viewModel.claims.indexOf(claim);
$scope.viewModel.claims.splice(index, 1);
};
The editClaim function will make a copy of the claim to edit and put it in the variable that is bound to the form:
$scope.editClaim = function(claim) {
$scope.viewModel.claim = angular.copy(claim);
};
You can also do the following:
$scope.viewModel.claim = claim;
And when you edit the claim in the form it will update in the ng-repeat at the same time. But then you have no good way of canceling and the save button wouldn't be needed. So it depends on how you want it to work.
If you edit the claim in the form now and save, we will come back to the saveClaim function:
$scope.saveClaim = function() {
if (!isValidClaim()) return;
$scope.viewModel.claim.id ? updateClaim() : saveNewClaim();
resetClaim();
};
This time the claim will have an id, so the updateClaim function will execute:
var updateClaim = function() {
var claim = $scope.viewModel.claims.filter(function(c) {
return c.id === $scope.viewModel.claim.id;
})[0];
angular.extend(claim, $scope.viewModel.claim);
};
It will retrieve the claim that we are editing from the claims array based on the id. We need to do this since we used angular.copy earlier and have two difference objects.
We will then use angular.extend to move all the new edited values to the claim that we pressed edit on in the ng-repeat.
Demo: http://plnkr.co/edit/yuNcZo7nUyxVsOyPTBEd?p=preview
Related
Angular task 1.5.x:
I have object like this:
{
id: 1,
name: "a",
items: [
{"holiday": "false", "day": "monday"},
{"holiday": "true", "day": "tuesday"...}
]
}
I want a new object to be created in the above way with click of a single button. Note I dont want to add each item separately, all at once. Means, for a single object with name "a", I want to add all items for all days at once.
I can make it work but I want to know the correct way.
ultimately we should be able to create a javascript object in the above format(without id)(I think this format is correct) and send it to server so it will work. But how to add html/angular code so I will get an object that way.
Please let me know for more info.
When using ng-model you do not have to fully define your object in order to have it constructed. E.g.:
Controller
$scope.object= {
items: []
};
var n = 7;
for (var i = 0; i < n; i++)
$scope.object.items({});
HTML
<input type="text" ng-model="object.name"/>
<div ng-repeat="currObj in object.items">
<input type="text" ng-model="currObj.holiday" />
<input type="text" ng-model="currObj.day" />
</div>
The general structure must be defined beforehand, but you do not have to initialize all the properties. They will receive values when binding is triggered (view -> model).
You can do like this :
Create a button in your view and use ng-click directive to capture on click event.
Html:
<button ng-click="createCopy()"></button>
Use angular.copy to create a deep copy of the source object.
This is what angular.copy does as explained in the docs :
Creates a deep copy of source, which should be an object or an array.
If no destination is supplied, a copy of the object or array is
created. If a destination is provided, all of its elements (for
arrays) or properties (for objects) are deleted and then all
elements/properties from the source are copied to it. If source is not
an object or array (inc. null and undefined), source is returned. If
source is identical to destination an exception will be thrown.
Controller:
$scope.mainObject = { id: 1, name: "a", items: [{"holiday": "false", "day": "monday"}, {"holiday": "true", "day": "tuesday"...}] };
$scope.createCopy = function (){
$scope.copyObject = angular.copy($scope.mainObject);
}
My data in firebase looks like the below. I am reading https://www.firebase.com/docs/rest/api/ and its weird, I am trying to remove an item.
burning-*
contacts
-K7qAf6egBeg5l3e_Gjc
name: "Ind"
phonenumber: "(408) ***-***"
uid: "1"
-K7qB8Afu7bIm9LUtV68
name: "Paul Bhayya"
phonenumber: "(408) ***-***"
uid:"2"
Inside angular.js I am making this call inside a custom directive of mine.
$http.delete(Firebaseurl + '/contacts/'+scope.contact.name+'/.json').then(function(result) {
console.log(result);
});
The api is not making sense to me, I see the problem might be that my data is now nested inside a key with a weird ID i.e -K7qAf6egBeg5l3e_Gjc.
So I am wondering how can I make a call to delete an item by the key name so if client side that contact is clicked say Ind gets clicked then I tell Firebase to delete the contact with that name. Maybe ID is better, but whatever works.
EDIT:
FYI I parsed the Firebase object selectedContacts is the result of the GET method for the objects. It wasnt formatted very well for my angular code so I turned it into a clean array of objects and I am using it to compare to other set of data to pass into the $scope
Object.keys(selectedContacts.data).forEach(function(key) {
selectedContactsArray.push(selectedContacts.data[key]);
});
selectedContactsArray.filter( function( item ) {
for( var i=0, len=usersContacts.length; i<len; i++ ){
if( usersContacts[i].name == item.name ) {
usersContacts[i]['selectedContact'] = true
}
}
});
To get a user by their name:
...firebaseio.com/contacts.json?orderBy="name"&equalTo="Ind"&limitToFirst=1
You'll have to add an index to your security rules:
{
"rules": {
"contacts": {
".indexOn": ["name"]
}
}
}
With this index, the query will return an object like this:
{
"-K7qAf6egBeg5l3e_Gjc": {
"name": "Ind",
"phonenumber": "(408) ***-***",
"uid": "1"
}
}
You can read the key from there and then execute a REST DELETE request against
...firebaseio.com/contacts/-K7qAf6egBeg5l3e_Gjc.json
But as discussed in the comments to your question, you can also use AngularFire to do the same.
I am new to Angularjs. I am having the form data which I need to make a post request to the server passing the data. I have done the UI and controller part inside the angularjs but do not know how to capture the form data. Please find the link to plnkr - Plnkr link
By clicking the add button, a new li element gets added and the same gets deleted when the minus sign is clicked. I need to get all the key value items into the below format for sending for Post request.
{
"search_params": [
{
"key": "search string",
"predicate": "matches",
"value": "choosen text"
},
{
"key": "search string",
"predicate": "not-matches",
"value": " search value"
},
{
"key": "search string",
"predicate": "matches",
"value": " search value"
}
]
}
How to capture the form data and construct the param object inside my controller inside the searchParams function inside the controller. Please let me know as I am new to Angularjs.
Updated Question
Based on the inputs, I am able to get the user details in the controller. But there are few things:
By default there will be one li element and when the user submits, the current li elements data should be captured in the controller.
Only when I add the criteria using the plus button, the array is getting updated, but the last elements data is not being updated in the model when submitted.
The same is holding good for the deleting the criteria too.
Link to updated Plunker - Plnkr Link
Expanding on #Chris Hermut and assuming you want an array of map, according to json you posted. You can do that by
var arr = [];
var form = {
name: 'asd',
surname: 'aasdasdsd',
wharever: 'asd'
}
arr.push(form);
//prentending to be another (key,value)
form.surname = 'hfg';
arr.push(form);
here's a fiddle illustrating just that.
Directives like <select>, <input> require ng-model attribute to correctly bind your input to $scope properties.
In your HTML markup you'll have to update your form elements with required attributes (like ng-model).
I would recommend (in controller/link) to use only one object for form data with different properties like
var form = {
name: '',
surname: '',
wharever: ''
}
Corresponding <input> would be ex. <input ng-model="form.name" type="text">
After you have your 'form' object populated you can do JSON.stringify(form) before your request (if your using some other content-type then application/json).
I'm new to AngularJS and have written an app which calls an specific API. It gets an object back like this:
posts = [
posts = [...]
users = [...]
]
That's the HTML template:
<div id="{{'post-' + $index}}" ng-repeat="post in posts.posts">
<article>
<h1>{{post.user.username}}</h1>
<span>{{post.date | date: "dd.MM.yy, HH:mm"}}</span>
<p>{{post.content}}</p>
</article>
</div>
I want to show different posts (from the posts object) including the username (which is in the users object). How can I tell Angular that the posts are in the posts object and the proper usernames are in the users object?
ngRepeat creates a new scope for each entry. That scope will contain $index for the current offset in the array. Since it's a new scope the variable posts is now in the $parent scope.
Assuming posts and users contain parallel data and are arrays. You can lookup data in users using $index.
<h1>{{$parent.posts.users[$index].username}}</h1>`
Without seeing the structure of the posts and users object, it's hard to say exactly, but the general idea is to use the users object, with the user_id from the post object, so either something like this:
<h1>{{users[post.user.id].username}}</h1>
Or like this:
<h1>{{getUserNameForPost(post)}}</h1>
$scope.getUserNameForPost = function(post) {
//find the user based of the post however you want
user = $scope.users.filter(function(u) { return u.id = post.user });
return user[0].username;
}
If possible, I would suggest you change the structure of the returned JSON object, to something similar to the one below:
posts = [
{
id : "post id",
title: "Post title",
content: "Post content",
date : "post date",
user : {
id: "user id",
name: "user name"
}
},
....
....
]
This should work.
Suppose I have the following users:
$scope.users = {
"2": {
email: 'john#gmail.com',
name: 'John'
},
"3": {
email: 'elisa#gmail.com',
name: 'Elisa'
}
}
I would like to create a <select> with the following options:
<option value="3">Elisa</option>
<option value="2">John</option>
In other words, users should be sorted by name.
I tried the following using the (key, value) in expression syntax, but it doesn't work:
<option ng-repeat="(user_id, user) in users | orderBy:'user.name'"
value="{{ user.id }}">
{{ user.name }}
</option>
Live example here.
What am I missing?
Please do not suggest solutions with ng-options as I use ui-select2 which is incompatible with ng-options.
While you would see this in the thread the author's answer references in a link, I thought it would be good to put up one of the mentioned workarounds up on SO:
It is true that this is technically not implemented, but there is an easy work around if you're willing to change your data model slightly: use a custom filter to generate an array of the object's properties (without replacement). If you add the key field to the objects ("id" in the above case), you should be able to get the behavior your looking for:
app.filter("toArray", function(){
return function(obj) {
var result = [];
angular.forEach(obj, function(val, key) {
result.push(val);
});
return result;
};
});
...
$scope.users = {
1: {name: "John", email: "john#gmail.com", id: 1},
2: {name: "Elisa", email: "elisa#gmail.com", id: 2}
};
Here's the ng-repeat directive that could be used:
<option value="{{user.id}}" ng-repeat="user in users | toArray | orderBy:'name'">{{user.name}}</option>
And here's the plunkr.
Notice that the orderBy filter takes name as its parameter and not user.name.
Unfortunately, adding the id property to your objects does create potential for mismatch with it's key in the containing object.
In the link you mentioned in your answer, there are also proposed solutions that create the id property in the user objects on the fly, but I feel like this approach is a little less messy (at the cost of introducing data replication).
OK, I found the answer:
It is not implemented yet :(
Adding to the accepted answer and seeing your data format, you should transform your data as
app.filter('myFilterUsers',function(){
return function(data)
{
var newRes = [];
angular.forEach(data,function(val,key){
val["id"] = key; //Add the ID in the JSON object as you need this as well.
newRes.push(val);
});
return newRes;
}
});