This code does not work:
var mappingOption = {
key: function (data) {
return ko.utils.unwrapObservable(data.id);
}
ko.mapping.fromJS(serverItems, mappingOption, viewModel.items);
I want to UNION of both items (viewModel.items and serverItems)
var ClassA = new function(data) {
ko.mapping.fromJS(data, {
key: function (item) {
return ko.utils.unwrapObservable(item.id);
},
'items': {
create: function (itemData) {
return new ClassB(itemData.data);
}
}
}, self);
}
var ClassB = new function(data) {
ko.mapping.fromJS(data, {
key: function(item) {
return ko.utils.unwrapObservable(item.id);
};
), self);
}
So ClassA has a collection of ClassB. On creation, it calls the mapping function, which has a create call back on a collection of items (of type ClassB) which calls the create class B constructor, passing in the relevant data.
ClassB then takes the data from the constructor, and merges it into itself.
I'm pretty sure this will work due to the key mapping of ClassB. But haven't got the time to test it ;-)
Related
My Main function
import AppLauncher from './Applauncher'
function Mainfunc() {
global.app= AppLauncher()
global.app.start('index')
}
AppLauncher.js
function AppLauncher() {
function start(opts){
console.log('functions start called with' + opts)
}
}
export default AppLauncher
I want to assign the AppLauncher function as global, and call the start function nested inside it
Constructors are the way to go. You can do something like this:
// AppLauncher.js
function AppLauncher() {
// run some code...
// notice `this`
this.start = function(opts) {
console.log('start function called with', opts);
}
}
export default AppLauncher;
In your main function, call it with the new keyword:
import AppLauncher from './AppLauncher';
function Mainfunc() {
global.app = new AppLauncher();
global.app.start('index');
}
Constructors can also be written as classes (you can use it the same way as in my last example):
class AppLauncher {
constructor() {
// Anything in here gets executed when once you create an object from this class with the `new` keyword
}
// Instead of `this` we add a method to the class:
start(opts) {
console.log('start function called with', opts);
}
}
export default AppLauncher;
More about constructors: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor
If you don't want to use a constructor, you can also return an object:
// AppLauncher.js
function AppLauncher() {
// Some code here...
return {
start(opts) {
console.log("function start called with", opts);
}
};
}
export default AppLauncher;
And you can use this just like you thought:
import AppLauncher from `./AppLauncher`;
function Mainfunc() {
global.app = AppLauncher();
global.app.start('index');
}
As a side note, it's conventional to call constructors with PascalCase, while regular functions are called with camelCase.
I'm able to get a Core Data backed flat list working (with no .listStyle modifier) with delete and move functionality.
But when I tried to make list grouped
}.listStyle(GroupedListStyle())
the wheels fall off conceptually. The onDelete modifier parameter has a function signature of IndexSet? -> Void. So I can't pass in the object to be deleted.
onMove is essentially the same problem, except worse. Both modifiers rely on a data source assumed to be a flat array of sequential values which can be accessed by IndexSet subscription. But I can't think how to build a grouped list using a flat datasource.
My view body looks like this:
//I'm building the list using two independent arrays. This makes onDelete impossible to implement as recommended
ForEach(folders, id: \.self) { folder in
Section(header: Text(folder.title) ) {
ForEach(self.allProjects.filter{$0.folder == folder}, id: \.self){ project in
Text(project.title)
//this modifier is where the confusion starts:
}.onDelete(perform: self.delete)
}
}
}.listStyle(GroupedListStyle())
func delete (at offsets: IndexSet) {
// ??.remove(atOffsets: offsets)
//Since I use two arrays to construct group list, I can't use generic remove at Offsets call. And I can't figure out a way to pass in the managed object.
}
func move (from source: IndexSet, to destination: Int) {
////same problem here. a grouped list has Dynamic Views produced by multiple arrays, instead of the single array the move function is looking for.
}
Can't you store the result of the filter and pass that on inside .onDelete to your custom delete method? Then delete would mean deleting the items inside the IndexSet. Is moving between sections possible? Or do you just mean inside each folder? If only inside each folder you can use the same trick, use the stored projects and implement move manually however you determine position in CoreData.
The general idea is the following:
import SwiftUI
class FoldersStore: ObservableObject {
#Published var folders: [MyFolder] = [
]
#Published var allProjects: [Project] = [
]
func delete(projects: [Project]) {
}
func move(projects: [Project], set: IndexSet, to: Int) {
}
}
struct MyFolder: Identifiable {
let id = UUID()
var title: String
}
struct Project: Identifiable {
let id = UUID()
var title: String
var folder: UUID
}
struct FoldersAndFilesView: View {
var body: some View {
FoldersAndFilesView_NeedsEnv().environmentObject(FoldersStore())
}
}
struct FoldersAndFilesView_NeedsEnv: View {
#EnvironmentObject var store: FoldersStore
var body: some View {
return ForEach(store.folders) { (folder: MyFolder) in
Section(header: Text(folder.title) ) {
FolderView(folder: folder)
}
}.listStyle(GroupedListStyle())
}
}
struct FolderView: View {
var folder: MyFolder
#EnvironmentObject var store: FoldersStore
func projects(for folder: MyFolder) -> [Project] {
return self.store.allProjects.filter{ project in project.folder == folder.id}
}
var body: some View {
let projects: [Project] = self.projects(for: folder)
return ForEach(projects) { (project: Project) in
Text(project.title)
}.onDelete {
self.store.delete(projects: $0.map{
return projects[$0]
})
}.onMove {
self.store.move(projects: projects, set: $0, to: $1)
}
}
}
You are correct that the key to doing what you want is getting one array of objects and grouping it appropriately. In your case, it's your Projects. You do not show your CoreData schema, but I would expect that you have a "Projects" entity and a "Folders" entity and a one-to-many relationship between them. Your goal is to create a CoreData query that creates that array of Projects and groups them by Folder. Then the real key is to use CoreData's NSFetchedResultsController to create the groups using the sectionNameKeyPath.
It's not practical for me to send you my entire project, so I will try to give you enough pieces of my working code to point you in the right direction. When I have a chance, I will add this concept into the sample program that I just published on GitHub. https://github.com/Whiffer/SwiftUI-Core-Data-Test
This is the essence of your List:
#ObservedObject var dataSource =
CoreDataDataSource<Project>(sortKey1: "folder.order",
sortKey2: "order",
sectionNameKeyPath: "folderName")
var body: some View {
List() {
ForEach(self.dataSource.sections, id: \.name) { section in
Section(header: Text(section.name.uppercased()))
{
ForEach(self.dataSource.objects(forSection: section)) { project in
ListCell(project: project)
}
}
}
}
.listStyle(GroupedListStyle())
}
Portions of CoreDataDataSource:
let frc = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: McDataModel.stack.context,
sectionNameKeyPath: sectionNameKeyPath,
cacheName: nil)
frc.delegate = self
public func performFetch() {
do {
try self.frc.performFetch()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
private var fetchedObjects: [T] {
return frc.fetchedObjects ?? []
}
public var sections: [NSFetchedResultsSectionInfo] {
self.performFetch()
return self.frc.sections!
}
public func objects(forSection: NSFetchedResultsSectionInfo) -> [T] {
return forSection.objects as! [T]
}
public func move(from source: IndexSet, to destination: Int) {
self.reorder(from: source, to: destination, within: self.fetchedObjects)
}
If you want to easily delete things from a sectioned (doesn't have to be grouped!) List, you need to take advantage of your nesting. Consider you have the following:
List {
ForEach(self.folders) { folder in
Section(header: folder.title) {
ForEach(folder.items) { project in
ProjectCell(project)
}
}
}
}
Now you want to set up .onDelete. So let's zoom in on the Section declaration:
Section(header: Text(...)) {
...
}
.onDelete { deletions in
// you have access to the current `Folder` at this level of nesting
// this is confirmed to work with singular deletion, not multi-select deletion
// I would hope that this actually gets called once per section that contains a deletion
// but that is _not_ confirmed
guard !deletions.isEmpty else { return }
self.delete(deletions, in: folder)
}
func delete(_ indexes: IndexSet, in folder: Folder) {
// you can now delete this bc you have your managed object type and indexes into the project structure
}
How can i extract the inner id? I know that it's possible to change the fat arrow function to standart function or instead of this.id to use obj.id.
But is there any other way to get the inner id while using the fat arrow function?
var obj = {
id: 1,
cool: () => {
console.log( this.id );
}
};
var id = 2;
obj.cool(); // 2
Arrow function does not create a new scope and uses parent constructor's scope. in this case parent is Windows constructor Function so this refers to window and there is no way except the ones you listed
var obj = {
id: 1,
cool() {
console.log( this.id );
}
};
I have a function that I need to pass to a class I have defined in nodeJs.
The use case scenario is I want to give the implementer of the class the control of what to do with the data received from createCall function. I don't mind if the method becomes a member function of the class. Any help would be appreciated.
//Function to pass. Defined by the person using the class in their project.
var someFunction = function(data){
console.log(data)
}
//And I have a class i.e. the library.
class A {
constructor(user, handler) {
this.user = user;
this.notificationHandler = handler;
}
createCall(){
var result = new Promise (function(resolve,reject) {
resolve(callApi());
});
//doesn't work. Keeps saying notificationHandler is not a function
result.then(function(resp) {
this.notificationHandler(resp);
}) ;
//I want to pass this resp back to the function I had passed in the
// constructor.
//How do I achieve this.
}
callApi(){ ...somecode... }
}
// The user creates an object of the class like this
var obj = new A("abc#gmail.com", someFunction);
obj.createCall(); // This call should execute the logic inside someFunction after the resp is received.
Arrow functions (if your Node version supports them) are convenient here:
class A {
constructor(user, handler) {
this.user = user;
this.notificationHandler = handler;
}
createCall() {
var result = new Promise(resolve => {
// we're fine here, `this` is the current A instance
resolve(this.callApi());
});
result.then(resp => {
this.notificationHandler(resp);
});
}
callApi() {
// Some code here...
}
}
Inside arrow functions, this refers to the context that defined such functions, in our case the current instance of A. The old school way (ECMA 5) would be:
createCall() {
// save current instance in a variable for further use
// inside callback functions
var self = this;
var result = new Promise(function(resolve) {
// here `this` is completely irrelevant;
// we need to use `self`
resolve(self.callApi());
});
result.then(function(resp) {
self.notificationHandler(resp);
});
}
Check here for details: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this
I am getting this error in my code
TypeError: account.on() is not a function
Where did i go wrong?
Code
var events = require('events');
function Account() {
this.balance = 0;
events.EventEmitter.call(this);
this.deposit = function(amount) {
this.balance += amount;
this.emit('balanceChanged');
};
this.withdraw = function(amount) {
this.balance -= amount;
this.emit('balanceChanged');
};
}
Account.prototype._proto_ = events.EventEmitter.prototype;
function displayBalance() {
console.log('Account balance : $%d', this.balance);
}
function checkOverdraw() {
if (this.balance < 0) {
console.log('Account overdrawn!!!');
}
}
function checkgoal(acc, goal) {
if (acc.balance > goal) {
console.log('Goal Achieved!!!');
}
}
var account = new Account();
account.on('balanceChanged', displayBalance);
account.on('balanceChanged', checkOverdraw);
account.on('balanceChanged', function() {
checkgoal(this, 1000);
});
account.deposit(220);
account.deposit(320);
account.deposit(600);
account.withdraw(1200);
Your example code is not idiomatic Node JS.
I'd strongly recommend you follow the recommended best practices when creating new inheritable objects, as in:
var util=require('util');
var EventEmitter = require('events').EventEmitter;
var Account = function(){
EventEmitter.call(this); // should be first
this.balance=0; // instance var
};
util.inherits(Account,EventEmitter);
Account.prototype.deposit = function(amount){
this.balance += amount;
this.emit('balanceChanged');
};
Account.prototype.withdraw = function(amount){
this.balance -= amount;
this.emit('balanceChanged');
};
var account = new Account();
var displayBalance = function(){
console.log("Account balance : $%d", this.balance);
};
account.on('balanceChanged',displayBalance);
account.deposit(200);
account.withdraw(40);
// ... etc. ....
Which, when run displays:
Account balance : $200
Account balance : $160
Best practices are there so that
your code can be expressed in a way that is easy for others to understand
you don't run into unexpected problems when you try to replicate functionality that is already defined, possibly complex and difficult to understand.
The reason that util.inherits exists is so you don't have to worry about how the prototype chain is constructed. By constructing it yourself, you will often run into the problem you experienced.
Also, since the current Node runtime (>6.0) also includes most of the ES6 spec, you can also (and really should) write your code as:
const util = require('util');
const EventEmitter = require('events').EventEmitter;
const Account = () => {
EventEmitter.call(this);
this.balance = 0;
};
util.inherits(Account,EventEmitter);
Account.prototype.deposit = (val) => {
this.balance += val;
this.emit('balanceChanged');
};
Account.prototype.withdraw = (val) => {
this.balance -= val;
this.emit('balanceChanged');
};
The use of the const keyword assures the variables you create cannot be changed inadvertently or unexpectedly.
And the use of the "fat arrow" function definition idiom (() => {}) is more succinct and thus quicker to type, but also carries the added benefit that it preserves the value of this from the surrounding context so you never have to write something like:
Account.prototype.doSomething = function() {
var self = this;
doSomething(val, function(err,res){
if(err) {
throw err;
}
self.result=res;
});
};
which, using the 'fat arrow' construct becomes:
Account.prototype.doSomething = () => {
doSomething(val, (err,res) => {
if(err) {
throw err;
}
this.result=res; // where 'this' is the instance of Account
});
};
The "fat arrow" idiom also allows you to do some things more succinctly like:
// return the result of a single operation
const add = (a,b) => a + b;
// return a single result object
const getSum = (a,b) => {{a:a,b:b,sum:a+b}};
Another way to create inheritable "classes" in ES6 is to use its class construction notation:
const EventEmitter = require('events');
class Account extends EventEmitter {
constructor() {
super();
this._balance = 0; // start instance vars with an underscore
}
get balance() { // and add a getter
return this._balance;
}
deposit(amount) {
this._balance += amount;
this.emit('balanceChanged');
}
withdraw(amount) {
this._balance -= amount;
this.emit('balanceChanged');
}
}
It should be noted that both ways of constructing inheritable prototypal objects is really the same, except that the new class construction idiom adds syntactic "sugar" to bring the declaration more in-line with other languages that support more classical object orientation.
The ES6 extensions to node offer many other benefits worthy of study.