I'm using Telerik UI Pro components for {N} (without support) and having some problems with the DataForm.
When I pass a object to the page context, all fields using Picker editor can't select the right value.
I'll show the code I wrote:
teste-model.js
var Value = (function() {
function Value(text, value) {
this.text = text;
this.value = value;
}
return Value;
})();
var ValueModel = (function() {
function ValueModel() {}
Object.defineProperty(ValueModel.prototype, "model", {
get: function () {
if (!this._model) {
this._model = new Value("This is a text", "VALUE 1");
}
return this._model;
},
enumerable: true,
configurable: true
});
return ValueModel;
})();
exports.Value = Value;
exports.ValueModel = ValueModel;
teste.js
var ValueModel = require("./teste-model").ValueModel;
var page;
exports.onPageLoaded = function(args) {
console.log("Carregando página...");
page = args.object;
page.bindingContext = new ValueModel();
console.log(JSON.stringify(page.bindingContext));
}
teste.xml
<Page loaded="onPageLoaded"
xmlns:df="nativescript-telerik-ui-pro/dataform">
<StackLayout>
<df:RadDataForm id="myDataForm" source="{{ model }}">
<df:RadDataForm.properties>
<df:EntityProperty name="text" displayName="Text" index="0" />
<df:EntityProperty name="value" displayName="Value" index="1" valuesProvider="VALUE 0, VALUE 1, VALUE 2">
<df:EntityProperty.editor>
<df:PropertyEditor type="Picker" />
</df:EntityProperty.editor>
</df:EntityProperty>
</df:RadDataForm.properties>
</df:RadDataForm>
</StackLayout>
</Page>
The field value should show "VALUE 1" but shows "VALUE 0":
Any tips to solve this?
Update
I've made the changes Vladimir recommended but the picker property still not reflecting the object changes.
I also added a button to the page, to fill the dataform with random values.
The text property listen to the change normally but the picker property does not.
If I choose a picker value and click the button, the property is resetting to the first provider value.
The actual code is:
teste.xml
<Page loaded="onPageLoaded"
xmlns:df="nativescript-telerik-ui-pro/dataform">
<StackLayout>
<df:RadDataForm id="myDataForm" source="{{ model }}">
<df:RadDataForm.properties>
<df:EntityProperty name="text" displayName="Text" index="0" />
<df:EntityProperty name="value" displayName="Value" index="1" valuesProvider="VALUE 0, VALUE 1, VALUE 2">
<df:EntityProperty.editor>
<df:PropertyEditor type="Picker" />
</df:EntityProperty.editor>
</df:EntityProperty>
</df:RadDataForm.properties>
</df:RadDataForm>
<Button text="change" tap="changeModel" />
</StackLayout>
</Page>
teste.js
exports.onPageLoaded = function(args) {
console.log("Carregando página...");
page = args.object;
page.bindingContext = new ValueModel();
}
exports.changeModel = function(args) {
var arr = ["VALUE 0", "VALUE 1", "VALUE 2"];
page.bindingContext.set("model", new Value(
Math.random(10000, 99999).toString()
, arr[Math.floor(Math.random() * arr.length)]
)
);
console.log(JSON.stringify(page.bindingContext.model));
}
teste-model.js
var Observable = require("data/observable").Observable;
var Value = (function() {
function Value(text, value) {
this.text = text;
this.value = value;
}
return Value;
})();
var ValueModel = (function(_super) {
__extends(ValueModel, _super);
function ValueModel() {
_super.call(this);
this.model = new Value("This is a texte","VALUE 1");
}
Object.defineProperty(ValueModel.prototype, "model", {
get: function () {
return this.get("_model");
},
set: function(_model) {
this.set("_model", _model);
},
enumerable: true,
configurable: true
});
return ValueModel;
})(Observable);
exports.Value = Value;
exports.ValueModel = ValueModel;
It looks like you you are making runtime changes to an simple JavaScript object (the model) which is why those changes are not being reflected in the RadDataForm. When the desired behavior is to be able to change some objects properties at runtime you could use the {N} Observable located at data/observable module of the tns-core-modules. After that make the ValueModel to extend it and change the signature of the model property like this:
var observable_1 = require("data/observable");
var ValueModel = (function (_super) {
__extends(ValueModel, _super);
function ValueModel() {
_super.call(this);
this.model = new Value("This is a text", "INITIAL VALUE 0");
this.model.value = "VALUE 1";
}
Object.defineProperty(PersonViewModel.prototype, "model", {
get: function () {
return this.get("_model");
},
set: function (value) {
this.set("_model", value);
},
enumerable: true,
configurable: true
});
return PersonViewModel;
}(observable_1.Observable));
You can also take a look at the TypeScript variant in the master branch of the nativescript-telerik-ui-samples GitHub repository which was changed to illustrate this scenario.
Related
while playing around with the cats listview example from Raymond Camden
(thanks so much
[1]: https://nativescript.org/blog/client-side-storage-in-nativescript-applications/),
I try to store the data with the nativescript secure storage plugin.
In the cats.vue file I will show my code:
<template>
<Page class="page">
<ActionBar title="cats" class="action-bar" color="#ffbb00" />
<StackLayout class="home-panel">
<ListView for="cat in cats">
<v-template>
<Label :text="cat.name"></Label>
</v-template>
</ListView>
<Button #tap="addCat" text="Add Cat"></Button>
</StackLayout>
</Page>
</template>
<script>
const appSettings = require("application-settings");
var SecureStorage = require("nativescript-secure-storage").SecureStorage;
var secs = new SecureStorage();
var cats= [];
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomName() {
var initialParts = ["Fluffy","Scruffy","King","Queen","Emperor","Lord","Hairy","Smelly","Most Exalted Knight","Crazy","Silly","Dumb","Brave","Sir","Fatty"];
var lastParts = ["Saam","Smoe","Elvira","Jacob","Lynn","Fufflepants the III","Squarehead","Redshirt","Titan","Kitten Zombie","Dumpster Fire","Butterfly Wings","Unicorn Rider"];
return initialParts[getRandomInt(0, initialParts.length - 1)] + ' ' + lastParts[getRandomInt(0, lastParts.length - 1)];
}
function getCats() {
return secs.getSync({
key: "foo"
});
}
export default {
data() {
return {
cats: []
}
},
created() {
this.cats = getCats();
},
methods: {
addCat() {
cats = [{ "name":randomName()}, ...cats];
var success = secs.setSync({
key: "foo",
value: cats
});
this.cats = getCats();
}
}
};
</script>
<style scoped>
</style>
The problem is, I do not see any cats on the app screen, when I add a cat.
When I use the original code with the CouchDB, it updates the cats.
What should I change?
Also when the app starts, there are no visible cats which should be stored in another
start of the app. How may I load the data from the securestorage to the screen when the app starts?
May you please explain the changes, so that I can understand it?
Do you have good link with an explanation of nativescript-vue update the data synchronization to the screen?
Best regards and thank you very much for your help
Juergen
This is wrong.
You should not use var as it is a global.
Remove the var cats= [], leave it only in data, not outside the export.
Further more move your other two functions to the methods within the exported object.
Thanks Robertino,
with following code there are 2 problems less:
<script>
const appSettings = require("application-settings");
var SecureStorage = require("nativescript-secure-storage").SecureStorage;
var secs = new SecureStorage();
export default {
data() {
return {
cats: []
}
},
created() {
this.cats = this.getCats();
},
methods: {
addCat() {
this.cats = [{ "name":this.randomName()},...this.cats ];
//console.log("xxxxxxxxxxxxxxxxxxx:" + cats);
var success = secs.setSync({
key: "foo",
value: this.cats
});
this.cats = this.getCats();
console.log(`after get:${this.cats}`);
},
getCats() {
return secs.getSync({
key: "foo"
});
},
getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
randomName() {
var initialParts = ["Fluffy","Scruffy","King","Queen","Emperor","Lord","Hairy","Smelly","Most Exalted Knight","Crazy","Silly","Dumb","Brave","Sir","Fatty"];
var lastParts = ["Saam","Smoe","Elvira","Jacob","Lynn","Fufflepants the III","Squarehead","Redshirt","Titan","Kitten Zombie","Dumpster Fire","Butterfly Wings","Unicorn Rider"];
return initialParts[this.getRandomInt(0, initialParts.length - 1)] + ' ' + lastParts[this.getRandomInt(0, lastParts.length - 1)];
}
}
};
I see a nice console.log in the method addcat() after the getcat method.
The cat data will be in the array after an add. And, when I restart, the cats stayed in the database.
The only problem now is, no cats where listed in the frontend.
Neither when adding nor when starting the app.
The data will not be shown in the frontend.
Any ideas?
Regards Juergen
I have an issue with the radlistview with multiple item templates and toggling visibility.
I'm trying to re-create an according type display without using the nativescript-accordion plugin. By toggling the visibility attribute for a given item. Here's my xml:
<lv:RadListView row="3" items="{{ locationList }}" id="locationList" iosEstimatedRowHeight="0" itemTap="listViewItemTap" itemTemplateSelector="templateSelector" class="list-group">
<lv:RadListView.itemTemplates>
<template key="header">
<GridLayout columns="auto, *" visibility="{{ isItemVisible ? 'visible' : 'collapsed' }}">
...content...</GridLayout>
</template>
<template key="list-item">
<GridLayout columns="*, auto" rows="auto, 15" visibility="{{ isItemVisible ? 'visible' : 'collapsed' }}">...content...
</GridLayout>
</template>
</lv:RadListView.itemTemplates>
</lv:RadListView>
Here's the itemTap method:
if (tappedItem.type == "list-item" || tappedItem.type == "list-item-no-location") {
// Navigate to the details page with context set to the data item for specified index
topmost().navigate({....}});
} else if (tappedItem.type == "header") {
viewModel.collapseExpandItems(tappedItem);
setTimeout(() => {
if (platformModule.isIOS) {
// Uncomment the lines below to avoid default animation
// UIView.animateWithDurationAnimations(0, () => {
var indexPaths = NSMutableArray.new();
indexPaths.addObject(NSIndexPath.indexPathForRowInSection(rowIndex, args.groupIndex));
//console.log("indexPaths:", indexPaths);
listView.ios.reloadItemsAtIndexPaths(indexPaths);
// });
}
if (platformModule.isAndroid) {
listView.androidListView.getAdapter().notifyItemChanged(rowIndex);
}
}, 550);
And for the loading of the items, here is some code:
var newHeader = new Item(location.type, location.id, location.name, ..., true);
viewModel.locationList.push(newHeader);
var newItem = new Item(listItem.type, listItem.id, listItem.name, ... , true);
viewModel.locationList.push(newItem);
locationList being the ObservableArray in the viewModel.
And here is the Item class in the viewModel:
var Item = (function (_super) {
__extends(Item, _super);
function Item(type, id, name, ..., isItemVisible) {
var _this = _super.call(this) || this;
_this.type = type;
...
_this.isItemVisible = isItemVisible;
return _this;
}
Item.prototype.toggleVisibility = function (args) {
// console.dir(this);
console.log("toggleVisibility value: " + this.isItemVisible);
this.set("isItemVisible", !this.isItemVisible);
};
return Item;
}(Observable.Observable));
And finally the viewModel.collapseExpandItems method in the viewModel:
collapseExpandItems: function(tappedItem) {
this.locationList.forEach(function(item) {
//console.log("isItemVisible:", item.isItemVisible);
if ((item.type === 'list-item') && item.id === tappedItem.id) {
item.toggleVisibility();
}
});
},
It's hiding the items below the header item, but all the items below, even the ones that were not set to visibilty="collapsed".
Please see .gif for the behavior. Any ideas?enter image description here
It seems like momentarily its doing the right thing, but then it hides everything under, which is not what I want. I want it to just hide the items under the tapped header.
var ContactManager = new Marionette.Application();
ContactManager.addRegions({
mainRegion: "#main-region",
child:"#child2"
});
Ar = Backbone.Model.extend({});
Se = Backbone.Model.extend({});
Articlescollection = new Ar({ product_id: "104", title: "Test title"});
SelectedsCollection = new Se({ product_id: "71", title: "Test title"});
ContactManager.StaticView = Marionette.ItemView.extend({
template: tpl2,
tagName: "div",
model:Articlescollection,
modelEvents: {
'change': 'fieldsChanged'
},
fieldsChanged:function(){
console.log('dddd')
},
initialize: function () {
this.model.on('change', this.render);
}
});
ContactManager.StaticView2 = Marionette.ItemView.extend({
template: tpl2,
tagName: "div",
model:SelectedsCollection
});
var MyLayout = Backbone.Marionette.LayoutView.extend({
template: tpl3,
regions: {
menu: "#menu",
content: "#content"
}
});
ContactManager.on("start", function() {
// ContactManager.mainRegion.show( new MyLayout )
var layout = new MyLayout
ContactManager.mainRegion.show( layout )
layout.menu.show(new ContactManager.StaticView());
layout.content.show(new ContactManager.StaticView2())
Articlescollection.set("product_id", 24)
//init fieldsChanged trigger for change model
})
ContactManager.start();
What differences between modelEvents and this.model.on ?
they both initizlized when model was change but
modelEvents: {
'change': this.render
},
throw exception Uncaught TypeError: Cannot read property 'split' of undefined
modelEvents is the same as this.listenTo(this.model, { 'change': 'fieldsChanged' }); It is just sugar so you don't have to add that to initialize. You should probably never use this.model.on inside a view. That would not get cleaned up automatically like this.listenTo would. Other than this.on I don't think on should be used in general as listenTo is much safer.
The other major difference here is that:
var model = this.model;
var view = this;
this.model.on('change', function() {
this === model; // true
this === view; //false
});
The only reason this would work with render is because render is forcibly bound to the view by marionette. Any other function would have a different scope. You can change the scope by passing it as the 3rd variable of on, but again then you need to this.model.off in onBeforeDestroy
If you want to call render from modelEvents you have a few options:
modelEvents: {
'change': 'render'
}
//or
modelEvents: function() {
return {
'change': this.render
};
}
// or
modelEvents: {
'change': function() { this.render(); }
}
I have a simple table (type sap.ui.table.Table) where I allow my users to sort, filter and group elements. However there is no possibility to remove sorting or grouping once it is applied? The filter could be removed by entering no value in the filter, but how do I remove sorting/grouping?
var oTableEmpl = new sap.ui.table.Table({
width : "100%",
visibleRowCount : 20,
selectionMode : sap.ui.table.SelectionMode.Multi,
navigationMode : sap.ui.table.NavigationMode.Scrollbar,
editable : false,
enableCellFilter : true,
enableColumnReordering : true,
enableGrouping : true,
extension : oMatrixLayout,
});
oTableEmpl.addColumn(new sap.ui.table.Column({
label : new sap.ui.commons.Label({
text : "Label",
textAlign : sap.ui.core.TextAlign.Center
}),
template : new sap.ui.commons.TextView({
text : "{Value}",
textAlign : sap.ui.core.TextAlign.Center
}),
visible : false,
sortProperty: "Value",
filterProperty: "Value",
}));
This might seem easy, but in the table itself there is no option to remove anything. Does it really have to be removed by programming something?
Yes, there is only way to do this by coding. Basically you need to clear sorters and filters of the ListBinding, and then refresh the DataModel. For grouping, reset the grouping of Table and Column to false, after reset, set grouping of Table back to true.
//set group of table and column to false
oTableEmpl.setEnableGrouping(false);
oTableEmpl.getColumns()[0].setGrouped(false);
var oListBinding = oTableEmpl.getBinding();
oListBinding.aSorters = null;
oListBinding.aFilters = null;
oTableEmpl.getModel().refresh(true);
//after reset, set the enableGrouping back to true
oTableEmpl.setEnableGrouping(true);
I also attached a working code snippet. Please have a check.
<script id='sap-ui-bootstrap' type='text/javascript' src='https://sapui5.hana.ondemand.com/resources/sap-ui-core.js' data-sap-ui-libs="sap.m,sap.ui.commons,sap.ui.table,sap.viz" data-sap-ui-theme="sap_bluecrystal"></script>
<script id="view1" type="sapui5/xmlview">
<mvc:View xmlns:core="sap.ui.core" xmlns:layout="sap.ui.commons.layout" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.ui.commons" xmlns:table="sap.ui.table" controllerName="my.own.controller" xmlns:html="http://www.w3.org/1999/xhtml">
<layout:VerticalLayout>
<Button text="Reset" press="onPress" />
<table:Table id="testTable" rows="{/}" enableGrouping="true">
<table:Column sortProperty="abc" sorted="true" visible="true">
<table:label>
<Label text="abc"></Label>
</table:label>
<table:template>
<Label text="{abc}"></Label>
</table:template>
</table:Column>
<table:Column>
<table:label>
<Label text="abc2"></Label>
</table:label>
<table:template>
<Label text="{abc2}"></Label>
</table:template>
</table:Column>
</table:Table>
</layout:VerticalLayout>
</mvc:View>
</script>
<script>
sap.ui.controller("my.own.controller", {
onInit: function() {
var aTableData = [{
abc: 1,
abc2: "a"
}, {
abc: 6,
abc2: "b"
}, {
abc: 6,
abc2: "c"
}, {
abc: 3,
abc2: "g"
}, {
abc: 3,
abc2: "h"
}];
var oTableModel = new sap.ui.model.json.JSONModel();
oTableModel.setData(aTableData);
var oTable = this.getView().byId("testTable");
oTable.setModel(oTableModel);
oTable.sort(oTable.getColumns()[0]);
},
onPress: function() {
var oTable = this.getView().byId("testTable");
//set group of table and column to false
oTable.setEnableGrouping(false);
oTable.getColumns()[0].setGrouped(false);
var oModel = oTable.getModel();
var oListBinding = oTable.getBinding();
oListBinding.aSorters = null;
oListBinding.aFilters = null;
oModel.refresh(true);
//after reset, set the enableGroup back to true
oTable.setEnableGrouping(true);
}
});
var myView = sap.ui.xmlview("myView", {
viewContent: jQuery('#view1').html()
}); //
myView.placeAt('content');
</script>
<body class='sapUiBody'>
<div id='content'></div>
</body>
For openui5 v1.78.7: If you want to delete these Filters from the table:
You can do:
var columns = this.byId("tableId").getColumns();
for (var i = 0, l = columns.length; i < l; i++) {
var isFiltered = columns[i].getFiltered();
if (isFiltered) {
// clear column filter if the filter is set
columns[i].filter("");
}
}
You can clear sort filters with:
var columns = table.getColumns();
var sortedCols = table.getSortedColumns();
for (var i = 0, l = columns.length; i < l; i++) {
if (sortedCols.indexOf(columns[i]) < 0) {
columns[i].setSorted(false);
}
}
Make sure you set back sort on row binding if you had any with:
table.getBinding("rows").sort(new Sorter(sPath, bDescending));
I have created a horizontal bar chart. I have generated the data for the bar chart from the database in the code behind.
I have two questions regarding the bar chart:
The bar charts are displaying but few of them are overlapping. I have tried setting "barPadding" and "barMargin" but that doesn't work.
I want to display label against each bar chart stating what each bar represents. These labels are retrieved from the database in code behind and stored in string array. That is against each bar in the bar chart I want to display label like "Accounts", "Telecommunication" etc. These labels are in the array retrieved from database in code behind.
Any inputs on above queries would be much appreciated.
The following is the JQPlot Script
<script type="text/javascript">
function RunTest() {
$.jqplot.config.enablePlugins = true;
<% var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();%>
var industryName = <%= serializer.Serialize(arrIndustryName) %>
industryPostings = <%= serializer.Serialize(arrIndustryPostings)%>
plot = $.jqplot('dispStats', [industryPostings], {
captureRightClick: true,
seriesDefaults: {
renderer: $.jqplot.BarRenderer,
showAngel: 135,
rendererOptions: {
barDirection: 'horizontal',
barMargin: 200,
barPadding: 500,
barWidth: 10,
highlightMouseDown: true
},
pointLabels: { show: true, formatString: '%d' }
},
legend: {
show: true,
location: 'e',
placement: 'outside'
},
axes: {
yaxis: {
renderer: $.jqplot.CategoryAxisRenderer
}
}
});
return false;
}
</script>
//asp.net button control to invoke code behind code and run the bar chart script
<asp:Button ID="btnTrends" runat="server" Text="Get Data" OnClick="GetPostingStatistics"
style="margin-right:10px"/>
<asp:Button ID="test" runat="server" Text="Show Trend" OnClientClick="return RunTest()" />
The following is the Code Behind Code
protected void GetPostingStatistics(object sender, EventArgs e)
{
string strFromDate = txtFromDate.Text;
string strToDate = txtToDate.Text;
switch (ddlTrend.SelectedValue)
{
case "Industries":
{
//Code for Sql Connection
sql = "select Value from mpats_FieldValue where FieldID =15";
cmd = new SqlCommand(sql, conn);
adapter = new SqlDataAdapter(cmd);
postingRecords = new DataSet();
adapter.Fill(postingRecords);
List<string> industryNames = new List<string>();
// Code for retrieving labels against each bar from database
foreach (DataRow name in postingRecords.Tables[0].Rows)
{
industryNames.Add(name.Field<string>("Value"));
}
int[] numberOfPostings = new int[industryNames.Count];
for (int i = 0; i < industryNames.Count; i++)
{
sql = "select count(p.PostingId) as PostingsCount from
mpats_AdvertPosting p left join mpats_Advert a on p.AdvertID =
a.AdvertID " +
"where p.PostingStatusID between 400 and 451 " +
"and a.Industry = #Industry";
cmd = new SqlCommand(sql, conn);
cmd.Parameters.Add("#Industry", industryNames[i]);
adapter = new SqlDataAdapter(cmd);
postingRecords = new DataSet();
adapter.Fill(postingRecords);
numberOfPostings[i] = Convert.ToInt32(postingRecords.Tables
[0].Rows[0]
["PostingsCount"]);
}
arrIndustryName = industryNames.ToArray();
arrIndustryPostings =
numberOfPostings;
}
conn.Close();
break;
}
default:
{
break;
}
}
}
Thanks
Shwetha