I have an application which uses backbone js and jquery. I need to generate templates dynamically based on what response i get from the server. The scenario is like this... I have a dropdown of templates which I can choose, after I select any template from the dropdown, an api is called for that template which provides me with a structure like this
items:[
{
"vars":
{
"name":
{
"required": false,
"default": "abc"
},
"address":
{
"required": false,
"default": "xyz"
}
}
"tables": {}
}]
So how can I create dynamic form elements using this kind of server response?
You could use the backbone-forms plugin, and map your server response to a form object. Something like:
var data = {};
var schema = {};
var item = _.first(items);
_.each(item.vars, function(value, key) {
data[key] = value.default;
schema[key] = {
type:"Text",
validators:value.required ? ['required'] : undefined
};
});
var form = new Backbone.Form({data:data, schema:schema}).render();
That's just a simple, untested example. Check out the backbone-forms documentation, and see if it can do what you need.
Related
I am in Angular environment using Kendo. All I want to do is following:
Take Json
Produce Kendo tree using it
I have tried it with simple data and it seems to work fine. But this time I have somewhat complex data and it seems like it does not work well with complex Json. I have been trying to have it render Json but it seems like it keeps on thinking and never comes back. I have created a sample Dojo for reference:
http://dojo.telerik.com/EdOqE
I am not sure what am I doing wrong but it just does not seem to work. Can anyone help me with this please?
I presume you have controll over the resultant json, because you'll have to change it a little to fit the TreeView's expected format. Check this out:
{
"items": [{ // Projects
"Id": 0,
"Name": "Your Example Project",
"CreatedOn": "",
"hasChildren": true,
"items": [{ // Analyses
"Id": 0,
"Name": "1.0 - Your Example Run",
"CreatedOn": "",
"hasChildren": true,
"items": [{ // Samples
"Id": 0,
"Name": "Sample 1",
"hasChildren": false,
"Description": "ample frample sample"
}, {
"Id": 0,
"Name": "Sample 2",
"hasChildren": false,
"Description": null
}]
}]
}]
};
The above json is what I did to work in the widget. First of all, the collection properties were renamed to items. All of them, in all levels. With that, kendo will know how property it should deal with. A hasChildren property was added to let it know when it has to show the expand icon. Otherwise it will show the expand option even if the item doesn't haves any children. So user clicks it and get an empty result.
This is the widget initialization options:
{
dataSource: new kendo.data.HierarchicalDataSource({
data: things,
schema: {
data: "items"
}
}),
dataTextField: "Name"
};
With schema.data I tell which property kendo will deal as the collection item. The dataSource expects an array, but if you give him an object, you have to set this property. If it was an array, then kendo would look for item property of each child for default. dataTextField is the name of the property it will use as the label.
Demo
Here is another demo with the data as an array. No need to set schema.data.
Update:
I was afraid you would say that. Yes, there is a way to deal with the data if you can't change it in the server-side. You have to intercept the data at the schema.parse() method and change the resultant data object property to items, so then the widget will understand:
schema: {
data: "items",
parse: function(data) {
if (data.hasOwnProperty("Projects")) {
return { items: data.Projects };
}
else if (data.hasOwnProperty("Analyses")) {
return { items: data.Analyses };
}
else if (data.hasOwnProperty("Samples")) {
return { items: data.Samples };
}
}
}
Demo
Every node when opened will call parse with items collection as data parameter. You have to return a new object with the property name as items instead of Projects, Analysis or Samples.
I forgot you can't touch the data, so can't add hasChildren property as well. Then you have to add a tiny logic into parse to set those properties in each level, otherwise the expand icon would not appear:
schema: {
data: "items",
parse: function(data) {
if (data.hasOwnProperty("Projects")) {
data.Projects.forEach(p => {
p.hasChildren = false;
if (p.hasOwnProperty("Analyses")) {
p.hasChildren = true;
}
});
return { items: data.Projects };
}
else if (data.hasOwnProperty("Analyses")) {
data.Analyses.forEach(a => {
a.hasChildren = false;
if (a.hasOwnProperty("Samples")) {
a.hasChildren = true;
}
});
return { items: data.Analyses };
}
else if (data.hasOwnProperty("Samples")) {
return { items: data.Samples };
}
}
}
Demo
It is ugly, I know. But get used to Kendo, it is the it goes with it.
My problem is that I am just starting out with Backbone.js and are having trouble wrapping my head around a complex problem. I want to save a form that have infinite fields, and some of the fields also needs to have infinite options. I'm just worried I might have started at the wrong end with a JSON response, instead of building the models/collections first. Here is a short pseudocode of what I try to achieve.
id:
parent: <blockid>
fields: array(
id:
title:
helpertext
options: array(
id:
type:
value:
)
)
Currently I am working with a faked JSON response from the server, which I built from scratch, and now I want to divide it into models and collections on the client side.
//Fake a server response
var JSONresponse = {
"formid":"1",
"fields":[
{
"fieldid":"1",
"title":"Empty title",
"helper":"Helper text",
"type":"radio",
"options":[
{
"optionid":"1",
"value":"Empty option.."
},
{
"optionid":"2",
"value":"Empty option.."
}
]
},
{
// fieldid2
}
]
};
The idea is to add fields as I see fit, and then if the field type is radio/checkbox/ul/ol there must also be an "options" array within the field.
My work so far:
var app = {};
app.Models = {};
app.Collections = {};
app.View = {};
app.Models.Option = Backbone.Model.extend({
});
app.Collections.Options = Backbone.Collection.extend({
model: app.Models.Option
});
app.Models.Field = Backbone.Model.extend({
options: new app.Collections.Options()
});
app.Collections.Fields = Backbone.Collection.extend({
model: app.Models.Field
});
app.Models.Form = Backbone.Model.extend({
formid : "1",
fields: new app.Collections.Fields(),
initialize: function() {
}
});
How do I split up my JSON response into all these models and collections?
(Perhaps I should re-evaluate my approach, and go for something like form.fieldList and form.optionList[fieldListId] instead. If so, how would that look like?)
Edit: Here is a little jsfiddle after many fixes, but I still don't really know how to make the inner options list work.
The easiest solution would be using Backbone Relational or Backbone Associations.
The documentation should be enough to help you get started.
If you don't want to use a library you could override the parse function on the Form model.
app.Models.Form = Backbone.Model.extend({
defaults: {
fields: new app.Collections.Fields()
},
parse: function(response, options) {
return {
formid: response.formid,
fields: new app.Collections.Fields(_.map(response.fields, function(field) {
if (field.options) {
field.options = new app.Collections.Options(field.options);
}
return field;
}))
};
}
});
Now if you fetch a form from the server, the response will be parsed into an object graph of models and collections.
form.get('fields') will return an app.Collections.Fields collection. form.get('fields').first().get('options') will return an app.Collections.Options collection, if any options exist.
Also, you could create the form model like this:
var form = new app.Models.Form(JSONresponse, {
parse: true
});
This would result in the same object structure.
It's quite hard to handle the case of nested models and collections right in plain Backbone.
Easiest way of handling this will be something like this:
var Option = Nested.Model.extend({
idAttribute : 'optionid',
defaults : {
optionid : Integer
value : ""
}
});
var Field = Nested.Model.extend({
idAttribute : 'fieldid',
defaults : {
fieldid : Integer,
title : "",
helper : "",
type : "radio",
options : Option.Collection
}
});
var Form = Nested.Model.extend({
idAttribute : 'formid',
defaults : {
formid: Integer,
fields: Field.Collection
});
https://github.com/Volicon/backbone.nestedTypes
And that's it. Yep, you'll get direct access to the attributes as free bonus, just form.fields.first().options.first().value, without that get and set garbage.
I have quite an interesting question (I hope) for all you AngularJS gurus out there. I am looking to create a dynamic list of form input fields based on a SELECT dropdown. As an example, we have a number of categories with each category having a set of specifications which are unique to that category. To help with the explanation we have the following:
Firstly, in the controller we start by initializing the models.
$scope.category = {};
$scope.category.specs = [];
Next we ready the data to be used in the form (actually retrieved from the server via $http). We also initialize a variable to the first element in the categories array.
$scope.categories = [
{ "id": "1", "name": "mobile", specs: [
{ "id": "1", "label": "Operating System" },
{ "id": "2", "label": "Camera type" } ] },
{ "id": "2", "name": "laptop", specs: [
{ "id": "1", "label": "Operating System" },
{ "id": "2", "label": "Graphics Card" } ] }
};
$scope.selectedCategory = $scope.categories[0];
In the form, we have a dropdown which when selected loads the appropriate input fields specific to that category. We use the ngRepeat directive to accomplish this. This is a dynamic list of fields based on $scope.categories.specs. (please note the ???)
<select ng-model="selectedCategory" ng-options="category.name for category in categories"></select>
<div ng-repeat="spec in selectedCategory.specs">
<label>{{spec.label}}</label>
<input type="text" ng-model="???">
</div>
Ultimately, when the user clicks the submit button, we would like to extract the category he/she has selected and then package it together with the specifications they have filled in. The post request should contain something like the following for instance (of course, I only included one spec item, but in reality there would be many):
{ "id": "1", specs [ { "id": "2", "details": "RADEON HD 8970M" } ] }
Unfortunately I am not really sure how to accomplish this. I need to somehow create an array for the spec model, and then ensure that both the ID and user entered data are appropriately extracted... what goes in the ??? and what do we do after? Any help would be much appreciated.
this is how I do it. I make a form, validate it with angular, and then when its valid I submit it with a function.
<form name="signup_form" novalidate ng-submit="signupForm()"></form>
$scope.signupForm = function() {
var data = $scope.signup;
$http({
method : 'POST',
url : 'http://yoursite.com/mail.php',
data : $.param(data), // pass in data as strings
headers : { 'Content-Type': 'application/x-www-form-urlencoded' } // set the headers so angular passing info as form data (not request payload)
})
.success(function(data) {
});
}
also if you want to look at another form validation system for angular check out http://nimbly.github.io/angular-formly/#!/ It may help you solve your current form system.
In the controller, initialize $scope.specDetails as follows:
$scope.specDetails = {};
angular.forEach($scope.categories, function (category, index1) {
$scope.specDetails[category.id] = {};
angular.forEach(category.specs, function (spec, index2) {
$scope.specDetails[category.id][spec.id] = '';
});
});
In the html, replace "???" with specDetails[selectedCategory.id][spec.id]
I have a network array like the following way
"network_contents": [
{
"facebook":"contents to all pages",
},
{
"twitter":"twiter contents",
},
{
"linkedin":"linked in contents",
}
]
I would like to add some keys to that array bases on its content. If it is facebook the key should be facebook, if it is twitter key should be twitter. But not sure how to do it.
My requirement is to access network array contents, but it may or may not content these facebook, twitter, linked in values. I need to access its values. When i assign a key value will be easy to fetch its contents. So i tried this way to loop through the array
message.network_contents.forEach( function (nwContent) {
if(nwContent.twitter) {
console.log('nw content', nwContent.twitter);
}
})
can i create an array in this foreach loop like the following way.
{
"data": [
{
"facebook": {
"facebook": "facebook content"
},
"twitter": {
"twitter": "twitter content"
}
}
]
}
Your help is much appreciated thanks
Implementation of what I said in the comment:
var oldsies = stuff.network_contents;
var newsies = stuff.network_contents = {};
oldsies.forEach(function(network) {
var name = Object.keys(network)[0];
newsies[name] = network;
});
You gave an example of a JS object and not a dictionary and therefore cant add key-values.
You need something like this:
var network_contents = [];
network_contents["facebook"] = {config1: {name:"config1", value:"value1"}};
network_contents["twitter"] = {config2: {name:"config2", value:"value2"}};
example:
network_contents["facebook"].config1.value; // will return "value1"
You can covert your object to a dictionary easily.
This the model that I want to create using json file
Ext.define('Users', {
extend: 'Ext.data.Model',
fields: [{name: 'user_id', type: 'int'},
{name: 'user_name', type: 'string'}]
});
What do I have to do in order to automatically create this model, based on the content of a json response from the server?
In order to have the model created automatically, you need to include the metaData field with your Json data. metaData can be used to describe all of the fields for the Model.
In the ExtJS 4.1 documentation - Ext.data.reader.Json has a section called Response MetaData which describes basic use of this feature.
You should be able to pull down some json with fields and or some format that can be transformed into that format pretty easily.
Make call to service to get model's fields. Might need to define some chain that first calls model service and performs subsequent steps after.
Build model's field array w/ fields results from #1. May need to transform data based on response in #1.
var fields = response.fields;
Define model based on fields in Store's constructor
var store = Ext.create('Ext.data.Store', {
constructor: function () {
var model = Ext.define("Users", {
extend: "Ext.data.Model",
fields: fields
});
this.model = model.$className;
this.callParent(arguments);
}
});
I only use the jsonp, which loads an json file and parses it automatically, don't know if Ext.Ajax does this, too.
But you would do something like this:
definition.json:
{
"name": "User",
"fields": [
{ "name": "user_id" , "type": "int" },
{ "name": "user_name", "type": "string" }
]
}
load it:
Ext.Ajax.request({
url : "..../definition.json"
success: function( res ) {
Ext.define( res.name, {
extend: 'Ext.data.Model',
fields: res.fields
}, function() {
Ext.create( 'somestore', { model: res.name });
});
}
});