jaydata "automatic attach of related entities" not working? - javascript

According to http://jaydata.org/blog/release-notes, below "JayData 1.3.1 Interoperability Edition", there is an item titled "Auto-attach of included child objects"
In my code, i tried this:
smarterpjs.localdb.Clientes
.include('CondicionVenta')
.include('ListaPrecios')
.include('Vendedor')
.include('Localidad')
.single(function(c) {
return c.Oid === this.ID
}, {
ID: e.model.Oid
}, function(c) {
var item = smarterpjs.crearOrdenVenta();
smarterpjs.localdb.Clientes.attach(c);
item.Cliente(c);
...
});
Problem is that c.CondicionVenta has no context set after the call to attach.
If i try latter to save that "item", it tries to save a new "CondicionVenta", a new "ListaPrecios", a new "Vendedor", a new "Localidad" too, which are wrong.
So, my question is: is that Auto-attach of included child objects" working or not? I'm using v1.3.2

Related

How to add a new Inspector(apart from the Inspector of elements and links) in jointJS - Rappid

I want to add a 3rd Inspector which will open only for an element(not a link) of specific type, for example only for basic.Rect in Rappid.
So far, there are 2 Inspectors.For elements and for links.
Is there any way it can be done?
The following code is a part of the KitchenSkink version of Rappid.
Here is function createInspector:
createInspector: function(cellView) {
var cell = cellView.model || cellView;
// No need to re-render inspector if the cellView didn't change.
if (!this.inspector || this.inspector.options.cell !== cell) {
// Is there an inspector that has not been removed yet.
// Note that an inspector can be also removed when the underlying cell is removed.
if (this.inspector && this.inspector.el.parentNode) {
this.inspectorClosedGroups[this.inspector.options.cell.id] = _.map(app.inspector.$('.group.closed'), function(g) {
return $(g).attr('data-name');
});
// Clean up the old inspector if there was one.
this.inspector.updateCell();
this.inspector.remove();
}
var inspectorDefs = InspectorDefs[cell.get('type')];
this.inspector = new joint.ui.Inspector({
inputs: inspectorDefs ? inspectorDefs.inputs : CommonInspectorInputs,
groups: inspectorDefs ? inspectorDefs.groups : CommonInspectorGroups,
cell: cell
});
this.initializeInspectorTooltips();
this.inspector.render();
$('.inspector-container').html(this.inspector.el);
if (this.inspectorClosedGroups[cell.id]) {
_.each(this.inspectorClosedGroups[cell.id], this.inspector.closeGroup, this.inspector);
} else {
this.inspector.$('.group:not(:first-child)').addClass('closed');
}
}
}
If you use joint.ui.Inspector.create('#path', inspectorProperties) any previous instance of the Inspector in a specific DOM element is removed and new one is created and rendered into the DOM automatically (it avoids creating a new instance of joint.ui.Inspector(), rendering it, adding the rendered result manually and removing the previous instance).
It also keeps track on open/closed groups and restore them based on the last used state.
Besides this, you may always have several different inspectorProperties objects previously defined when you are about to create() the inspector. So following the code you pasted, you could perform the tests you need first and then create the appropriate inspector:
if(cell instanceof joint.basic.Rect){
var customInputs = _.clone(CommonInspectorInputs);
// extend more inputs into `customInputs` from a variable previously defined
// OR modify the default rectangle's inspector directly, example:
customInputs.attrs.text = {
type: 'textarea',
label: 'Multiline text',
text: 'Type\nhere!',
group: joint.util.getByPath(CommonInspectorInputs.attrs, 'text/group', '/');
};
joint.ui.Inspector.create('.extra-inspector-container', {
cell: cell
inputs: customInputs,
groups: CommonInspectorGroups,
});
} // if only ONE inspector needs to be loaded add an ELSE block here
// and use '.inspector-container' in the `create()` above
// If `InspectorDefs` is a global variable with all the cells inspectors properties
// create and load the default inspector
joint.ui.Inspector.create('.inspector-container', _.extend({cell: cell},
InspectorDefs[cell.get('type')])
);

Set Kendo UI Window values globally

I'm working with a lot of Kendo UI windows. Is there some way to specify default values somehow globally? Or maybe a more realistic version, can I create some parent with predefined values and then just overwrite the values I need to change?
For example, I want the same error behavior and a modal parameter for all of the windows, so I would like to do something like:
$("#parentWindow").kendoWindow({
modal: true,
error: function () {
this.close();
new Notification().error();
}
});
And then use the parent window as a base for new windows:
$("#newWindow").kendoWindow({
title: "This window should have the options (modal and error) of the parentWindow",
}).??getTheRestOfTheValuesFromParent()??;
Or rewrite some parameter:
$("#newWindow2").kendoWindow({
modal: false,
title: "A window with overwritten modal parameter",
}).??getTheRestOfTheValuesFromParent()??;
Is it somehow possible to achieve this, is there any possibility of something like C# inheritance?
Maybe it's a stupid question, but I'm not so familiar with JS.
I highly encourage you to create your own wrapper code over all - or at least those more complex - kendo widgets. My team has been doing it for years in a project we use kendo for everything and we are having very positivelly results. The main purpose is what you need: a global behaviour. If after thousand windows coded over your project, you need to change them all, just change the wrapper. It's just a simple jQuery function:
$.fn.MyWindow = function(options) {
var $target = $(this);
var widget = {
_defaultOptions: {
actions: ["Minimize", "Maximize", "Close"],
visible: false,
width: 400,
height: 400,
modal: true
},
_options: options || {},
_target: $target,
_widget: null,
_init: function() {
this._manageOptions();
this._createWidget();
return this;
},
_manageOptions: function() {
// Here you can perform some validations like displaying an error when a parameter is missing or whatever
this._options = $.extend(this._options, this._defaultOptions);
},
_createWidget: function() {
this._widget = this._target.kendoWindow(this._options).data("kendoWindow");
// Create here some behaviours that the widget doesn't haves, like closing the window when user click the black overlay
if (this._options.closeOnOverlayClick) {
$('body').off('click', '.k-overlay').on('click', '.k-overlay', function() {
this._widget.close();
}.bind(this));
}
},
Show: function(center) {
if (center) {
this._widget.center();
}
this._widget.open();
}
};
return widget._init();
};
var wnd = $("#wnd").MyWindow({
title: "My first window",
closeOnOverlayClick: true // Your own parameter
});
// Now you work with your own functions:
wnd.Show(true);
Demo.
There are so many customizations, like your own events - some of those kendo's widgets doesn't haves - etc..
I will just add that there is an article(here) about creating custom Kendo widgets where you can find more information about the specifics of different scenarios that may be implemented.
Ι had a case like yours with kendo windows, kendo grids and kendo dropdownlists. For that I created HtmlHelpers for all my elements and called them when I needed to. Since you are using kendo asp.net-mvc I would recommend to look at this way.
public static WindowBuilder GlobalKendoWindow(this HtmlHelper helper)
{
return helper.Kendo().Window()
.Draggable()
.Animation(true)
.Visible(false)
.AutoFocus(true)
.Modal(true)
.Scrollable(true)
.HtmlAttributes(new { #class = "atn-modal-container" })
.Actions(actions => actions.Minimize().Close())
.Deferred();
}
and render it in my Html like this
#(Html.GlobalKendoWindow()
.Name("addCandidateDialog")
.Title(Html.GetResource(cps, "AddCandidateDialogTitle"))
.LoadContentFrom("AddCandidate", "Candidate")
.Events(events => events.Open("athena.addCandidacy.onAddCandidateOpen").Close("athena.addCandidacy.onAddCandidateClose"))
)

AppGyver Steroids Supersonic Views

I'm trying to get my head around switching views / passing views to another view.
I have an app that is calling in a kimono API, that's all setup with the supersonic background and looks fine. I have 1 string and 2 objects in the API. I have a page that is calling in the full list of events using a page called event:
{{ event.eventdescription }}
The Event#Index controller is:
angular
.module('event')
.controller("IndexController", function ($scope, Event, supersonic) {
$scope.events = null;
$scope.showSpinner = true;
Event.all().whenChanged( function (events) {
$scope.$apply( function () {
$scope.events = events;
$scope.showSpinner = false;
});
});
});
And all of that displays properly. The issue is when I click on one of those items shown which should go to the specific event I get nothing. And I'm sure I'm doing this wrong or don't understand enough about switching views. I've read many examples, but I'm not getting how it all goes together.
here is my event#show page. Very generic just trying to load any information at this point.
<div ng-controller="ShowController">
<super-navbar>
<super-navbar-title>
Show
</super-navbar-title>
</super-navbar>
<div class="padding">
{{ event.eventdescription }}
</div>
</div>
And the showcontroller:
angular
.module('event')
.controller("ShowController", function ($scope, Event, supersonic) {
$scope.events = null;
Event.all().whenChanged( function (events) {
$scope.$apply( function () {
});
});
});
And this always returns a blank page. When i check the log it says Undefined.undefined which i'm not sure what that means.
Any insight on this is greatly appreciated. In the appgyver docs I saw something called.
var view = new supersonic.ui.View("bananas#show");
supersonic.ui.layers.push(view);
But I'm not sure how to use this?
ANY insight is appreciated.
So, UPDATED I have:
here's the event#index i'm working with.
<div ng-controller="IndexController">
<super-navbar>
<super-navbar-title>
Event Index
</super-navbar-title>
</super-navbar>
<ul class="list" ng-hide="events.length == 0">
<super-navigate view-id="event#show" data-params-id="{{event.id}}" ng-repeat="event in events">
<li class="item item-icon-right">
<h2 ng-bind="event.EventTitles['text']"></h2>
<img ng-src="{{ event.HeadlineImages.src }}" width="100px" height="100px">
<p> {{ event.eventdescription }} </p>
<i class="icon super-ios7-arrow-right"></i>
</li>
</super-navigate>
</ul>
</div>
And the Index Controller
angular
.module('event')
.controller("IndexController", function ($scope, Event, supersonic) {
$scope.events = null;
Event.all().whenChanged( function (events) {
$scope.$apply( function () {
$scope.events = events;
});
});
});
The show html page.
<div ng-controller="ShowController">
<super-navbar>
<super-navbar-title>
Show
</super-navbar-title>
</super-navbar>
<div class="padding">
<p>
{{event.eventdescription}}
</p>
</div>
</div>
The ShowController
angular
.module('event')
.controller("ShowController", function ($scope, Event, supersonic) {
supersonic.ui.views.current.params.onValue( function (Event) {
$scope.events = event.id;
});
Event.find($scope.events).then( function (Event) {
$scope.$apply( function () {
$scope.event = Event;
});
});
});
And I also updated the structure.coffee as so
rootView:
location: "event#index"
preloads: [
{
id: "event#show"
}
{
id: "using-the-scanner"
location: "example#using-the-scanner"
}
]
Any help is appreciated.
It doesn't look like the data is being set in the your ShowController. I commented about this before. I think you need to pass the id of the event using <super-navigate> with a location property and a data-params-id or whatever you want the parameter name to be. Then in your ShowController you can access it with:
supersonic.ui.views.current.params.onValue( function (values) {
// values.nameOfPropertyPassedInCouldBeEventId
$scope.id = values.id;
});
Then you might be able to do something like this to access the Event by id:
Event.find($scope.id).then( function (theEvent) {
$scope.$apply( function () {
$scope.event = theEvent;
});
});
Now in your view where you have {{ event.eventdescription }} there should be some data.
And another piece for when the view is visible meaning every time you see that view page this will fire:
supersonic.ui.views.current.whenVisible( function () {
// your code for watching events
});
Ok, after a couple weeks of trying to get this working and although, I still haven't been able to get this to work yet.. I think I'm getting somewhere with this FINALLY... It seems the biggest problem here is using Kimono and AppGyver. The JSON file has been updated in Kimono using:
function transform(data) {
data.results.collection1 = data.results.collection1.map(function(o) {
o.eventdescription = {
text: o.eventdescription
}
return o;
});
return data;
}
This cleans up the JSON file exported/ coming in as API to App Gyver so that all parts are objects. ( I know, maybe not a big deal, but I just wanted to make this as clean as possible). To give you an idea of the before and after of using this script in the Kimono Modify Results box -->
BEFORE:
"EventTitles": {
"href": "http://",
"src": "http://.jpg",
"text": "Lorem Ipsum"
},
"HeadlineImages": {
"href": "http://",
"src": "http://.jpg",
"text": "Lorem Ipsum"
},
"eventdescription":"Lorem Ipsum"
},
which leaves eventdescription as a string rather than object and then the AFTER:
"EventTitles": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"HeadlineImages": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"eventdescription": {
"text": "TEXT"
},
So, after running this into Kimono as you can see all entries are "objects". And you'd use &kimmodify=1 AFTER the apikey in the link thusly:
https://www.kimonolabs.com/api/{indentifier}{apikey}&kimmodify=1
NEXT, as I was explained to by the AppGyver community one would pretty much need an "id" of sorts for each item in the JSON / API that's being created to be able to use the ShowController to create a reasonable/ feasible url string on the show.html.
Which should create something like /app/tier/showid=123456789 when going from the index to a specific entry view.
(You find the URLs by using the debug mode in AppGyver either via Safari Web Inspector on Mac with the IOS Emulator. or a browser using http://localhost:[some port number]/location/of/app when using the Android Emulator (the recommended Genymotion).
So, to do this, in Kimono use the API Hash addition &kimhash=1 to the end of your url AFTER the APIKEY but BEFORE the modify such as this:
https://www.kimonolabs.com/api/{indentifier}{apikey}&kimhash=1&kimmodify=1
. See: Kimono API Docs- Re:Hash.
This creates something like
"EventTitles": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"HeadlineImages": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"eventdescription": {
"text": "TEXT"
},
"hash":"1a2b3c4d5e6f7g8h9z"},
a random 'indentifier' is created for each entry.
Now, that's where I'm stuck now. ...because the API URL needing to come in is:
https://www.kimonolabs.com/api/{indentifier}{apikey}&kimhash=1&kimmodify=1
and when you go to configure your API on the backend there is no area I see to enter this new &kimhash=1&kimmodify=1 that needs to be at the end of the URL to call in the correctly formatted and id'd API and as far as I can see there is no reference for doing this.
http://docs.appgyver.com/supersonic/guides/data/other-data-providers/kimono-labs/
I feel like this is the next to last step in figuring this all out and finally being able to get this up and working. The last being to obviously revisit pulling in the id to the ShowController which I'm feeling somewhat confident about if I can somehow figure out this last part.
Any ideas??

TreeNode select() does not call select listener

I use the following code to get a sap.ui.commons.TreeNode and to select it.
var newNode = this.tree.getNodes()[typeIdx].getNodes()[typeArray.length - 1];
newNode.select();
Unfortunately though, nothing happens. While newNode.getIsSelected() returns true, no handlers are executed (neither select on the tree nor selected on the node).
P.S. I made sure newNode.getSelectable() is true.
Did anyone use TreeNode's select() method sucessfully?
Code example
Here is an example
Adding an element does highlight the element but the alert is only shown when clicking with the mouse.
Here's an example of how this works.
Here's the function that we want to call on the node selection:
var sel = function(oEvent) {
console.log(oEvent.getSource().getText() + " selected");
};
And here's the tree with some nodes, nodes 1.1 and 1.2 have the handler:
new sap.ui.commons.Tree("tree", {
nodes: [
new sap.ui.commons.TreeNode({
text: "1",
nodes: [
new sap.ui.commons.TreeNode({
text: "1.1",
selected: sel
}),
new sap.ui.commons.TreeNode({
text: "1.2",
selected: sel
})
]
}),
new sap.ui.commons.TreeNode({
text: "2"
})
]
}).placeAt("content");
And when we do this (based on your example):
newNode = sap.ui.getCore().byId("tree").getNodes()[0].getNodes()[0]
newNode.select()
we get
1.1 selected
in the console, and the the node is highlighted.
Can you try using newNode.setIsSelected(true); That works for me
Best,
Robin
Having read the exact requirement (the event onSelect not being fired), I think there were two things missing:
the fireSelected event was not explicitly fired
the select event was set on the Tree element rather than the TreeNode template
I have updated your example to a new version: http://jsbin.com/hososexu/7/edit

Learning Ember.js- Persistence In Many To Many Relationships

To learn Ember.js I started writing a small bookmark application. I'm struggling with issues related to wrong data handling now.
To Explain The Application
User can add label
User can add links to selected labels
A Label can have n links
A Link can have n labels
Show links by selecting the associated labels
The Issue
Link data is not writtern to the data store. Because of this, local updates to the links model due selection changes are overwritten. Also, implementing true persistence later wouldn't work.
Narrowing down the issue
In IndexRoute I initialize the model:
model: function(params) {
return {
labels: this.get("store").find("label"),
// TODO: this is probably wrong.
links: Ember.A()
};
}
On the one side I fetch label data from the data store. On the other side I initialize link data with an empty ember array.
In my opinion this is the root of the malfunction but I don't know how to implement this properly.
I tried to replace this with bogus references to the storage adapter:
this.get("store").find("label").filter("_")
This is neither right nor does it work properly. Then it continues to the point where I then can't use the storage adapter to update records:
// TODO: this is probably wrong.
this.get("links").addObject({
name: linkName,
url: linkUrl,
labels: this.selectedLabels
});
/*store.push("link", {
name: newLink,
url: linkUrl,
labels: this.selectedLabels
});*/
And so on.
Jsbin: http://jsbin.com/ucanam/1751/edit
How to store link data properly so changing the local data of the controler won't break the application?
Edit:
I think I found my conceptual mistake with your advice. Does that mean that I always have to handle the local copy aswell?
var link = this.get("model");
link.deleteRecord();
link.save().then(function(link) {
var indexController = this.get('controllers.index');
indexController.get("links").removeObject(link);
});
In your example you used a promise to add the object to the controller. In this code sample the promise will never fulfill- therefore it only works without.
Also in the LabelController the remove method should also deletes associated links. If I use deleteRecord in the forEach loop it only deletes one label and then it somehow brings the loop to exit. Is this intentionally or have I made a mistake?
I have updated your JsBin.
http://jsbin.com/ucanam/1987/edit
I modified your JSbin http://jsbin.com/ucanam/1975/
If you want to persist records you must create them in the store with createRecord() and then save() them. The new newLink function
newLink: function() {
var store = this.get("store"),
linkName = this.get("linkName"),
linkUrl = this.get("linkUrl"),
links = this.get("links"),
selectedLabels = this.get('selectedLabels');
if(selectedLabels.get('length') > 0) {
if(linkName.length > 0 && linkUrl.length > 0) {
var newLink = store.createRecord('link',
{
name: linkName,
url: linkUrl
});
selectedLabels.forEach(function(label){
newLink.get('labels').addObject(label);
});
newLink.save().then(function(link){
links.addObject(link);
});
this.set("linkName", "");
this.set("linkUrl", "");
}
} else {
alert("You have to select a label!");
}
},
For deleting records there are problems using forEach because the result of a find to the store is a live array. You can see this discussion in GitHub https://github.com/emberjs/data/issues/772.
So your remove label function should be (note the use of toArray() to make a static copy of the live array)
remove: function() {
var indexController = this.get('controllers.index'),
label = this.get('model'),
linksPromise = this.get('store').find('link');
linksPromise.then(function(links){
links.toArray().forEach(function(link){
var linkLabels = link.get('labels'),
linkLabelsIds = linkLabels.mapProperty('id');
if(linkLabelsIds.contains(label.get("id"))) {
if(linkLabelsIds.get("length") == 1) {
console.log("delete link: "+link.get("name"));
indexController.get("links").removeObject(link);
link.deleteRecord();
link.save();
}
}
});
label.deleteRecord();
label.save();
});
}
Final note, don't forget to make a save() on the records after deleting them, jsBin here: http://jsbin.com/ucanam/1989/
Have you defined your models?
App.Label = DS.Model.extend({
title: DS.attr('string'),
links: DS.hasMany('link')
});
App.Link = DS.Model.extend({
name: DS.attr('string'),
url: DS.attr('string'),
labels: DS.hasMany('label')
});
App.IndexRoute = Ember.Route.extend({
model: function() {
this.store.find('label')
}
});
App.LabelController = Ember.ObjectController.extend({
actions:
<!-- functions here -->
});
<script type='text/handlebars' data-template-name="index">
{{#each label in model itemController='label'}}
{{label.title}}
{{#each link in label.links}}
{{link.title}}
{{/each}}
{{/each}}
</script>

Categories

Resources