sapui5 data from json - json

Have problem to read data from json. My login.json file
{
"Users": [
{
"login" : "admin",
"password" : "admin"
}
]
}
Here is my login.xml.view
<core:View
controllerName="sap.ui.demo.myFiori.view.login"
xmlns="sap.m"
xmlns:l="sap.ui.layout"
xmlns:core="sap.ui.core" >
<Page
title="{i18n>LoginIn}">
<VBox
class="marginBoxContent" >
<items>
<Label text="User" />
<Input
id="nameInput"
type="Text"
placeholder="..." />
<Label text="password" />
<Input
id="passwInput"
type="Password"
placeholder=" ..." />
<Text id="description" class="marginOnlyTop" />
<Button text="Login" press="handleContinue" />
</items>
</VBox>
</Page>
</core:View>
and login.controller.js
jQuery.sap.require("sap.ui.demo.myFiori.util.Formatter");
sap.ui.controller("sap.ui.demo.myFiori.view.login", {
handleContinue : function (evt) {
var name = this.getView().byId("nameInput").getValue();
var paswd = this.getView().byId("passwInput").getValue();
if (name == "log" && paswd == "pass") {
var context = evt.getSource().getBindingContext();
this.nav.to("Master", context);
}
else {
jQuery.sap.require("sap.m.MessageToast");
sap.m.MessageToast.show("Wrong login");
}
}
});
This login screen works, but I can't get login and password from json file and currently these data are taken from if sentence, which is not good. Any suggestions?

First, I am assuming that this is just a test app, as having the correct plain text auth credentials actually in the client app is rather bad practice ...
The crux of your question seems to be: "How can I access JSON data from a file?"
There is a Q&A already on SO with that theme: How to get data from a json file? but if you want to use a model, which is a common practice in UI5 apps, then you could:
1) Create a JSON model, specifying the name of the JSON file; the JSONModel mechanism will take care of the loading; assign this to the view
this.getView().setModel(new sap.ui.model.json.JSONModel('login.json'), "login");
2) Access the data in the JSON model when you need to check, using the getData() method
// Note - extremely bad practice - don't do this for real!
var authinfo = this.getView().getModel("login").getData().Users[0];
if (name == authinfo.login && paswd == authinfo.password) {
....
(I'm indexing 0 of Users as you seem to have your authinfo inside an array)

Maybe I defined something wrong before but now it works. I changed login.controller.js. Now it looks like this:
jQuery.sap.require("sap.ui.demo.myFiori.util.Formatter");
sap.ui.controller("sap.ui.demo.myFiori.view.login", {
onInit: function () {
var oModels = new sap.ui.model.json.JSONModel("login.json");
sap.ui.getCore().setModel(oModels);
},
handleContinue : function (evt) {
var authinfo = this.getView().getModel().getData().Users[0];
var name = this.getView().byId("nameInput").getValue();
var paswd = this.getView().byId("passwInput").getValue();
if (name == authinfo.login && paswd == authinfo.passw) {
var context = evt.getSource().getBindingContext();
this.nav.to("Master", context);
}
else {
jQuery.sap.require("sap.m.MessageToast");
sap.m.MessageToast.show("Wrong login");
}
}
});

Related

How do I update the model for a WebGrid using Ajax?

I'm trying to create a WebGrid that allows me to update the data source with Ajax. The data source is assigned a List<> containing a model I created to hold the data. The WebGrid populates correctly and I have no problem sorting using the header of the WebGrid or paging.
The code for the View:
#model IEnumerable<TestDataModel.Models.TestData>
#{
ViewBag.Title = "Test Website";
var grid = new WebGrid(canPage: true, canSort: true, ajaxUpdateContainerId: "grid", ajaxUpdateCallback: "callBack");
grid.Bind(Model, rowCount: 10);
grid.Pager(WebGridPagerModes.All);
#grid.GetHtml(htmlAttributes: new { id = "grid" },
tableStyle: "webgrid-table",
headerStyle: "webgrid-header",
alternatingRowStyle: "webgrid-alternating-row",
rowStyle: "webgrid-row-style",
mode: WebGridPagerModes.All,
firstText: "<< First",
previousText: "< Prev",
nextText: "Next >",
lastText: "Last >>",
columns: grid.Columns("This is a place holder,
I have a lot of columns"
));
}
<div>
<br />
<p>
Page Number: <input type="text" name="pageNumber" id="pageNumber" />
</p>
<p>
<input type="button" name="pageNumberButton" id="pageNumberButton" value="Page" />
</p>
</div>
<script type="text/javascript">
$(document).ready(function () {
$("#pageNumberButton").click(function () {
var pageNumber = $("#pageNumber").val();
$.ajax({
url: '#Url.Action("GetData", "Test")?pageNumber=' + pageNumber,
success: function (data) {
$("#grid").html(data);
},
error: function (data) {
alert("Error");
}
})
});
});
</script>
The bottom script tag is the ajax call that I'm trying to use to update the WebGrid. All that the "GetData" does is create a list pulling data from an SQL server and returns Json(testData, JsonRequestBehavior.AllowGet).
Code for Controller:
public ActionResult GetData(string pageNumber)
{
List<TestData> testData = new List<TestData>();
if (string.IsNullOrEmpty(pageNumber))
{
testData = Data.Data.TestDataGet();
}
else
{
try
{
var pageInt = Convert.ToInt32(pageNumber);
List<ParameterSQL> parameter = new List<ParameterSQL>();
parameter.Add(new ParameterSQL { Name = "#pageOffset", Value = pageInt });
testData = Data.Data.TestDataGet(parameter);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
return Json(testData, JsonRequestBehavior.AllowGet);
}
The error that I'm experiencing is when I push the button the WebGrid disappears entirely. The reason that I need to be able to update the WebGrid data source is because my table is millions of lines long and I'm only grabbing them a few thousand at a time. Once the user reaches the end of the of the list I want them to be able to load more using a SQL query that I already have working. Any help would be great, thanks.

JSON.stringify() function is replacing the file object with an empty one

ISSUE
Hello guys, can anyone please help me to solve this complicated issue? :
I'm creating an application using Spring boot v2.0.5 and React.js v15.6.2, ReactDom v15.6.2, React Bootstrap v0.32.4, and the linking between my frontend and serverside parts is made of restful web services using Spring annotations on the back and fetch API on the front. My React components are made by following the Parent-Children design pattern concept, that means: some of my components can be children of some others and vice versa.
How it works?
I have a table with columns and rows, each row inside the table has a unique id, 2 drop-downs, 1 text input, 2 datepickers and 1 file upload input which is causing the main issue; The user can add more rows that has same components as the previous ones, by clicking on the "+ Document" button; Each row has a unique incremental Id of type number (integer); the drop-downs and the inputs events are handled by one method inside the parent component based on their tag names;
I'm storing all data entered by the user inside a list ([]) of objects({}).
Example: if the user fill only the first row; the object stored inside the list state will be like this:
[{id:0,type:"forms",lang:"all",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"12-12-2018",document:{File(154845)}]
if the user adds one other row and then filled it like the first one, the list will be like this:
[{id:0,type:"forms",lang:"all",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"12-12-2018",document:{File(154845)},{id:1,type:"howTo",lang:"en",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"01-01-2019",document:{File(742015)}]
Check this image to see how the table look like: Table Demo
The table as code in Presentational component Class (child of the main component)
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
docObjList: [],
element: (
<FormDocRowItem // this contains the table tbody tds elements..
id={1}
handleChanges={this.props.handleChanges}/>)
};
this.handleAddDocumentRow = this.handleAddDocumentRow.bind(this);
}
// handleAddDocumentRow method
handleAddDocumentRow(e) {
const value = e.target.value;
const name = e.target.name;
if (name === 'add') {
let arr = this.state.docObjList; // get the list state
// assign the new row component
arr = [...arr, Object.assign({}, this.state.element)];
// set the new list state
this.setState({docObjList: arr});
}
// if name === 'delete' logic..
}
// render method
render() {
const {handleReset} = this.props;
return(
<FormGroup>
<Form encType="multipart/form-data">
<Table striped bordered condensed hover>
<thead>
<tr>
<th>id</th>
<th>Type</th>
<th>Lang</th>
<th>Title</th>
<th>Date begin</th>
<th>Date end</th>
<th>+ Document</th>
<th>Options</th>
</tr>
</thead>
<tbody>
{this.state.element} // this row is required as initialization
{
this.state.docObjList.map((doc, index) => {
// as index in map() starts from 0 and there is an
// already row component above => The index inside the
// table should start from 1 except The key property
// which should know the right index of the function
const id = index+1;
return (
<tr key={index}>
<td>
{id}
</td>
<td>
<DocumentTypes id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<DocumentLanguage id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<DocumentLibelle id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<FormControl id={''+id} name="dateBegin" componentClass="input" type="date"
onChange={this.props.handleChanges}/>
</td>
<td>
<FormControl id={''+id} name="dateEnd" componentClass="input" type="date"
onChange={this.props.handleChanges}/>
</td>
<td>
<Document id={id} handleChange={this.props.handleChanges}/>
</td>
{
this.state.docObjList.length == index + 1 &&
<td>
<button type="button" style={{verticalAlign: 'middle', textAlign: 'center'}} id={index + 1}
name="delete"
onClick={this.handleAddDocumentRow}>
Delete
</button>
</td>
}
</tr>
);
})
}
</tbody>
</Table>
<button type="button" name="add" onClick={this.handleAddDocumentRow}>+ Document</button>
<FormGroup>
<Button type="reset"
style={{marginRight: '20%'}}
className="btn-primary"
onClick={this.props.handleClickSubmit}>Submit</Button>
<Button name="back" onClick={this.props.handleClickSubmit}>Annuler</Button>
</FormGroup>
</Form>
</FormGroup>
)
}
}
The row component class (Child component of Presentational)
const FormDocRowItem = (props) => {
const {id} = props; // the ID here is refering the column that is going to be
// show inside the table not the index of the map function
return(
return (
<tr>
<td>
{id}
</td>
<td>
<DocumentTypes id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<DocumentLanguage id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<DocumentLibelle id={id} handleChange={this.props.handleChanges}/>
</td>
<td>
<FormControl id={''+id} name="dateBegin" componentClass="input" type="date" onChange={this.props.handleChanges}/>
</td>
<td>
<FormControl id={''+id} name="dateEnd" componentClass="input" type="date" onChange={this.props.handleChanges}/>
</td>
<td>
<Document id={id} handleChange={this.props.handleChanges}/>
</td>
</tr>
);
}
}
Parent Component Class (The main component)
constructor(props) {
this.state ={
docDataList: [],
formIsReadyToSubmit: false
}
this.handleSubmit = this.handleSubmit.bind(this); // button submit click
this.handleReset = this.handleReset.bind(this); // button reset click
this.fillWithData = this.fillWithData.bind(this); // handle changes
}
// handleReset method..
fillWithData(e) {
const name = e.target.name; // get the name of the target
const id = parseInt(e.target.id); // get the id of the target
let value = e.target.value; // get the value of the target
let arr = this.state.docDataList; // get the list state
// if the target is a file upload
if (name === 'selectDocument')
value = e.target.files[0];
// create properties with null values starting from the first onchange
// event handling, to not get a misplaced properties inside the
// objects of the list state
arr.map((x) => {
x.type = x.type ? x.type : null;
x.lang = x.lang ? x.lang : null;
x.libelle = x.libelle ? x.libelle : null;
x.dateBegin = x.dateBegin ? x.dateBegin : null;
x.dateEnd = x.dateEnd ? x.dateEnd : null;
x.document = x.document ? x.document : null;
});
// if the event target name is not delete
if (name != 'delete') {
// check if the object id already exist in the table
// if it exists, the new value should replace the previous one
// and not allowed to add a new object to the list state
if ((arr.find((x) => x.id == id))) {
// loop through the list state to find the id of the object
arr.map((x) => {
if (x.id == id) {
// helper variable to prevent empty strings
const val = value != '' ? value : null;
switch (name) {
case 'selectType':
x.type = val;
break;
case 'selectLang':
x.lang = val;
break;
case 'libelle':
x.libelle = val;
break;
case 'dateBegin':
x.dateBegin = val;
break;
case 'dateEnd':
x.dateEnd = val;
break;
case 'selectDocument':
x.document = val;
break;
}
}
});
// assign the new list to my docDataList state
// mentioning that the id of the element already exist
this.setState({docDataList: arr}, () => {
console.log(' ID exist; new dataList :', this.state.docDataList);
});
}
// if the id doesn't exist (means that the button +document is clicked)
else {
// again, a helper variable as the previous statement
const val = value != '' ? value : null;
this.setState({
docDataList: [...arr, Object.assign({
id: id,
type: name === 'selectType' ? val : null,
lang: name === 'selectLang' ? val : null,
libelle: name === 'libelle' ? val : null,
dateBegin: name === 'dataBegin' ? val : null,
dateEnd: name === 'dateEnd' ? val : null//,
//document: name==='selectDocument'? val:null
})]
}, () => {
console.log('ID doesnt exist; new dataList :', this.state.docDataList);
});
}
}
}
HandleSubmit() method (Inside the Parent component class)
// Submit button click handler
handleSubmit(e) {
let docDataList = this.state.docDataList;
// if the user didn't touch any thing on the table rows
// that means the list is empty and its length = 0
if (docDataList.length === 0) {
this.setState({
alerts: {
message: 'Please enter your document information ',
show: true
}
});
}
// if the user has entered a data on the table row
else if (docDataList.length > 0) {
let data = new FormData(); // object which will be sent
// check the docDataList before request
console.log('DocDataList before request:', docDataList);
data.append('docDataList', JSON.stringify(docDataList));
fetch('http://localhost:8080/api/files/uploadFile', {
method: 'POST',
body: data
}).then(response => {
console.log('success document upload', response);
}).catch(error => {
console.log('error', error);
});
this.setState({
formIsReadyToSubmit: true,
docDataList: [], // reset the list
alerts: {updateAlert: true} // make an alert
});
}
}
To see what the console show when I fill the row with data: CLICK HERE PLEASE
To see the response of the request: CLICK HERE PLEASE
NOTE: You may notice after watching those screenshots, that there is an extra list of data called "arrContrats" which which I didn't mention it in my issue because it doesn't have any problem; the problem is with the "docDataList" list. Thanks in advance
If your problem is that you're getting a File object from the browser, and then later using JSON.stringify on it (or something containing it) and getting {} for it in the JSON, that's correct. The browser's File object has no own, enumerable properties. JSON.stringify only includes own, enumerable properties.
If you want the various properties that File objects have (inherited accessor properties), you'll need to copy them to a new object.
If you want the file data, it's not accessible as a property on the object, you have to use one of the methods it inherits from Blob to read the file data, such as stream, text, or arrayBuffer (or alternatively, you could use a FileReader, but there's no need to except in obsolete environments that don't have the modern methods).
function stringifyFileObject(arrWithFiles = []) {
const arrData = [];
for (let i=0; i < arrWithFiles.length; i++) {
const file = arrWithFiles[i];
const obj = {
lastModified: file.lastModified,
name: file.name,
size: file.size,
type: file.type,
webkitRelativePath: file.webkitRelativePath,
}
arrData.push( obj );
}
}
Or whatever that suits your needs. You get the idea...
Don't use JSON.stringify() with FormData instance. And don't use headers: {'Content-Type': 'application/json'} if you are sending a file.
Example:
let data = new FormData(form);
let config = {
method: "POST",
body: data
}
const response = await fetch(url, config);
const responseData = response.json();

Validate ID using JSON

I have a JSON file like this:
[
{
"id":"4028c2fe3ec554da013ec557dcb50000",
"logInformation":{
"createDate":1369110469000
},
"idbarang":"01",
"nama":"COBA",
"status":"Tidak Bergaransi",
"memo":"ddsdsds"
}
]
The JSON data above was created automatically from a database. And I have a form with code like this:
<form name="simpanbarang" id="tambahbarang">
<input type="text" id="id">
<input type="submit" value="save">
</form>
I want to validate at submit time and if the value of id is the same as in the JSON file above, then show an alert "data is unavailable".
Can you help me?
Refer this site. Here is a sample code for your validation, make needful modifications
$(function () {
/* Varable Declarataion..... */
var $id = $("#id").attr("name");
/* Varable Declarataion ends..... */
var $params = {debug:false, rules:{}, messages:{}};
$params['rules'][$id] = {required:true, "jsonValidator":true};
$params['messages'][$id] = {required:"Please enter a value", jsonValidator:"Invalid data"};
jQuery.validator.addMethod("jsonValidator", function(value, element) {
var status=false;
var request = $.ajax( {
url : "url/fromwhich/youget/jsondata",
type : "GET",
dataType : "json",
success : function(data) {
for (var key in data) {
if(key == "id") {
if(data[key] == value) {
status=true;
} else {
status=false;
}
}
}
},
async: false
});
return status;
},"Invalid data");
$("#tambahbarang").validate($params);
});

I cannot get my jqGrid to populate

Edited with working code as per Mark's answer below.
I'm really starting to loath MVC. I have been trying all day to get a simple grid to work, but I'm having more luck banging a hole in my desk with my head.
I'm trying to implement a search page that displays the results in a grid. There are 3 drop-down lists that the user can use to select search criteria. They must select at least one.
After they have searched, the user will be able to select which records they want to export. So I will need to include checkboxes in the resulting grid. That's a future headache.
Using JqGrid and Search form - ASP.NET MVC as a reference I have been able to get the grid to appear on the page (a major achievement). But I can't get the data to populate.
BTW, jqGrid 4.4.4 - jQuery Grid
here is my view:
#model Models.ExportDatex
<script type="text/javascript">
$(document).ready(function () {
$('#btnSearch').click(function (e) {
var selectedSchool = $('#ddlSchool').children('option').filter(':selected').text();
var selectedStudent = $('#ddlStudent').children('option').filter(':selected').text();
var selectedYear = $('#ddlYear').children('option').filter(':selected').text();
var selectedOption = $('#exportOption_1').is(':checked');
if (selectedSchool == '' && selectedStudent == '' && selectedYear == '') {
alert('Please specify your export criteria.');
return false;
}
selectedSchool = (selectedSchool == '') ? ' ' : selectedSchool;
selectedStudent = (selectedStudent == '') ? ' ' : selectedStudent;
selectedYear = (selectedYear == '') ? ' ' : selectedYear;
var extraQueryParameters = {
school: selectedSchool,
student: selectedStudent,
year: selectedYear,
option: selectedOption
};
$('#searchResults').jqGrid({
datatype: 'json',
viewrecords: true,
url: '#Url.Action("GridData")?' + $.param(extraQueryParameters),
pager: '#searchResultPager',
colNames: ['SchoolID', 'Student Name', 'Student ID', 'Apprenticeship', 'Result'],
colModel: [
{ name: 'SchoolID' },
{ name: 'Student Name' },
{ name: 'StudentID' },
{ name: 'Apprenticeship' },
{ name: 'Result' }]
}).trigger('reloadGrid', [{ page: 1 }]);
});
});
</script>
#using (Html.BeginForm("Index", "Datex", FormMethod.Post))
{
<h2>Export to Datex</h2>
<div class="exportOption">
<span>
#Html.RadioButtonFor(model => model.ExportOption, "true", new { id = "exportOption_1" })
<label for="exportOption_1">VET Results</label>
</span>
<span>
#Html.RadioButtonFor(model => model.ExportOption, "false", new { id = "exportOption_0" })
<label for="exportOption_0">VET Qualifications</label>
</span>
</div>
<div class="exportSelectionCriteria">
<p>Please specify the criteria you want to export data for:</p>
<table>
<tr>
<td>School:</td>
<td>#Html.DropDownListFor(model => model.SchoolID, Model.Schools, new { id = "ddlSchool" })</td>
</tr>
<tr>
<td>Student: </td>
<td>#Html.DropDownListFor(model => model.StudentID, Model.Students, new { id = "ddlStudent" })</td>
</tr>
<tr>
<td>Year Completed:
</td>
<td>
#Html.DropDownListFor(model => model.YearCompleted, Model.Years, new { id = "ddlYear" })
</td>
</tr>
</table>
<table id="searchResults"></table>
<div id="searchResultPager"></div>
</div>
<div class="exportSearch">
<input type="button" value="Search" id="btnSearch" />
<input type="submit" value="Export" id="btnExport" />
</div>
}
Here is my Controller. As we don't have a database yet, I am just hardcoding some results while using an existing table from a different DB to provide record IDs.
[HttpGet]
public JsonResult GridData(string sidx, string sord, int? page, int? rows, string school, string student, string year, string option)
{
using (SMIDbContainer db = new SMIDbContainer())
{
var ds = (from sch in db.SCHOOLs
where sch.Active.HasValue
&& !sch.Active.Value
&& sch.LEVEL_9_ORGANISATION_ID > 0
select sch).ToList();
var jsonData = new
{
total = 1,
page = 1,
records = ds.Count.ToString(),
rows = (
from tempItem in ds
select new
{
cell = new string[]{
tempItem.LEVEL_9_ORGANISATION_ID.ToString(),
tempItem.SCHOOL_PRINCIPAL,
"40161",
"No",
"Passed (20)"
}
}).ToArray()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
}
Is the JSON you are passing back to the grid valid? Are you passing back the information that the jqGrid needs? Why setup your jqGrid inside of an ajax call instead of inside your $(document).ready(function () { ?
Here is an example of the portion of code I use to format my json for jqGrid:
var jsonData = new
{
total = (totalRecords + rows - 1) / rows,
page = page,
records = totalRecords,
rows = (
from tempItem in pagedQuery
select new
{
cell = new string[] {
tempItem.value1,
tempItem.value2, ........
}
}).ToArray()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
If you want to make your user search first, you can on the client side, set the jqgrid datatype: local and leave out the url. Then after your user does whatever you want them to do you can have the jqGrid go out and fetch the data, via something like:
$('#gridName').jqGrid('setGridParam', { datatype: 'json', url: '/Controller/getGridDataAction' }).trigger('reloadGrid', [{ page: 1}]);
If you want to pass in the search data, or other values to the controller/action that is providing the data to the jqGrid you can pass it via the postData: option in the jqGrid. To set that before going out you can set it via the setGridParam option as shown above via postData: { keyName: pairData}.
MVC and jqGrid work great...there are a ton of examples on stackoverflow and Oleg's answers are a vast resource on exactly what you are trying to do. No hole in desk via head banging required!

Knockout dropdown form binding

I have a html form for adding multiple addresses:
http://i48.tinypic.com/jg2ruo.png
This way If I change the Address Type selection the entire form has to bind to the correct json address object:
var jsonAddresses = { Addresses:
[
{ AddressType: 1, Address1: "", Address2: "",Province:"",City:"",State:"",PostalCode:"",Municipal:"" },
{ AddressType: 2, Address1: "", Address2: "",Province:"",City:"",State:"",PostalCode:"",Municipal:"" },
{ AddressType: 3, Address1: "", Address2: "",Province:"",City:"",State:"",PostalCode:"",Municipal:"" },
{ AddressType: 4, Address1: "", Address2: "",Province:"",City:"",State:"",PostalCode:"",Municipal:"" }
]
};
I have done this with Jquery with a lot of code actually but I want to know how can I do this with Knockout. The idea is instead of having a fixed json object with the 4 types of addresses i want to have only 1 json object and if I select an address type that is not in the array then the object is added and binded, if the address type already exists in the array then just bind it. then i can have a "remove" link that when clicked the selected address type object is removed from the array.
Thanks in advance.
I'm guessing a little bit on this, because its not entirely clear. You want a single form for editing addresses, with a dropdown that lets you select which address you are editing. I've put a working fiddle together, but here are the important parts.
You have a concept of an Address object, which is observable since you will be updating the values. Then you need a viewmodel to keep track of all the address, have some concepted of the selected address, and the ability to add new addresses. This is the part that wasn't clear, so I just went with a New Address button. Let me know if you have something else in mind. Other than the list of states, and the initial address data (both which should come from the server) this is all the code, and as you can see knockout makes it pretty simple.
HTML:
<select data-bind="options: addresses, optionsText: 'typeName', value: selectedAddress"></select>
<div data-bind="with: selectedAddress">
Name: <input data-bind="value: typeName" /></br>
Line1: <input data-bind="value: address1" /></br>
Line2: <input data-bind="value: address2" /></br>
City: <input data-bind="value: city" /></br>
State: <select data-bind="options: $parent.states, value: state"></select></br>
Postal Code: <input data-bind="value: postalCode" />
</div>
<button data-bind="click: addAddress">New Address</button>
<button data-bind="click: deleteAddress">Remove Address</button>
​
ViewModels:
var Address = function(address) {
this.id = ko.observable(address.AddressType || 0);
this.typeName = ko.observable(address.TypeName || '');
this.address1 = ko.observable(address.Address1 || '');
this.address2 = ko.observable(address.Address2 || '');
this.city = ko.observable(address.City || '');
this.state = ko.observable(address.State || '');
this.postalCode = ko.observable(address.PostalCode || '');
};
var App = function(addressList) {
var self = this;
self.states = states;
self.addresses = ko.observableArray(ko.utils.arrayMap(addressList,
function(i) { return new Address(i); }
));
self.selectedAddress = ko.observable(self.addresses[0]);
self.addAddress = function() {
var newId = self.addresses()[self.addresses().length -1].id + 1;
var newAddress = new Address({AddressType: newId});
self.addresses.push(newAddress);
self.selectedAddress(newAddress);
};
self.deleteAddress = function() {
self.addresses.remove(self.selectedAddress());
self.selectedAddress(self.addresses[0]);
};
};
EDIT1: added remove button. This is for the demo, obviously you will want some safety logic when the array is empty.