first of all, I'm quite new to angularJS, so be gentle!
Second, I know there are no "ID"s, "class"es etc. in angularJS so the title might be somewhat misleading. However, I failed to describe the issue any better.
Here's the deal: I have an array of elements, and each has its details. Now, I ng-repeat the first set of elements to show the basic info. And when user clicks "details", the corresponding details are loaded. Yet, in my case, each item gets the details of a selected element.
The case in practice: http://jsbin.com/zilayija/2/edit
Is there any way to append the corresponding details to the ones that match Id ONLY?
The real issue here is that you're binding the details to a single array on your $scope, namely details. As this reference is used in each iteration of the ng-repeat, you'll end up showing it everywhere.
Edit: As mentioned in the comments, the original question did not reflect what was actually asked. What was requested was that the details needed to be loaded in asynchronously and only when they were requested (clicked). I therefore updated the code example).
What changed?
HTML:
<li ng-repeat="item in items">
{{item.name}}
show details
<p ng-show="item.detailsVisible" ng-repeat="detail in getDetails(item.id)">
{{detail.belongsToItem}}
</p>
</li>
JS:
var Item = function(id, name){
var promiseDetailsAreLoaded;
var self = this;
this.id = id;
this.name = name;
this.detailsVisible = false;
this.details = [];
this.showDetails = function(){
if(!promiseDetailsAreLoaded){
promiseDetailsAreLoaded = $http.get("").then(function(){
// mock adding details
for(var i = 0; i < itemDetails.length; i++){
if(itemDetails[i].belongsToItem === self.id){
self.details.push(itemDetails[i]);
}
}
});
}
promiseDetailsAreLoaded.then(function(){
self.detailsVisible = !self.detailsVisible;
});
}
};
$scope.items = [
new Item(1, "Item 1"),
new Item(2, "Item 2"),
new Item(3, "Item 3")
];
I guess you want to archive a master/detail view. You need to place the displaying of the details outside the ng-repeat. You also do not need an array for the details. Just use an object on the scope and assign it in getDetails.
http://jsbin.com/heyaqexa
Related
I have a Feed List for posting comments in my UI5 xml view
<layout:content>
<m:FeedInput post="onFeedPost" class="sapUiSmallMarginTopBottom"/>
<m:List id="feedList" showSeparators="Inner" items="{path: '/table', sorter: {path: 'DATE', descending: true}}">
<m:FeedListItem sender="{MEMBERID}" timestamp="{DATE}" text="{COMMENT}" convertLinksToAnchorTags="All"/>
</m:List>
</layout:content>
I want to not display duplicate comments that have the same text and date, but keep them in the database. My idea was to in the controller iterate over over the items to do this, but I am not sure what to do with the resulting array
var results = [];
var comments = feed.getItems();
for (var n = 0; n < comments.length - 1; n++) {
var contained = false;
for (var m = n + 1; m < comments.length; m++) {
if (comments[n].getText() === comments[m].getText() &&
comments[n].getDate() === comments[m].getDate()) {
comments.pop(m);
contained = true;
if (!results.includes(comments[n])) {
results.push(comments[n]);
}
}
}
if (!contained && !results.includes(comments[n])) {
results.push(comments[n]);
}
}
// replace list items with results array
I can't figure out how to replace the feed list's items with the new array as there is a getItems function but not a setItems function. It occurs to me there is probably a simpler more idiomatic UI5 way to do this but I haven't found it yet.
First off, the correct way to handle this situation is in the OData service. The service should remove the duplicates before sending the data to the client. If we assume, however, that you can't do this server side, then you have some options.
1.) Do not bind the list items to anything. Instead, use the ODataModel to read the data, then filter out duplicates, create a new list item and add it to the list
Read the data using the ODataModel, then pass the results to a method that will filter and add them items to the list
oModel.read("/EntitySet", {
success: function(oResponse) {
this._addCommentsToList(oResponse.results)
}.bind(this)
})
In your method to handle the results, you'll need to do three things -- create a new FeedListItem, set the binding context of the list item, and then add the list item to the list
var aDistinctComments = //use your logic to filter out duplicates
aDistinctComments.forEach(function(oComment) {
//to set the binding context, you'll need the entity key/path
var sCommentKey = oModel.createKey("/EntitySet", oComment)
//create a new binding context
var oContext = oModel.createBindingContext(sCommentKey)
//create a new FeedListItem
var oItem = new FeedListItem({
sender: "{MemberId}",
...
});
//set the context of the item and add it to the list
oItem.setBindingContext(oContext);
oList.addItem(oItem);
})
2.) Bind the list directly to the OData entity set and then when the list receives the data, iterate over the items and hide the duplicates
<List items="{/EntitySet}" updateFinished="onListUpdateFinished"....>
----- onListUpdateFinished ---
var aItems = oList.getItems();
for (var m = n + 1; m < aItems.length; m++) {
//set a boolean, true if duplicate
var bDuplicate = aItems[m].getText() ==== aItems[n].getText() &&
aItems[m].getDate() === aItems[n].getDate();
//set the visibility of the item to true if it is not a duplicate
aItems[m].setVisible(!bDuplicate)
}
3.) Read the data manually, remove duplicates, and stash it in a JSON model, and bind the table to your JSON model path
oModel.read("/EntitySet", {
success: function(oResponse) {
this._addCommentsToJSONModel(oResponse.results)
}.bind(this)
})
You can stash an array of objects in your JSON model, and then bind the table items to that path
var aDistinctComments = // your logic to get distinct comments
oJSONModel.setProperty("/comments", aDistinctComments)
oList.setModel(oJSONModel);
-----
<List items="{/comments"}....>
4.) Bind your list items to your entity set, iterate over the items, and then remove duplicates from the list. I don't recommend this approach. Removing items manually from lists bound to an entity set can lead to trouble with duplicate IDs.
var oItem = //use your logic to find a duplicate list item
oList.removeItem(oItem)
I recommend first handling this server side in the OData service, and if that's not an option, then use option 1 above. This will give you the desired results and maintain the binding context of your list items. Options 2 and 3 will get you the desired results, but depending on your applicaiton, may make working with the list more difficult.
Here is one approach :
Do not directly bind the list to your oData.
You can create a JSON model which will be the resulting model after removing duplicate items.
Bind the JSON model to the List as such:
var oList = this.getView().byId("feedList");
oList.bindAggregation("items", "pathToJsonArray", template);
(The template is feedlistitem in this case).
I have data being sent to a custom data list from the following code:
// Get the site name and dataLists
var site = siteService.getSite("Testing");
var dataLists = site.getContainer("dataLists");
// Check for data list existence
if (!dataLists) {
var dataLists = site.createNode("dataLists", "cm:folder");
var dataListProps = new Array(1);
dataListProps["st:componentId"] = "dataLists";
dataLists.addAspect("st:siteContainer", dataListProps);
dataLists.save();
}
// Create new data list variable
var orpList = dataLists.childByNamePath("orplist1");
// If the data list hasn't been created yet, create it
if (!orpList) {
var orpList = dataLists.createNode("orplist1","dl:dataList");
// Tells Alfresco share which type of items to create
orpList.properties["dl:dataListItemType"] = "orpdl:orpList";
orpList.save();
var orpListProps = [];
orpListProps["cm:title"] = "Opportunity Registrations: In Progress";
orpListProps["cm:description"] = "Opportunity registrations that are out for review.";
orpList.addAspect("cm:titled", orpListProps);
}
// Create new item in the data list and populate it
var opportunity = orpList.createNode(execution.getVariable("orpWorkflow_nodeName"), "orpdl:orpList");
opportunity.properties["orpdl:nodeName"] = orpWorkflow_nodeName;
opportunity.properties["orpdl:dateSubmitted"] = Date().toString();
opportunity.properties["orpdl:submissionStatus"] = "Requires Revisions";
opportunity.save();
This correctly creates data list items, however, at other steps of the workflow require these items to be updated. I have thought of the following options:
Remove the data list item and add another with the updated information
Simply update the data list item
Unfortunately I have not found adequate solutions elsewhere to either of these options. I attempted to use orpWorkflow_nodeName, which is a unique identifier generated at another step, to identify a node to find it. This does not seem to work. I am also aware that nodes have unique identifiers generated by Alfresco itself, but documentation doesn't give adequate information on how to obtain and use this.
My question:
Instead of var opportunity = orpList.createNode(), what must I use in
place of createNode() to identify an existing node so I can update its
properties?
You can use this to check existing datalist item.
var opportunity = orpList .childByNamePath(execution.getVariable("orpWorkflow_nodeName"));
// If the data list Item is not been created yet, create it
if (!opportunity ) {
var orpList = orpList .createNode(execution.getVariable("orpWorkflow_nodeName"),"dl:dataList");}
I'm new with angular and I have a problem with a filter.
I have two different file json like this:
[
{
"IdPers": "1067",
"CognNome": "JANE SMITH",
"Sex": "F"
},
{
"IdPers": "1093",
"CognNome": "JOHN SMITH",
"Sex": "M"
}
]
and:
[
{
"IdPers": "1067",
"DescRuol": "Membro"
},
{
"IdPers": "163",
"DescRuol": "Membro"
}
]
I Put a working Plunker: http://plnkr.co/edit/1xkyxRallRGtj83fSteg?p=preview
I have to create a filter with the field "DescRuol" which is in the file "OutCommissioni.json", the two file have the same field "id". I thought that I can do like a join but I can't make something that works! Thanks for any help
For future readers, please ignore my other answer - I mis-understood the question and worked out the details with OP in the comments there.
To have a select box with multiple options using the same value, first thing is to not use the id as the direct value that the select box will use - instead reference the object directly:
<select data-ng-options="item as item.DescRuol for item in OutCommissioni" id="DescRuol" data-ng-model="filter.IdPers" class="form-control input-sm"></select>
"item as item.DescRuol" will use the object itself as the model value, rather than just the id value.
Next, rather than using the "filter" object directly in the filter, provide a new object which contains the values you need from your filter object:
<tr data-ng-repeat="Person in OutAnagrafica|filter:{Sex:filter.Sex,IdPers:filter.IdPers.IdPers}">
Working example here: http://plnkr.co/edit/gMvuNny99b8aV66C8GAw?p=preview
Edit: ignore this answer - I misunderstood the question. New answer submitted with final details after I worked out what OP really wanted in the comments of this answer.
After fetching data from each file, normalize the difference in properties by assigning the relevant differing field to a common property, and append both sets of data to a single array.
var App = angular.module('App', []);
App.controller("OutAnCtrl", function ($http, $scope) {
$scope.data = [];
$http.get('OutAnagrafica.json')
.success(function (data) {
data.forEach(function(item) {
item.name = item.CognNome;
});
$scope.data.push.apply($scope.data, data);
});
$http.get('OutCommissioni.json')
.success(function (data) {
data.forEach(function(item) {
item.name = item.DescRuol;
});
$scope.data.push.apply($scope.data, data);
});
$scope.clearBox = function () {
$scope.filter = "";
};
});
Then finally change your repeater to use the merged data object "data" and the common property name.
<tr data-ng-repeat="Person in data|filter:filter">
<td>{{Person.IdPers}}</td>
<td>{{Person.name}}</td>
<td>{{Person.Sex}}</td>
</tr>
updated example: http://plnkr.co/edit/03rwNY7eLX9i9VCTHz7Z?p=preview
Edit: I probably mis-understood and you probably don't need those properties merged. The key part is just to append both arrays to a single array using Array.push.apply
// Create initial array
$scope.data = [];
// Append some arrays
$scope.data.push.apply($scope.data, [1,2,3]);
$scope.data.push.apply($scope.data, [4,5,6]);
// $scope.data will now contain [1,2,3,4,5,6]
Edit: I think this is what you're looking for?
http://plnkr.co/edit/jvyXlpv2iBh2n4pZ1miv?p=preview
<label for="DescRuol">DescRuol:</label>
<select data-ng-options="item.IdPers as item.DescRuol for item in OutCommissioni" id="DescRuol" data-ng-model="filter.IdPers" class="form-control input-sm"></select>
How am I able to create an index for the object data that I am passing into Firebase?
I am using the .$add function in the AngularFire library to push the data. This is the filter and controller that I am using:
angular.module('bestDay', ["firebase"]).factory("GreatService", ["$firebase", function($firebase) {
var ref = new Firebase("https://quickjournal.firebaseIO.com/");
return $firebase(ref);
}])
.controller("bdctrl", ["$scope", "GreatService",
function($scope, greatService) {
$scope.theval = "Val " + Math.round(Math.random()*101);
$scope.daylist = greatService;
$scope.addDayGood = function() {
$scope.daylist.$add({
desc: $scope.newDay.desc,
date: $scope.newDay.date,
value: $scope.theval
});
$scope.newDay.desc = "";
$scope.newDay.date = "";
};
}
]);
As you can see, I was attempting to use a unique value when passing the objects in, but it was only generating the same number every time (13). If it isn't apparent, I am semi-new to programming.
I would also like to be able to write a function that will remove the data by that index. Since I am unable to conquer the prior task, I may need assistance in doing this as well.
I am writing my code with the angularjs library.
I have combed through the firebase and angularfire library documentation with no results. If you could point me to a URL with the documentation on this, it would be much appreciated.
Firebase should do the indexing, as this makes it easier if you have more than one user accessing the same data.
Relevant to your question, you should look up https://www.firebase.com/docs/ordered-data.html for working with lists in firebase.
More the point, the push() function provided makes for easy chronological sorting, and if you need more complex sorting you can look at the setWithPriority() function.
angular.module('bestDay', ["firebase"])
.controller("bdctrl", ['$scope', '$firebase',
function($scope,$firebase) {
var daysRef = new Firebase("https://quickjournal.firebaseIO.com/daylist/");
$scope.dayList = $firebase(daysRef);
$scope.dayLocationInFirebase = daysRef.push();
$scope.addDayGood = function(){
// Setdata to the generated location
$scope.dayLocationInFirebase.set({
desc: $scope.newDay.desc,
date: $scope.newDay.date
});
//holds reference to location the object was pushed to, for direct manipulation of the value. Pass it to the scope or an array if you need it for later
var pushedName = $scope.dayLocationInFirebase.name();
alert(pushedName);
$scope.newDay.desc = "";
$scope.newDay.date = "";
}
}
]);
I tried figuring this out by reading both how the Tags are handled in the Todo-List Example and how the RSVPS are handled in the Parties example, but I could not figure out a general way to achieve my Goal.
I have a Plan Collection which has a name ownerid and excerciselist
Plans.insert({name: names[i], ownerId: 1, excerciselist: excercises});
Now in this Excerciselist, I want to add an undefined Amout of Excercises by ID.
I have an Excercisecollection with the following:
Excercises.insert({name: names[i], muscle: muscles[i], device: devices[i], description: descriptions[i]});
Now all these Excercises have a unique _id element by default.
Adding things to the excerciselist no Problem I can do that by Push.
But what I can not figure out is, how I can access only certain ID's in the excerciselist via it's Index.
I can only access the whole Stringarray and output in in HTML via
{{#each planarray}}
{{excerciselist}}
{{/each}}
But there is no possiblity to do smoething like
{{ excerciselist }}
I have also tried returning only excerciselist to the planarray, but the problem is that because it is only indexed and not mapped it can not be accessed by the LiveHTML.
Does anyone have an Idea how this problem could be solved?
Why don't you add a field for the unique id to the Excersies insert?
Excercises.insert({ uniqueID: [i], name: names[i], muscle: muscles[i], device: devices[i], description: descriptions[i]});
This way you can get just the excercise you want based on the uniqueID-field.
Oh and you should probably call "uniqueID" something that makes more sense.
I found a little Workaround which is not exactly what I had in mind but it gets the job done.
Template.editplan.excercises = function() {
var names = [];
var add = [];
var adder = null;
for(var i = 0; i < this.excerciselist.length; i++)
{
add[i] = [];
adder = Excercises.find({_id: this.excerciselist[i]});
add[i]['_id'] = this.excerciselist[i];
add[i]['location'] = i;
adder.forEach(function (obj) {
add[i]['name'] = obj.name;
});
names.push(add[i]);
}
return names;
};
Basically I made a new Array in which i put the Data I want to have so I can read it in the LiveHTML see example below
{{#each planarray}}
<h1>{{name}}</h1>
{{#each excercises}}
{{name}}
{{/each}}
<select name="selectedexcercise{{_id}}" id="selectedexcercise{{_id}}">
{{> excerciseoption}}
</select>
<input type="button" class="addtoplan" value="Eine Übung hinzfügen">
{{/each}}
But there must be a more efficient or nice way.... At least I hope so!