I currently have this in a simple MVC Api controller:
var rootFolder = Umbraco.TypedMedia(200);
return rootFolder.Children().Select(s => new MediaItem
{
Name = s.Name,
Children = s.Children.Select(e => new MediaItem
{
Name = e.Name
})
});
It works, but only return level 1 and 2.
I tried using
return rootFolder.Descendants(), which returns all results from all levels - but "flattened out", so I cannot see the structure in the output.
The output is used in a simple app, navigating a tree structure.
Any ideas, as to how I can make it recursive?
Using Descendants, the output is returned like this
[
{
"Name":"dok1"
},
{
"Name":"dok2"
},
{
"Name":"dok21"
}
]
But it should be
[
{
"Name":"dok1"
},
{
"Name":"dok2"
"Children": [
{
"Name":"dok21"
}
]
}
Not sure you really need recursion here -- the solution below (or something similar) should suffice
// Dictionary between level/depth(int) and the files on that level/depth
var fileDictionary = new Dictionary<int, List<MediaItem>>();
var children = rootFolder.Children();
var depth = 1;
while (children.Any())
{
var tempList = new List<MediaItem>();
children.ForEach(child => {
tempList.Add(child);
});
fileDictionary.Add(depth, tempList);
children = children.Children();
depth++;
}
Then, you can do something like:
foreach (var key in fileDictionary.Keys)
{
// Access the key by key.Key (key would be "depth")
// Access the values by fileDictionary[key] (values would be list of MediaItem)
}
Why not just create a recursive function like so?
IEnumerable<MediaItem> ConvertToMediaItems(IEnumerable<IPublishedContent> items)
{
return items?.Select(i => new MediaItem
{
Name = i.Name,
Children = ConvertToMediaItems(i.Children)
}) ?? Enumerable.Empty<MediaItem>();
}
Then the usage would be
var rootFolder = Umbraco.TypedMedia(200);
return ConvertToMediaItems(rootFolder.Children());
You can also make the function a local function if it's only needed in one place.
Related
CommentCollection
{
"_id":"5b63f0f23846b70011330889",
"CommentType":"task",
"EntityReferenceId":"6082ef25-6f9a-4874-a832-f72e0f693409",
"Threads":[
{
"_id":"69bcef71-3695-4340-bdec-4a6e4c58c490",
"CommentType":"task",
"UserId":ObjectId("52ffc4a5d85242602e000000"),
"CommentByUserType":"Admin",
"EntityReferenceId":"6082ef25-6f9a-4874-a832-f72e0f693409",
"Content":"fdffd",
},
{
"_id":"69bcef71-3695-4340-bdec-4a6e4c58c490",
"CommentType":"task",
"UserId":ObjectId("52ffc4a5d85242602e000000"),
"CommentByUserType":"Admin",
"EntityReferenceId":"6082ef25-6f9a-4874-a832-f72e0f693409",
"Content":"fdffd",
}
]
}
Here I have to write a Mongodb filter query from asp.net core based on two conditions,
first I want to get CommentCollection by EntityReferenceId, then want to find the specific thread by id from the first result.
Any help will be appreciated.
I got the answer,I have wrote my method like below
public async Task<bool> UpdateCommentAsync(Threads thread)
{
var builder = Builders<Comments>.Filter;
var filter = builder.Empty;
var update = Builders<Comments>.Update.Set("Threads.$[i].Content", thread.Content);
var arrayFilters = new List<ArrayFilterDefinition> { new JsonArrayFilterDefinition<Threads>("{'i._id': '" + thread.Id + "'}") };
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var result = await _context.Comments.UpdateManyAsync(filter, update, updateOptions);
return result.ModifiedCount > 0;
}
I'm using Q Promises to retrieve data from my redis repository. The problem I'm having, is that through each iteration, the array object (localEncounter) I'm using to store data returned from the chained functions is never updated at each iteration. Previously, I tried to solve this with a foreach loop and spread but the results were the same.
How should I correct this so that localEncounter is updated at each iteration, and ultimately localEncounters contains correct data when returned? Thank you.
var localEncounters = [];
var localEncounter = {};
Promise.all(ids.map(function(id) {
return localEncounter, getEncounter(id, client)
.then(function (encounter) {
encounterObject = encounter;
//set the fields for the return object
localEncounter['encounterid'] = encounterObject[f_id];
localEncounter['screeningid'] = encounterObject[f_screening_id];
localEncounter['assessmentid'] = encounterObject[f_clinical_assessment_id];
localEncounter['psychevalid'] = encounterObject[f_psych_eval_id];
//get screening
return getScreening(encounterObject[f_screening_id], client);
})
.then(function (screening) {
//set the fields for the return object
localEncounter['screeningbegintime'] = screening[f_begin_time];
//get assessment
return getAssessment(localEncounter['assessmentid'], client);
})
.then(function (assessment) {
//set the fields for the return object
localEncounter['assessmentbegintime'] = assessment[f_begin_time];
//get psycheval
//localEncounters.push(assessment);
return getPsychEval(localEncounter['psychevalid'], client);
})
.then(function (psychEval) {
//set the fields for the return object
localEncounter['assessmentbegintime'] = psychEval[f_begin_time];
localEncounters.push(localEncounter);
}
, function (reason) {
console.log(reason); // display reason why the call failed;
reject(reason, 'Something went wrong creating the encounter!');
})
})).then(function(results) {
// results is an array of names
console.log('done ');
resolve(localEncounters);
})
Solution: I only needed to move the declaration of localEncounter inside the map iterator
before:
var localEncounter = {};
Promise.all(ids.map(function(id) {
after:
Promise.all(ids.map(function(id) {
var localEncounter = {};
This now allows that each id iteration gets its own localEncounter object.
I have a viewmodel which consists of a list(foreach loop) of DoctorPrices and when clicking on an item in the list it open up a CRUD form on the side. However when i update the values on the CRUD the observableArray that is bound to the foreach is not refreshing? (although the values are updates in the DB correctly)
From my data access module i call the following query.
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
return manager.executeQueryLocally(query);
}
In my viewmodel i have the following code:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
services is bound using a foreach loop (not posting here as the code is simple and works)
When i click on a one of the DoctorPrices it gets the data as follows and places it in an observable:
this.selectedPrice = function (data, event) {
self.currentService(data);
self.showEdit(true);
};
I then bind selectPrice to a simple form that has the properties on it to be modified by the user. I then call manager.SaveChanges().
This results in the following problem: the value is being updated correctly but the GUI / Original List that is bound in the foreach is not being updated? Are the properties in breeze not observables? What is the best way to work with something like this.
I thought of a workaround and changing the code with something like this:
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
});
But i feel that clearing the array in that way is sloppy and not the right way of doing things specially with long lists.
Can someone please point me in the right direction on how to bind observableArray properties properly so they are updated?
Additional code my VM Component:
function services() {
var self = this;
this.showForm = ko.observable(false);
this.currentService = ko.observable();
this.services = ko.observableArray(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
this.title = ko.observable();
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
self.showDetails(false);
});
this.show = function (value) {
self.showForm(value);
};
this.showDetails = ko.observable(false);
this.addNewService = function() {
self.currentService(doctorServices.createService(doctorList.viewModel.instance.currentDoctorID()));
console.log(self.currentService().entityAspect.entityState);
self.showDetails(true);
};
this.showDelete = ko.computed(function() {
if (self.currentService() == null)
return false;
else if (self.currentService().entityAspect.entityState.isDetached()) {
self.title('Add new service');
return false;
} else {
self.title('Edit service');
return true;
}
});
this.deleteService = function() {
self.currentService().entityAspect.setDeleted();
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
};
this.closeDetails = function () {
doctorServices.manager.rejectChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.showDetails(false);
};
this.selectService = function (data, event) {
self.currentService(data);
self.showDetails(true);
};
this.saveChanges = function () {
console.log(self.currentService().entityAspect.entityState);
if (self.currentService().entityAspect.entityState.isDetached()) {
doctorServices.attachEntity(self.currentService());
}
console.log(self.currentService().entityAspect.entityState);
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.currentService.notifySubscribers();
self.showDetails(true);
};
}
return {
viewModel: {
instance: new services()
},
template: servicesTemplate,
};
Below is my Breeze Data Class:
define('data/doctorServices', ['jquery', 'data/dataManager', 'knockout','mod/medappBase', 'breeze', 'breeze.savequeuing'], function ($, manager, ko,base, breeze, savequeuing) {
var services = ko.observableArray([]);
return {
attachEntity:attachEntity,
getServices: getServices,
services: services,
manager:manager,
getDoctorServices: getDoctorServices,
getServiceById: getServiceById,
createService:createService,
hasChanges: hasChanges,
saveChanges: saveChanges
};
function getServices() {
var query = breeze.EntityQuery.from("DoctorPrices");
return manager.executeQuery(query).then(function (data) {
services(data.results);
}).fail(function (data) {
console.log('fetch failed...');
console.log(data);
});;
}
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
var set = manager.executeQueryLocally(query);
return set;
}
function getServiceById(serviceId) {
return manager.createEntity('DoctorPrice', serviceId);
//return manager.getEntityByKey('DoctorPrice', serviceId);
}
function handleSaveValidationError(error) {
var message = "Not saved due to validation error";
try { // fish out the first error
var firstErr = error.innerError.entityErrors[0];
message += ": " + firstErr.errorMessage;
base.addNotify('error', 'Could not save.', message);
} catch (e) { /* eat it for now */ }
return message;
}
function hasChanges() {
return manager.hasChanges();
}
function attachEntity(entity) {
manager.addEntity(entity);
}
function createService(doctorId) {
return manager.createEntity('DoctorPrice', { DoctorPricingID: breeze.core.getUuid(), DoctorID:doctorId }, breeze.EntityState.Detached);
};
function saveChanges() {
return manager.saveChanges()
.then(saveSucceeded)
.fail(saveFailed);
function saveSucceeded(saveResult) {
base.addNotify('success', 'Saved.', 'Your updates have been saved.');
}
function saveFailed(error) {
var reason = error.message;
var detail = error.detail;
if (error.innerError.entityErrors) {
reason = handleSaveValidationError(error);
} else if (detail && detail.ExceptionType &&
detail.ExceptionType.indexOf('OptimisticConcurrencyException') !== -1) {
// Concurrency error
reason =
"Another user, perhaps the server, " +
"may have deleted one or all of the settings." +
" You may have to restart the app.";
} else {
reason = "Failed to save changes: " + reason +
" You may have to restart the app.";
}
console.log(error);
console.log(reason);
}
}
});
Please note this is my frist attempt at both a data class and VM. At the moment i am relying heavily on clearing the array ([]) and using notifySubscribers to make the array refresh :(
I bet you're missing an observable somewhere. I can't tell because you keep hopping from property to property whose definition is not shown.
For example, I don't know how you defined this.currentService.
I'm confused by this:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
Why is it a ko.computed? Why not just make it an observable array.
self.service = ko.observableArray();
// ... later replace the inner array in one step ...
self.service(doctorServices.getDoctorServices(
doctorList.viewModel.instance.currentDoctorID()));
I urge you to follow the observability trail, confident that your Breeze entity properties are indeed observable.
vm.selectedPrice = ko.dependentObservable(function () {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
}, vm);
vm is ur model on which u applied bindings , try this it will work.
I am trying to do a simple CRUD app using Ember + Rails and I'm getting the following error when trying to go to the /workouts route.
Error while loading route: TypeError {} ember.js?body=1:415
Uncaught TypeError: Object function () {
if (!wasApplied) {
Class.proto(); // prepare prototype...
}
o_defineProperty(this, GUID_KEY, undefinedDescriptor);
o_defineProperty(this, '_super', undefinedDescriptor);
var m = meta(this), proto = m.proto;
m.proto = this;
if (initMixins) {
// capture locally so we can clear the closed over variable
var mixins = initMixins;
initMixins = null;
this.reopen.apply(this, mixins);
}
if (initProperties) {
// capture locally so we can clear the closed over variable
var props = initProperties;
initProperties = null;
var concatenatedProperties = this.concatenatedProperties;
for (var i = 0, l = props.length; i < l; i++) {
var properties = props[i];
Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin));
for (var keyName in properties) {
if (!properties.hasOwnProperty(keyName)) { continue; }
var value = properties[keyName],
IS_BINDING = Ember.IS_BINDING;
if (IS_BINDING.test(keyName)) {
var bindings = m.bindings;
if (!bindings) {
bindings = m.bindings = {};
} else if (!m.hasOwnProperty('bindings')) {
bindings = m.bindings = o_create(m.bindings);
}
bindings[keyName] = value;
}
var desc = m.descs[keyName];
Ember.assert("Ember.Object.create no longer supports defining computed properties.", !(value instanceof Ember.ComputedProperty));
Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
Ember.assert("`actions` must be provided at extend time, not at create time, when Ember.ActionHandler is used (i.e. views, controllers & routes).", !((keyName === 'actions') && Ember.ActionHandler.detect(this)));
if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) {
var baseValue = this[keyName];
if (baseValue) {
if ('function' === typeof baseValue.concat) {
value = baseValue.concat(value);
} else {
value = Ember.makeArray(baseValue).concat(value);
}
} else {
value = Ember.makeArray(value);
}
}
if (desc) {
desc.set(this, keyName, value);
} else {
if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
this.setUnknownProperty(keyName, value);
} else if (MANDATORY_SETTER) {
Ember.defineProperty(this, keyName, null, value); // setup mandatory setter
} else {
this[keyName] = value;
}
}
}
}
}
finishPartial(this, m);
this.init.apply(this, arguments);
m.proto = proto;
finishChains(this);
sendEvent(this, "init");
} has no method 'find'
My code is located here: https://github.com/ecl1pse/ember-workouts
What am I doing wrong?
Edit: Upon further investigation I believe the culprit is
EmberWorkouts.WorkoutsRoute = Ember.Route.extend(
model: -> EmberWorkouts.Workout.find()
This doesn't actually return anything. How do I debug from there?
If I replace that with this
EmberWorkouts.WorkoutsRoute = Ember.Route.extend
model: -> [{title: 'hi'}, {title: 'damn'}]
The view actually renders content.
How do I get the model to collect from Rails properly?
Ember Data's interface has changed a little with the current release:
You can clear out the store.js file entirely. Ember Data will automatically set up a data store for you using the REST Adapter (unless you tell it otherwise).
Use model: -> #store.find('workout') instead.
I tested this with your app and it works.
If you haven't read through the Ember Data Guide in the last week or two (it's changed a lot), I would spend a few minutes on it.
The fix for this error (as of ember-data 1.0.0.beta.6) for me was to make sure that the JSON returned from the server included an "id" field for each model, BUT not to explicitly declare the id when setting up the Ember DS.Model.
jbuilder template:
json.scans do
json.array! #scans do |scan|
json.id scan.id # This prop has to be there
json.name scan.name
end
end
Ember model:
EmberApp.Scan = DS.Model.extend(
// Don't include the id prop here
name: DS.attr("string")
)
I always have this issue in ember apps that are built on a rails backend. I have a groups.hbs template which lists out a bunch of groups, when you click on a group it loads the group.hbs template next to the groups template and changes the url to /groups/:group_id.
However, when I click the back and forward buttons, or try to manually load a url with a specific :group_id the group template fails to render and the console throws a giant
Uncaught TypeError: Object function () {
...
error.
group.js.coffee
App.Group = Ember.Object.extend()
App.Group.reopenClass
all: ->
App.ajax(
url: App.apiUrl('/groups')
).then (data) ->
console.log data
groups = []
for group in data.response
groups.addObject(App.Group.create(group))
console.log(groups)
groups
router.js.coffee
Mdm.Router.map ->
#resource 'groups', ->
#resource 'group', {path: "/:group_id"}
Mdm.Router.reopen
location: 'history'
I've never experienced this issue when building standalone ember apps. Any idea what would cause this?
EDIT
I should add that I am pulling my data from an api via XHR requests.
EDIT 2
I just explicitly created the GroupRoute and had it load all of the groups, this code is identical to the GroupsRoute. The template is still not rendering, but I no longer get that error.
GroupRoute
App.GroupRoute = Ember.Route.extend(model: ->
App.Group.all()
)
And GroupsRoute:
App.GroupsRoute = Ember.Route.extend(model: ->
App.Group.all()
)
EDIT 3
Here's the whole error if it helps anyone.
Uncaught TypeError: Object function () {
if (!wasApplied) {
Class.proto(); // prepare prototype...
}
o_defineProperty(this, GUID_KEY, undefinedDescriptor);
o_defineProperty(this, '_super', undefinedDescriptor);
var m = meta(this);
m.proto = this;
if (initMixins) {
// capture locally so we can clear the closed over variable
var mixins = initMixins;
initMixins = null;
this.reopen.apply(this, mixins);
}
if (initProperties) {
// capture locally so we can clear the closed over variable
var props = initProperties;
initProperties = null;
var concatenatedProperties = this.concatenatedProperties;
for (var i = 0, l = props.length; i < l; i++) {
var properties = props[i];
Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin));
for (var keyName in properties) {
if (!properties.hasOwnProperty(keyName)) { continue; }
var value = properties[keyName],
IS_BINDING = Ember.IS_BINDING;
if (IS_BINDING.test(keyName)) {
var bindings = m.bindings;
if (!bindings) {
bindings = m.bindings = {};
} else if (!m.hasOwnProperty('bindings')) {
bindings = m.bindings = o_create(m.bindings);
}
bindings[keyName] = value;
}
var desc = m.descs[keyName];
Ember.assert("Ember.Object.create no longer supports defining computed properties.", !(value instanceof Ember.ComputedProperty));
Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) {
var baseValue = this[keyName];
if (baseValue) {
if ('function' === typeof baseValue.concat) {
value = baseValue.concat(value);
} else {
value = Ember.makeArray(baseValue).concat(value);
}
} else {
value = Ember.makeArray(value);
}
}
if (desc) {
desc.set(this, keyName, value);
} else {
if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
this.setUnknownProperty(keyName, value);
} else if (MANDATORY_SETTER) {
Ember.defineProperty(this, keyName, null, value); // setup mandatory setter
} else {
this[keyName] = value;
}
}
}
}
}
finishPartial(this, m);
delete m.proto;
finishChains(this);
this.init.apply(this, arguments);
} has no method 'find'
EDIT
So I think I figured out the problem, when you click the back button or enter a manual url it wasn't finding the obkect based on id. So I added a find() method to the Group model. Not it looks like this:
Mdm.Group = Ember.Object.extend()
Mdm.Group.reopenClass
all: ->
Mdm.ajax(
url: Mdm.apiUrl('/groups')
).then (data) ->
console.log data
groups = []
for group in data.response
groups.addObject(Mdm.Group.create(group))
console.log(groups)
groups
find: (group_id) ->
Mdm.ajax(
url: Mdm.apiUrl("/groups/#{group_id}")
).then (data) ->
renderTemplate: (data)
And my GroupRoute looks like this:
Mdm.GroupRoute = Ember.Route.extend
model: (params) ->
console.log 'oh hai'
Mdm.Group.find(params.group_id)
Now in the console when I click the back button it is loading the data but its not associating the group template with the group_id. What is the best practice way to tell ember to do this?
I'm not a rails developer but try doing something like this for the simple model/route setup you show above
App.Group = Ember.Object.extend().reopenClass
groups: []
find: ->
$.ajax
url: "/api/groups/"
type: "GET"
cache: false
dataType: "json"
beforeSend: =>
#groups.clear()
success: (results) =>
[#groups.addObject(App.Group.create(result)) for result in results]
error: =>
alert "error: failed to load the available groups"
#groups
App.Router.map ->
#resource "groups", path: "/", ->
#route "group", path: "/:group_id"