Refresh rows in table, which are created from factory function (SAPUI5) - refresh

How can I "refresh" the data in rows inside a table? I know, that the table is refreshed, when the model is getting changed. But the problem is, that the rows are created by a factory method. The code for the rows looks like this:
formatter : function(text, id) {
if (text != null && id != null) {
if (this.getProperty("showId")) {
return text + " ( " + id + " )";
} else {
return text;
}
}
return "";
So, when I click on a button "hide ID" the property is getting changed and the table should be refreshed so that the content is built new. How can I do this? I checked the method .refresh() but this didn't work.
EDIT:
This is my column with the factory function:
columns : [ new sap.ui.table.Column({
label : "XYZ( ID )",
filterProperty : "SHORT_TEXT",
template : new sap.m.Label().bindProperty("text", {
parts : [ {
path : "SHORT_TEXT",
type : new sap.ui.model.type.String()
}, {
path : "ID",
type : new sap.ui.model.type.String()
} ],
formatter : function(text, id) {
if (text != null && id != null) {
if (this.getProperty("showId")) {
return text + " ( " + id + " )";
} else {
return text;
}
}
return "";
}.bind(this)
})
})
This is the method, which changes the property:
onShowHideIdRequest : function(oControlEvent) {
if (oControlEvent.getParameter("pressed")) {
this.setProperty("showId", true);
sap.ui.getCore().byId("oShowHideIdButton").setIcon("sap-icon://show");
} else {
this.setProperty("showId", false);
sap.ui.getCore().byId("oShowHideIdButton").setIcon("sap-icon://hide");
}
sap.ui.getCore().byId("oTreeTable").rerender();
},
And the the property looks like this inside my component:
metadata : {
properties : {
showId : {
type : "boolean",
defaultValue : true
}
},
The "oTreeTable" ID refers to a sap.ui.tableTreeTable
I thought for a few days this all works fine, I don't know what's no wrong ...

First of all, the method you have written is formatter not the factory method. There is a difference between formatter and factory method. Formatter is used to return the value of a property of ui5 control based on some conditions or evaluation where as factory method is used to bind aggregations

Related

Ionic / angulfire2 - Query join reference multiple times

I'm building app with Ionic and angulfire2 and I'm trying to join multiple references from firebase by using the object key.
Database looks following:
{
"achievements" : {
"200" : {
"authorId" : "nGSlhjaDRKh8XdrgxcusU0wdiHN2",
"description" : "I did it"
}
},
"challengeAchievements" : {
"100" : {
"200" : true
}
},
"challenges" : {
"100" : {
"name" : "test challenge"
},
"101" : {
"name" : "test challenge 2"
}
},
"users" : {
"nGSlhjaDRKh8XdrgxcusU0wdiHN2" : {
"email" : "user1#test.com"
},
"wBMX8WOHIpM7dEkzj0hM19OPMbs1" : {
"email" : "user2#test.com"
}
}
}
I would like to join all this data together so that from challenges you get achievements, and from achievements you get the user data.
Currently I'm able to get the achievement details, but not the user data. My provider looks like this at the moment:
getChallengeAchievements(challengeKey) {
return this.rtdb.list(`/challengeAchievements/${challengeKey}`)
.map(achievements => achievements.map((achievement) => {
if (achievement.key)
achievement.details = this.getAchievementDetails(achievement.key);
achievement.user = this.getAchievementUserDetails(achievement.details.authorId);
return achievement;
}));
}
getAchievementDetails(achievementKey?: string): Observable<any> {
if (achievementKey)
return this.rtdb.object(`/achievements/${achievementKey}`);
}
getAchievementUserDetails(authorId?: string): Observable<any> {
if (authorId)
return this.rtdb.object(`/users/${authorId}`);
else console.log('Not found');
}
How should I structure the authorId query in this function? If I use static value in
achievement.details.authorId('nGSlhjaDRKh8XdrgxcusU0wdiHN2')
I'm able to receive the data.
Solved it by subscribing to the first join "achievement.details" and obtaining the user data from there.
getChallengeAchievements(challengeKey) {
return this.rtdb.list(`/challengeAchievements/${challengeKey}`)
.map(achievements => achievements.map((achievement) => {
if (achievement.key)
achievement.details = this.getAchievementDetails(achievement.key);
achievement.details.subscribe(
details => {
achievement.user = this.getAchievementUserDetails(details.authorId);
})
return achievement;
}));
}

Programmatically add a new field in a template in sitecore

In Sitecore is it possible to programmatically add a new field in a template?
I have a template "DictionaryName", in this template I want to add a field "Newname" with its type "Single-Line Text".
I wrote and tested this code for you - it worked out perfect on my machine and created new single line field within template specified. Here is the method:
private void AddFieldToTemplate(string fieldName, string tempatePath)
{
const string templateOftemplateFieldId = "{455A3E98-A627-4B40-8035-E683A0331AC7}";
// this will do on your "master" database, consider Sitecore.Context.Database if you need "web"
var templateItem = Sitecore.Configuration.Factory.GetDatabase("master").GetItem(tempatePath);
if (templateItem != null)
{
var templateSection = templateItem.Children.FirstOrDefault(i => i.Template.Name == "Template section");
if (templateSection != null)
{
var newField = templateSection.Add(fieldName, new TemplateID(new ID(templateOftemplateFieldId)));
using (new EditContext(newField))
{
newField["Type"] = "Text"; // text stands for single-line lext field type
}
}
{
// there are no template sections here, you may need to create one. template has only inherited fields if any
}
}
}
And below is the usage - first string parameter is the name of your new field, the second is string value for template path within the database you are using:
AddFieldToTemplate("New Single Line Field", "/sitecore/templates/Sample/Sample Item");
Replace "Sample Item" template with your template path and set desired field name to add. Also do not forget usings for namespaces:
using Sitecore;
using Sitecore.Data;
using Sitecore.Data.Items;
Hope this helps!
You can access programmatically a template from the item and then add a an item to this template. The template is a usual item childrens.
Wrote this Example for you.
Want to find out more https://doc.sitecore.com/legacy-docs/SC71/data-definition-api-cookbook-sc70-a4.pdf
public JsonResult CreateTemplate()
{
try
{
using(new SecurityDisabler())
{
///Get Database
Database master = Sitecore.Configuration.Factory.GetDatabase("master");
/// Every node in content tree ia an Item. Ex- Templates,Field, Item, etc.
/// Template: /sitecore/templates/System/Templates/Template -{AB86861A-6030-46C5-B394-E8F99E8B87DB}
var templateId = master.GetTemplate(new ID("{AB86861A-6030-46C5-B394-E8F99E8B87DB}"));
/// parentItem is the item/ Template folder where you want to create your template.
/// ParentItem: /sitecore/templates/[new folder {Guid}]
Item parentItem = master.GetItem(new ID("{3C7516ED-7E3E-4442-8124-26691599596E}"));
Item newItem = parentItem.Add("HelloTemplate", templateId);
// adding Field in Templates.
TemplateItem exampleTemplate = master.Templates[new ID(newItem.ID.ToString())];
TemplateSectionItem data = exampleTemplate?.GetSection("data");
if( data == null || data.InnerItem.Parent.ID != exampleTemplate.ID)
{
data = exampleTemplate.AddSection("Data", false);
}
TemplateFieldItem title = data?.GetField("title");
if(title == null)
{
TemplateFieldItem field = data.AddField("Title");
}
}
return Json(new { Result = "item created" }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(new { Result = "item not created " + ex.Message+"\n"+ex.StackTrace } , JsonRequestBehavior.AllowGet);
}
}

Advanced update using mongodb [duplicate]

In MongoDB, is it possible to update the value of a field using the value from another field? The equivalent SQL would be something like:
UPDATE Person SET Name = FirstName + ' ' + LastName
And the MongoDB pseudo-code would be:
db.person.update( {}, { $set : { name : firstName + ' ' + lastName } );
The best way to do this is in version 4.2+ which allows using the aggregation pipeline in the update document and the updateOne, updateMany, or update(deprecated in most if not all languages drivers) collection methods.
MongoDB 4.2+
Version 4.2 also introduced the $set pipeline stage operator, which is an alias for $addFields. I will use $set here as it maps with what we are trying to achieve.
db.collection.<update method>(
{},
[
{"$set": {"name": { "$concat": ["$firstName", " ", "$lastName"]}}}
]
)
Note that square brackets in the second argument to the method specify an aggregation pipeline instead of a plain update document because using a simple document will not work correctly.
MongoDB 3.4+
In 3.4+, you can use $addFields and the $out aggregation pipeline operators.
db.collection.aggregate(
[
{ "$addFields": {
"name": { "$concat": [ "$firstName", " ", "$lastName" ] }
}},
{ "$out": <output collection name> }
]
)
Note that this does not update your collection but instead replaces the existing collection or creates a new one. Also, for update operations that require "typecasting", you will need client-side processing, and depending on the operation, you may need to use the find() method instead of the .aggreate() method.
MongoDB 3.2 and 3.0
The way we do this is by $projecting our documents and using the $concat string aggregation operator to return the concatenated string.
You then iterate the cursor and use the $set update operator to add the new field to your documents using bulk operations for maximum efficiency.
Aggregation query:
var cursor = db.collection.aggregate([
{ "$project": {
"name": { "$concat": [ "$firstName", " ", "$lastName" ] }
}}
])
MongoDB 3.2 or newer
You need to use the bulkWrite method.
var requests = [];
cursor.forEach(document => {
requests.push( {
'updateOne': {
'filter': { '_id': document._id },
'update': { '$set': { 'name': document.name } }
}
});
if (requests.length === 500) {
//Execute per 500 operations and re-init
db.collection.bulkWrite(requests);
requests = [];
}
});
if(requests.length > 0) {
db.collection.bulkWrite(requests);
}
MongoDB 2.6 and 3.0
From this version, you need to use the now deprecated Bulk API and its associated methods.
var bulk = db.collection.initializeUnorderedBulkOp();
var count = 0;
cursor.snapshot().forEach(function(document) {
bulk.find({ '_id': document._id }).updateOne( {
'$set': { 'name': document.name }
});
count++;
if(count%500 === 0) {
// Excecute per 500 operations and re-init
bulk.execute();
bulk = db.collection.initializeUnorderedBulkOp();
}
})
// clean up queues
if(count > 0) {
bulk.execute();
}
MongoDB 2.4
cursor["result"].forEach(function(document) {
db.collection.update(
{ "_id": document._id },
{ "$set": { "name": document.name } }
);
})
You should iterate through. For your specific case:
db.person.find().snapshot().forEach(
function (elem) {
db.person.update(
{
_id: elem._id
},
{
$set: {
name: elem.firstname + ' ' + elem.lastname
}
}
);
}
);
Apparently there is a way to do this efficiently since MongoDB 3.4, see styvane's answer.
Obsolete answer below
You cannot refer to the document itself in an update (yet). You'll need to iterate through the documents and update each document using a function. See this answer for an example, or this one for server-side eval().
For a database with high activity, you may run into issues where your updates affect actively changing records and for this reason I recommend using snapshot()
db.person.find().snapshot().forEach( function (hombre) {
hombre.name = hombre.firstName + ' ' + hombre.lastName;
db.person.save(hombre);
});
http://docs.mongodb.org/manual/reference/method/cursor.snapshot/
Starting Mongo 4.2, db.collection.update() can accept an aggregation pipeline, finally allowing the update/creation of a field based on another field:
// { firstName: "Hello", lastName: "World" }
db.collection.updateMany(
{},
[{ $set: { name: { $concat: [ "$firstName", " ", "$lastName" ] } } }]
)
// { "firstName" : "Hello", "lastName" : "World", "name" : "Hello World" }
The first part {} is the match query, filtering which documents to update (in our case all documents).
The second part [{ $set: { name: { ... } }] is the update aggregation pipeline (note the squared brackets signifying the use of an aggregation pipeline). $set is a new aggregation operator and an alias of $addFields.
Regarding this answer, the snapshot function is deprecated in version 3.6, according to this update. So, on version 3.6 and above, it is possible to perform the operation this way:
db.person.find().forEach(
function (elem) {
db.person.update(
{
_id: elem._id
},
{
$set: {
name: elem.firstname + ' ' + elem.lastname
}
}
);
}
);
I tried the above solution but I found it unsuitable for large amounts of data. I then discovered the stream feature:
MongoClient.connect("...", function(err, db){
var c = db.collection('yourCollection');
var s = c.find({/* your query */}).stream();
s.on('data', function(doc){
c.update({_id: doc._id}, {$set: {name : doc.firstName + ' ' + doc.lastName}}, function(err, result) { /* result == true? */} }
});
s.on('end', function(){
// stream can end before all your updates do if you have a lot
})
})
update() method takes aggregation pipeline as parameter like
db.collection_name.update(
{
// Query
},
[
// Aggregation pipeline
{ "$set": { "id": "$_id" } }
],
{
// Options
"multi": true // false when a single doc has to be updated
}
)
The field can be set or unset with existing values using the aggregation pipeline.
Note: use $ with field name to specify the field which has to be read.
Here's what we came up with for copying one field to another for ~150_000 records. It took about 6 minutes, but is still significantly less resource intensive than it would have been to instantiate and iterate over the same number of ruby objects.
js_query = %({
$or : [
{
'settings.mobile_notifications' : { $exists : false },
'settings.mobile_admin_notifications' : { $exists : false }
}
]
})
js_for_each = %(function(user) {
if (!user.settings.hasOwnProperty('mobile_notifications')) {
user.settings.mobile_notifications = user.settings.email_notifications;
}
if (!user.settings.hasOwnProperty('mobile_admin_notifications')) {
user.settings.mobile_admin_notifications = user.settings.email_admin_notifications;
}
db.users.save(user);
})
js = "db.users.find(#{js_query}).forEach(#{js_for_each});"
Mongoid::Sessions.default.command('$eval' => js)
With MongoDB version 4.2+, updates are more flexible as it allows the use of aggregation pipeline in its update, updateOne and updateMany. You can now transform your documents using the aggregation operators then update without the need to explicity state the $set command (instead we use $replaceRoot: {newRoot: "$$ROOT"})
Here we use the aggregate query to extract the timestamp from MongoDB's ObjectID "_id" field and update the documents (I am not an expert in SQL but I think SQL does not provide any auto generated ObjectID that has timestamp to it, you would have to automatically create that date)
var collection = "person"
agg_query = [
{
"$addFields" : {
"_last_updated" : {
"$toDate" : "$_id"
}
}
},
{
$replaceRoot: {
newRoot: "$$ROOT"
}
}
]
db.getCollection(collection).updateMany({}, agg_query, {upsert: true})
(I would have posted this as a comment, but couldn't)
For anyone who lands here trying to update one field using another in the document with the c# driver...
I could not figure out how to use any of the UpdateXXX methods and their associated overloads since they take an UpdateDefinition as an argument.
// we want to set Prop1 to Prop2
class Foo { public string Prop1 { get; set; } public string Prop2 { get; set;} }
void Test()
{
var update = new UpdateDefinitionBuilder<Foo>();
update.Set(x => x.Prop1, <new value; no way to get a hold of the object that I can find>)
}
As a workaround, I found that you can use the RunCommand method on an IMongoDatabase (https://docs.mongodb.com/manual/reference/command/update/#dbcmd.update).
var command = new BsonDocument
{
{ "update", "CollectionToUpdate" },
{ "updates", new BsonArray
{
new BsonDocument
{
// Any filter; here the check is if Prop1 does not exist
{ "q", new BsonDocument{ ["Prop1"] = new BsonDocument("$exists", false) }},
// set it to the value of Prop2
{ "u", new BsonArray { new BsonDocument { ["$set"] = new BsonDocument("Prop1", "$Prop2") }}},
{ "multi", true }
}
}
}
};
database.RunCommand<BsonDocument>(command);
MongoDB 4.2+ Golang
result, err := collection.UpdateMany(ctx, bson.M{},
mongo.Pipeline{
bson.D{{"$set",
bson.M{"name": bson.M{"$concat": []string{"$lastName", " ", "$firstName"}}}
}},
)

EXTJS grid store load - adding parameters?

I'm in the process on converting an asp repeater into an EXTJS grid. Above the repeater is a dropdown and a radiobutton list. The dropdown selects which clients' data the repeater shows, and the radiobuttonlist selects the query type (default, resource, or role). Currently, when the ddl or radiobutton is changed, the page postsback with the new data.
I'm not sure how to pass the value of these two objects into my static webservice on the backend via the extjs store api GET call.
The extjs store code...
store: Ext.create('Ext.data.Store', {
autoLoad: true,
autoSync: false,
model: 'Assembly',
proxy: {
type: 'ajax',
headers: { "Content-Type": 'application/json' },
api: {
read: '/Admin/BillRateData.aspx/Get'
},
reader: {
type: 'json',
root: function (o) {
if (o.d) {
return o.d;
} else {
return o.children;
}
}
},
writer: {
type: 'json',
root: 'jsonData',
encode: false,
allowSingle: false
},
listeners: {
exception: function (proxy, response, operation) {
Ext.MessageBox.show({
title: "Workflow Groups Error",
msg: operation.action + ' Operation Failed: ' + operation.getError().statusText,
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
}
}
}
And the webservice...(with some psuedocode)
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = true)]
public static List<BillRate> Get()
{
using (TimEntities db = new TimEntities())
{
int tableId = Int32.Parse(ddlTable.SelectedValue);
var defaultQry = from t1 in db.BillCostTableDatas
where t1.TableId == tableId
&& t1.ResourceId == 0 && t1.RoleId == 0
orderby t1.Rate
select new
{
id = t1.Id,
resource = "",
role = "",
rate = t1.Rate,
TierName = ""
};
var resourceQry = from t1 in db.BillCostTableDatas
join t2 in db.Machines on t1.ResourceId equals t2.Machine_ID
join t3 in db.TOMIS_USER on t2.Machine_User_ID equals t3.User_ID
join t4 in db.PricingTierNames on t1.PricingTierID equals t4.TierID
where t1.TableId == tableId
&& t1.ResourceId != 0
&& t1.RoleId == 0
orderby t3.LName, t3.FName, t1.Rate, t4.TierName
select new
{
id = t1.Id,
resource = t3.LName + ", " + t3.FName,
role = "",
rate = t1.Rate,
TierName = t4.TierName
};
var roleQry = from t1 in db.BillCostTableDatas
join t2 in db.TaskRoles on t1.RoleId equals t2.Id
where t1.TableId == tableId
&& t1.ResourceId == 2 && t1.RoleId != 0
orderby t2.Name, t1.Rate
select new
{
id = t1.Id,
resource = "",
role = t2.Name,
rate = t1.Rate,
TierName = ""
};
if (this.rblOptions.SelectedValue == "resource")
{
var results = from Res in resourceQry.ToList()
select new BillRate
{
};
return results.ToList();
}
else if (this.rblOptions.SelectedValue == "role")
{
var results = from Res in roleQry.ToList()
select new BillRate
{
};
return results.ToList();
}
else
{
var results = from Res in defaultQry.ToList()
select new BillRate
{
};
return results.ToList();
}
return null;
}
}
If you trigger your store loading manually, you can pass the params options to the load method.
Example:
var store = Ext.create('Ext.data.Store', {
// prevent the store from loading before we told it to do so
autoLoad: false
...
});
store.load({
params: {clientId: 123, queryType: 'default'}
...
});
If you want the params to be sent for multiple subsequent queries, you can write them in the extraParams property of the proxy.
Example:
var store = Ext.create('Ext.data.Store', { ... });
Ext.apply(store.getProxy().extraParams, {
clientId: 321
,queryType: 'role'
});
// the store will still need a refresh
store.reload();
The way these params are passed to the server will depend on the type of request. For GET ones, they will be appended as query params; for POST they will be embedded in the request body.

What's the best way to build an aggregate document in couchdb?

Alright SO users. I am trying to learn and use CouchDB. I have the StackExchange data export loaded as document per row from the XML file, so the documents in couch look basically like this:
//This is a representation of a question:
{
"Id" : "1",
"PostTypeId" : "1",
"Body" : "..."
}
//This is a representation of an answer
{
"Id" : "1234",
"ParentId" : "1",
"PostTypeId" : "2"
"Body" : "..."
}
(Please ignore the fact that the import of these documents basically treated all the attributes as text, I understand that using real numbers, bools, etc. could yield better space/processing efficiency.)
What I'd like to do is to map this into a single aggregate document:
Here's my map:
function(doc) {
if(doc.PostTypeId === "2"){
emit(doc.ParentId, doc);
}
else{
emit(doc.Id, doc);
}
}
And here's the reduce:
function(keys, values, rereduce){
var retval = {question: null, answers : []};
if(rereduce){
for(var i in values){
var current = values[i];
retval.answers = retval.answers.concat(current.answers);
if(retval.question === null && current.question !== null){
retval.question = current.question;
}
}
}
else{
for(var i in values){
var current = values[i];
if(current.PostTypeId === "2"){
retval.push(current);
}
else{
retval.question = current;
}
}
}
return retval;
}
Theoretically, this would yield a document like this:
{
"question" : {...},
"answers" : [answer1, answer2, answer3]
}
But instead I am getting the standard "does not reduce fast enough" error.
Am I using Map-Reduce incorrectly, is there a well-established pattern for how to accomplish this in CouchDb?
(Please also note that I would like a response with the complete documents, where the question is the "parent" and the answers are the "children", not just the Ids.)
So, the "right" way to accomplish what I'm trying to do above is to add a "list" as part of my design document. (and the end I am trying to achieve appears to be referred to as "collating documents").
At any rate, you can configure your map however you like, and combine it with an a "list" in the same function.
To solve the above question, I eliminated my reduce (only have a map function), and then added a function like the following:
{
"_id": "_design/posts",
"_rev": "11-8103b7f3bd2552a19704710058113b32",
"language": "javascript",
"views": {
"by_question_id": {
"map": "function(doc) {
if(doc.PostTypeId === \"2\"){
emit(doc.ParentId, doc);
}
else{
emit(doc.Id, doc);
}
}"
}
},
"lists": {
"aggregated": "function(head, req){
start({\"headers\": {\"Content-Type\": \"text/json\"}});
var currentRow = null;
var currentObj = null;
var retval = [];
while(currentRow = getRow()){
if(currentObj === null || currentRow.key !== currentObj.key){
currentObj = {key: currentRow.key, question : null, answers : []};
retval.push(currentObj);
}
if(currentRow.value.PostTypeId === \"2\"){
currentObj.answers.push(currentRow.value);
}
else{
currentObj.question = currentRow.value;
}
}
send(toJSON(retval));
}"
}
}
So, after you have some elements loaded up, you can access them like so:
http://localhost:5984/<db>/_design/posts/_list/aggregated/by_question_id?<standard view limiters>
I hope this saves people some time.