Collect Leaflet (draw created data) properties Attribute from a popup to feature properties - leaflet

I have read helpful answered given By #ghybs Page: “update properties of geojson to use it with leaflet”
but I am stuck to make it wok using a bootstrap popup window too collect data from user and hold it on feature.properties later I will collect multiple data from multiple marker, polygon polyline convert to geojson.
I can collect data form popup but data is showing same for every marker I am creating. feature.properties should different for each markers.
pelase review my code :
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '© OpenStreetMap contributors',
osm = L.tileLayer(osmUrl, {
maxZoom: 18,
attribution: osmAttrib
});
map = L.map('map', {
layers: [osm],
center: [31.9500, 35.9333],
zoom: 15
});
var editableLayers = L.geoJson().addTo(map);
map.addControl(new L.Control.Draw({
edit: {
featureGroup: editableLayers
}
}));
map.on('draw:created', function (e) {
var layer = e.layer,
feature = layer.feature = layer.feature || {};
feature.type = feature.type || "Feature";
var props = feature.properties = feature.properties || {};
//layer.feature = {properties: {}}; // No need to convert to GeoJSON.
//var props = layer.feature.properties;
props.action = null;
editableLayers.addLayer(layer);
addPopup(layer);
});
function addPopup(layer) {
var content = document.getElementById('action');
layer.feature.properties.action = content;
/* content.addEventListener("keyup", function () {
layer.feature.properties.action = content;
});*/
/* layer.on("popupopen", function () {
content.value = layer.feature.properties.desc;
content.focus();
});
layer.bindPopup(content).openPopup();*/
layer.on('click', function() {
$('#action').val(layer.feature.properties.action);
//content.value = layer.feature.properties.action;
$('#attributes').modal({'show' : true, backdrop:'static', keyboard:false});
$('#action').val(layer.feature.properties.action);
});
}
document.getElementById("convert").addEventListener("click", function () {
console.log(JSON.stringify(editableLayers.toGeoJSON(), null, 2));
});
#map {
height: 500px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script type="text/javascript" src="https://unpkg.com/leaflet#0.7.7/dist/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet#0.7.7/dist/leaflet.css" type="text/css">
<link rel="stylesheet" href="https://cdn.rawgit.com/Leaflet/Leaflet.draw/v0.3.0/dist/leaflet.draw.css" type="text/css">
<!--js-->
<script type="text/javascript" src="https://cdn.rawgit.com/Leaflet/Leaflet.draw/v0.3.0/dist/leaflet.draw-src.js"></script>
<body>
<div id="map"></div>
<button id="convert">
Get all features to GeoJSON string
</button>
<div class="modal" id="attributes">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Attribute Data</h4>
</div>
<div class="modal-body">
<div class="content-scroll5">
<div class="col-xs-2">
<label for="ex1">ACTION</label>
<input class="form-control" name="action" id="actin" type="text">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">OK</button>
</div>
</div>
</div>
</div>

Related

Open popup after flyTo in Leaflet?

I have a Leaflet-map with a layer containing markers with popups using bindPopup. I written this function that flies to the next marker onclick:
const makeFlyTo = () => {
const button = document.getElementById("next");
L.DomEvent.on(button, "click", function(e) {
if (currentView === data.length) {
currentView = 0;
}
map.flyTo(
[data[currentView].lat, data[currentView].lng],
{ zoom },
{
animate: true,
duration: 3
}
);
currentView++;
});
};
I would be nice if the popup opened up on "arrival". Any idea how this can be done?
We have to open marker first and they use FlyTo.
marker.openPopup();
map.flyTo([value['lat'], value['lng']], 15);
As mentioned in the comments, If we use flyTo and then open Popup then most of the times the view adjustment made by popup in map is incorrect.
map.panTo([value['lat'], value['lng']], 15).on('zoomend', () => { setTimeout(()=>marker.openPopup(), 3000) })
OR
map.panTo([value['lat'], value['lng']], 15).on('zoomend', () => marker.openPopup())
Example -
var map = L.map("map").setView([46.76336, -71.32453], 16);
var OpenStreetMap_Mapnik = L.tileLayer(
"http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
{
maxZoom: 19,
attribution:
'© OpenStreetMap'
}
).addTo(map);
function addRowTable(marker, code, coords) {
var tr = document.createElement("tr");
var td = document.createElement("td");
td.textContent = code;
tr.appendChild(td);
tr.onclick = () => {
marker.openPopup();
map.flyTo([value['lat'], value['lng']], 15);
};
document.getElementById("t_points").appendChild(tr);
}
function addMarker(code, lat, lng) {
var marker = L.marker([lat, lng]);
marker.title = code;
marker.bindPopup(code);
marker.addTo(map);
addRowTable(marker, code, [lat, lng]);
}
$(document).ready(function () {
var points = [
["M02KM262", 46.76336, -71.32453],
["M10KM052", 46.76186, -71.32247],
["83KM081", 46.76489, -71.32664],
["83KM082", 46.76672, -71.32919]
];
for (var i = 0; i < points.length; i++) {
addMarker(points[i][0], points[i][1], points[i][2]);
}
});
html, body, .full, #map{
margin: 0;
padding:0;
height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/leaflet#1.0.3/dist/leaflet.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/leaflet#1.0.3/dist/leaflet.css" rel="stylesheet"/>
<div class="row full">
<div class="col col-sm-3 full" style="overflow: auto;">
<h3 >List</h3>
<table class="table table-bordered">
<thead>
<tr>
<th>Codes</th>
</tr>
</thead>
<tbody id="t_points"></tbody>
</table>
</div>
<div id="map" class="col col-sm-9 full"></div>
</div>

How to select only one layer through a Select (leaflet)

I'm trying to select only one layer using a select.
This is my first time with leaflet and I use this great guide: https://maptimeboston.github.io/leaflet-intro/
Now I have on my map the part of "markerclusters" and the last one "heatmap" and I want to choose only one layer or the other or both.. I've been looking for examples but I don't know how to apply them.
(The select right now is ornamental as you can see.)
And here's the image of my map:
Image
here is my code, thanks in advance!
<html>
<head>
<title>MAPA PRUEBA</title>
<link rel="stylesheet" href="leaflet.css"/>
<link rel="stylesheet" href="MarkerCluster.css"/>
<script src="leaflet.js"></script>
<script src="leaflet.markercluster.js"></script>
<script src="leaflet.heat.js"></script>
<script src="jquery-2.1.1.min.js"></script>
<style>
#map{ height: 100% }
</style>
</head>
<body>
<div id="panel" style = "top-left:10px; background-color:#D3D3D3;">
<label>Tipo de mapa</label>
<select id="estilo" onchange="Bordeaux()">
<option value="points" >PUNTOS</option>
<option value="heat">MAPA DE CALOR</option>
</select>
<div id="map"></div>
<script>
// initialize the map
var map = L.map('map').setView([40.46, -3.74], 4);
// load a tile layer
L.tileLayer('http://192.168.0.103/osm/{z}/{x}/{y}.png',
{
attribution: 'Tiles by MAPC, Data by MassGIS',
maxZoom: 17,
minZoom: 1
}).addTo(map);
// load GeoJSON from an external file
$.getJSON("rodents.geojson",function(data){
var ratIcon = L.icon({
iconUrl: 'images/marker-icon.png'});
var rodents = L.geoJson(data,{
pointToLayer: function(feature,latlng){
var marker = L.marker(latlng,{icon: ratIcon});
marker.bindPopup(feature.properties.Location);
return marker;}
});
var clusters = L.markerClusterGroup();
clusters.addLayer(rodents);
map.addLayer(clusters);
});
// HEAT LAYER
$.getJSON("rodents.geojson",function(data){
var locations = data.features.map(function(rat) {
// the heatmap plugin wants an array of each location
var location = rat.geometry.coordinates.reverse();
location.push(1.9);
return location;
});
var heat = L.heatLayer(locations, { radius: 50 });
map.addLayer(heat);
});
</script>
</body>
</html>
change the clusters and heat variable to global. And add only one of both layers to the map.
var clusters, heat;
$.getJSON("rodents.geojson",function(data){
// ...
clusters = L.markerClusterGroup();
clusters.addLayer(rodents);
map.addLayer(clusters);
});
// HEAT LAYER
$.getJSON("rodents.geojson",function(data){
//...
heat = L.heatLayer(locations, { radius: 50 });
});
Then add / remove the layers in your onchange function from the select:
<select id="estilo" onchange="changeFnc(this)">
<option value="points" >PUNTOS</option>
<option value="heat">MAPA DE CALOR</option>
</select>
function changeFnc(obj){
var value = obj.value;
if(value == "points"){
if(map.hasLayer(clusters)){
map.removeLayer(clusters);
}
map.addLayer(points);
}else{
if(map.hasLayer(points)){
map.removeLayer(points);
}
map.addLayer(clusters);
}
}
PS: This code is not tested but should work

Google chart vAxis format not displaying % properly

please find my code to have google column chart is here:
<html>
<head>
<title>Test</title>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css'>
<link href='https://fonts.googleapis.com/css?family=Roboto:400,400i,500,700&display=swap' rel='stylesheet'>
<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>
</head>
<body>
<!-- detail section start -->
<section class='section-padding'>
<div class='container'>
<div class='row'>
<div class='col-12'>
<div>
<h2 class='text-blue main-title'>Current v/s Previous year selling</h2>
</div>
</div>
</div>
<div class='row'>
<div class='col-lg-12'>
<!-- start:: Chart card -->
<div class='card-blk chart-card d-flex flex-column'>
<div class='card flex-grow-3'>
<div class='card-title'>
<h6 class='text-center'>
Customers
</h6>
</div>
<div class='card-content text-center'>
<div id='chartElement3'>
<script type='text/javascript'>
google.charts.load('current', {'packages':['corechart', 'controls']});
google.charts.setOnLoadCallback(drawElement3Dashboard);
function drawElement3Dashboard() {
var data = new google.visualization.DataTable();
data.addColumn('string','customer_profile_value');
data.addColumn('number','Current Turnover');
data.addColumn({type:'string', role:'annotation'});
data.addColumn('number','Last year Turnover');
data.addColumn({type:'string', role:'annotation'});
data.addRows([['A+',19.9, '19.9%', 18.2, '18.2%'],['A',5.5, '5.5%', 5.4, '5.4%'],['B',2.4, '2.4%', 2.3, '2.3%'],['C',1.0, '1.0%', 1.0, '1.0%'],['C-',0.3, '0.3%', 0.3, '0.3%']]);
var dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_div_3'));
var controller = new google.visualization.ControlWrapper({'controlType': 'NumberRangeFilter','containerId': 'filter_div_3','options': {'filterColumnLabel':'Current Turnover'}});
var colChart = new google.visualization.ChartWrapper({'chartType': 'ColumnChart','containerId': 'chart_div_3',
'options': {
'height': 150,
'annotations': {'alwaysOutside': 'null','highContrast': 'true','textStyle': {'fontName': 'Times-Roman','fontSize': 9,'color': '#000000','opacity': 1}},
'legend' :{'position' :'bottom','alignment' :'center','element_legend_text' :'',},
'colors' :['#65A1DD','#9FC2EA','#919191','#CBCBCB','#E0E0E0','#717171','#C2D8F1'],
'enableInteractivity' :'true',
'forceIFrame' :'false',
'reverseCategories' :'false',
'tooltip' :'',
'slices' :'10',
'animation': { 'duration' :'2000',
'easing' :'linear',
'startup' :'true',
'alwaysOutside' :'',},
'bar': { 'groupWidth' :'61.8%',},
'hAxis': { 'direction':'1','format' :'short','gridlines': { 'count' :'-1','units' :'',},'logScale ':'false','scaleType' :'','textPosition' :'out','title' :'',},
'isStacked' :'false',
'orientation' :'horizontal',
'vAxis': { 'direction' :'1','format' :'#,###%','gridlines': { 'count' :'4','units' :'',},'logScale' :'false','scaleType' :'','textPosition' :'out','title' :'','viewWindow':{'min':'0',}}
}});
dashboard.bind(controller, colChart);
dashboard.draw(data);
}
</script>
<div id='dashboard_div_3'>
<div id='filter_div_3' style='display: none;'></div>
<div id='chart_div_3'>
</div>
</div>
</div>
</div>
</div>
<h6 class='card-subtitle'>
User: Company name
</h6>
</div>
<!-- end:: Chart card -->
</div>
</div>
<!-- Start:: Copyright and page text -->
<div class='row mt-auto pt-3'>
<div class='col-12'>
<div class='copyright-text d-flex justify-content-between'>
<span>Company Name</span>
<span>Page 1</span>
</div>
</div>
</div>
<!-- End:: Copyright and page text -->
</div>
</section>
<!-- detail section end -->
</body>
</html>
my working HTML chart is here
I want to display labels on vAxis as 0%, 5%, 10% up to 20%. So as per Google visualization documentation, I specified vAxis:{format:'#,###%'}
but now it started showing labels like 0%, 500%, 1000%, 1500% and 2000% as you can see in my code above.
Can anyone suggest me the correct way?
the format option assumes the number is already a percent
19.9 = 1,990%
if you want to use the format option,
the values will need to be divided by 100
0.199 = 19.9%
otherwise, you can use custom ticks for the y-axis.
we can provide the value and the formatted value of the tick using object notation.
{v: 20, f: '20%'}
we can also build them dynamically, using data table method --> getColumnRange(colIndex)
// build y-axis ticks
var formatPercent = new google.visualization.NumberFormat({
pattern: '#,##0%'
});
var range = data.getColumnRange(1);
var ticks = [];
for (var i = Math.floor(range.min); i <= Math.ceil(range.max); i = i + 5) {
addTick(i);
}
function addTick(y) {
ticks.push({v: y, f: formatPercent.formatValue(y / 100)});
}
see following working snippet...
google.charts.load('current', {'packages':['corechart', 'controls']});
google.charts.setOnLoadCallback(drawElement3Dashboard);
function drawElement3Dashboard() {
var data = new google.visualization.DataTable();
data.addColumn('string','customer_profile_value');
data.addColumn('number','Current Turnover');
data.addColumn({type:'string', role:'annotation'});
data.addColumn('number','Last year Turnover');
data.addColumn({type:'string', role:'annotation'});
data.addRows([['A+',19.9, '19.9%', 18.2, '18.2%'],['A',5.5, '5.5%', 5.4, '5.4%'],['B',2.4, '2.4%', 2.3, '2.3%'],['C',1.0, '1.0%', 1.0, '1.0%'],['C-',0.3, '0.3%', 0.3, '0.3%']]);
// build y-axis ticks
var formatPercent = new google.visualization.NumberFormat({
pattern: '#,##0%'
});
var range = data.getColumnRange(1);
var ticks = [];
for (var i = Math.floor(range.min); i <= Math.ceil(range.max); i = i + 5) {
addTick(i);
}
function addTick(y) {
ticks.push({v: y, f: formatPercent.formatValue(y / 100)});
}
var dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_div_3'));
var controller = new google.visualization.ControlWrapper({'controlType': 'NumberRangeFilter','containerId': 'filter_div_3','options': {'filterColumnLabel':'Current Turnover'}});
var colChart = new google.visualization.ChartWrapper({
'chartType': 'ColumnChart',
'containerId': 'chart_div_3',
'options': {
'height': 150,
'annotations': {'alwaysOutside': 'null','highContrast': 'true','textStyle': {'fontName': 'Times-Roman','fontSize': 9,'color': '#000000','opacity': 1}},
'legend' :{'position' :'bottom','alignment' :'center','element_legend_text' :'',},
'colors' :['#65A1DD','#9FC2EA','#919191','#CBCBCB','#E0E0E0','#717171','#C2D8F1'],
'enableInteractivity' :'true',
'forceIFrame' :'false',
'reverseCategories' :'false',
'tooltip' :'',
'slices' :'10',
'animation': { 'duration' :'2000',
'easing' :'linear',
'startup' :'true',
'alwaysOutside' :'',},
'bar': { 'groupWidth' :'61.8%',},
'hAxis': { 'direction':'1','format' :'short','gridlines': { 'count' :'-1','units' :'',},'logScale ':'false','scaleType' :'','textPosition' :'out','title' :'',},
'isStacked' :'false',
'orientation' :'horizontal',
'vAxis': { 'direction' :'1','ticks' :ticks,'gridlines': { 'count' :'4','units' :'',},'logScale' :'false','scaleType' :'','textPosition' :'out','title' :'','viewWindow':{'min':'0',}}
}});
dashboard.bind(controller, colChart);
dashboard.draw(data);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="dashboard_div_3">
<div id="filter_div_3"></div>
<div id="chart_div_3"></div>
</div>

Owl Curasol Not working in my date picker

Hi I have Date picker on select Date Month & Year it will show all Date in that Moth it working Fine
Now I want to add a Slider On that so that i used Owl Curasol after adding Curasol Date picker Stopped Working.
My Full code
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js"></script>
<link rel="stylesheet" type="text/css" media="screen" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/base/jquery-ui.css">
<link rel="stylesheet" type="text/css" href="http://www.jqueryscript.net/demo/Powerful-Customizable-jQuery-Carousel-Slider-OWL-Carousel/owl-carousel/owl.carousel.css">
<script type="text/javascript">
var weekday = new Array(7);
weekday[0] = "Sunday";
weekday[1] = "Monday";
weekday[2] = "Tuesday";
weekday[3] = "Wednesday";
weekday[4] = "Thursday";
weekday[5] = "Friday";
weekday[6] = "Saturday";
$(function() {
$('.date-picker').datepicker( {
changeMonth: true,
changeYear: true,
showButtonPanel: true,
dateFormat: 'MM yy',
minDate: 0,
onClose: function(dateText, inst) {
$d = new Date(inst.selectedYear, parseInt(inst.selectedMonth)+1, 0).getDate();
$(this).datepicker('setDate', new Date(inst.selectedYear, inst.selectedMonth, 1));
html='';
for(i=1;i<=$d;i++){
console.log(inst.selectedYear+'-'+(parseInt(inst.selectedMonth)+1)+'-'+i);
d = new Date(inst.selectedYear+'-'+(parseInt(inst.selectedMonth)+1)+'-'+i);
console.log(d);
n = weekday[d.getDay()];
html += '<div class="datediv">div-'+i+'<br>'+n+'</div>';
}
$('#datecontent').html(html);
}
});
$(document).ready(function() {
$(document).live('click', '.datediv', function() { alert("hello"); });});
});
</script>
Html Code
<label for="startDate">Date :</label>
<input name="startDate" id="startDate" class="date-picker" />
<div id="datecontent" id="owl-demo">
</div>
Owl Script
<script src="http://www.jqueryscript.net/demo/Powerful-Customizable-jQuery-Carousel-Slider-OWL-Carousel/assets/js/jquery-1.9.1.min.js"></script>
<script src="http://www.jqueryscript.net/demo/Powerful-Customizable-jQuery-Carousel-Slider-OWL-Carousel/owl-carousel/owl.carousel.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#owl-demo").owlCarousel({
items : 10, //10 items above 1000px browser width
itemsDesktop : [1000,5], //5 items between 1000px and 901px
itemsDesktopSmall : [900,3], // betweem 900px and 601px
itemsTablet: [600,2], //2 items between 600 and 0;
itemsMobile : false // itemsMobile disabled - inherit from itemsTablet option
});
});
</script>
I got This error TypeError: $(...).datepicker is not a function
How to fix this issue. I think because of Jquery Conflict Only
How to over come on this??
Hope this helps!
You should use the add method in carousel to append items inside carousel.Also use refresh to run the slider after appending.
owl.trigger('add.owl.carousel', ['<div class="datediv">div-'+i+'<br>'+n+'</div>']).trigger('refresh.owl.carousel');
use remove method to remove items from carousel before appending new items.
for (var i =0; i< $('.owl-item').length; i++) {
owl.trigger('remove.owl.carousel', [i]).trigger('refresh.owl.carousel');
}
$(document).ready(function() {
var weekday = new Array(7);
weekday[0] = "Sunday";
weekday[1] = "Monday";
weekday[2] = "Tuesday";
weekday[3] = "Wednesday";
weekday[4] = "Thursday";
weekday[5] = "Friday";
weekday[6] = "Saturday";
$('.date-picker').datepicker( {
changeMonth: true,
changeYear: true,
showButtonPanel: true,
dateFormat: 'MM yy',
minDate: 0,
onClose: function(dateText, inst) {
$d = new Date(inst.selectedYear, parseInt(inst.selectedMonth)+1, 0).getDate();
$(this).datepicker('setDate', new Date(inst.selectedYear, inst.selectedMonth, 1));
for (var i =0; i< $('.owl-item').length; i++) {
owl.trigger('remove.owl.carousel', [i]).trigger('refresh.owl.carousel');
}
for(i=1;i<=$d;i++){
console.log(inst.selectedYear+'-'+(parseInt(inst.selectedMonth)+1)+'-'+i);
d = new Date(inst.selectedYear+'-'+(parseInt(inst.selectedMonth)+1)+'-'+i);
console.log(d);
n = weekday[d.getDay()];
owl
.trigger('add.owl.carousel', ['<div class="datediv">div-'+i+'<br>'+n+'</div>'])
.trigger('refresh.owl.carousel');
}
}
});
$(document).on('click', '.datediv', function() { alert("hello"); });
var owl = $(".owl-demo");
owl.owlCarousel({
margin: 20,
items : 10, //10 items above 1000px browser width
itemsDesktop : [1000,5], //5 items between 1000px and 901px
itemsDesktopSmall : [900,3], // betweem 900px and 601px
itemsTablet: [600,2], //2 items between 600 and 0;
itemsMobile : false // itemsMobile disabled - inherit from itemsTablet option
});
});
.owl-item {
-webkit-tap-highlight-color: transparent;
position: relative;
min-height: 1px;
float: left;
-webkit-backface-visibility: hidden;
-webkit-touch-callout: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.0.0-beta.3/owl.carousel.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.0.0-beta.3/assets/owl.theme.default.min.css" rel="stylesheet"/>
<link href="https://owlcarousel2.github.io/OwlCarousel2/assets/owlcarousel/assets/owl.carousel.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.0.0-beta.3/assets/owl.theme.default.min.css" rel="stylesheet"/>
<label for="startDate">Date :</label>
<input name="startDate" id="startDate" class="date-picker" />
<div id="datecontent" class="owl-demo">
</div>

Error submitting form data using knockout and mvc

#model NewDemoApp.Models.DemoViewModel
#{
ViewBag.Title = "Home Page";
}
#*<script src="#Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>*#
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="#Url.Content("~/Scripts/knockout-3.3.0.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script type="text/javascript">
var viewModel;
var compViewModel, userViewModel;
$(document).ready(function () {
$(".wizard-step:visible").hide();
$(".wizard-step:first").show(); // show first step
$("#back-step").hide();
var result = #Html.Raw(Json.Encode(Model));
var viewModel = new DemoViewModel(result.userViewModel);
//viewModel.userViewModel.FirstName = result.userViewModel.FirstName;
//viewModel.userViewModel.LastName = result.userViewModel.LastName;
//viewModel.userViewModel.State = result.userViewModel.State;
//viewModel.userViewModel.City = result.userViewModel.City;
ko.applyBindings(viewModel);
});
var userVM = function(){
FirstName = ko.observable(),
LastName = ko.observable(),
State = ko.observable(),
City = ko.observable()
};
function DemoViewModel(data) {
var self = this;
self.userViewModel = function UserViewModel(data) {
userVM.FirstName = data.FirstName;
userVM.LastName = data.LastName;
userVM.State = data.State;
userVM.City = data.City;
}
self.Next = function () {
var $step = $(".wizard-step:visible"); // get current step
var form = $("#myFrm");
var validator = $("#myFrm").validate(); // obtain validator
var anyError = false;
$step.find("input").each(function () {
if (!validator.element(this)) { // validate every input element inside this step
anyError = true;
}
});
if (anyError)
return false; // exit if any error found
if ($step.next().hasClass("confirm")) { // is it confirmation?
$step.hide().prev(); // hide the current step
$step.next().show(); // show the next step
$("#back-step").show();
$("#next-step").hide();
//$("#myFrm").submit();
// show confirmation asynchronously
//$.post("/wizard/confirm", $("#myFrm").serialize(), function (r) {
// // inject response in confirmation step
// $(".wizard-step.confirm").html(r);
//});
}
else {
if ($step.next().hasClass("wizard-step")) { // is there any next step?
$step.hide().next().fadeIn(); // show it and hide current step
$("#back-step").show(); // recall to show backStep button
$("#next-step").show();
}
}
}
self.Back = function () {
var $step = $(".wizard-step:visible"); // get current step
if ($step.prev().hasClass("wizard-step")) { // is there any previous step?
$step.hide().prev().fadeIn(); // show it and hide current step
// disable backstep button?
if (!$step.prev().prev().hasClass("wizard-step")) {
$("#back-step").hide();
$("#next-step").show();
}
else {
$("#back-step").show();
$("#next-step").show();
}
}
}
self.SubmitForm = function (formElement) {
$.ajax({
url: '#Url.Content("~/Complaint/Save")',
type: "POST",
data: ko.toJS(self),
done: function (result) {
var newDiv = $(document.createElement("div"));
newDiv.html(result);
},
fail: function (err) {
alert(err);
},
always: function (data) {
alert(data);
}
});
}
self.loadData = function () {
$.get({
url: '#Url.Content("~/Complaint/ViewComplaint")',
done: function (data) {
debugger;
alert(data);
self.compViewModel(data);
self.userViewModel(data);
},
fail: function (err) {
debugger;
alert(err);
},
always: function (data) {
debugger;
alert(data);
}
});
}
}
</script>
<form class="form-horizontal" role="form" id="myFrm">
<div class="container">
<div class="row">
<div class="col-md-3">
</div>
<div class="col-md-6">
<div class="wizard-step">
</div>
<div class="wizard-step" >
<h3> Step 2</h3>
#Html.Partial("UserView", Model.userViewModel)
<div class="col-md-3"></div>
<div class="col-md-6">
<input type="submit" id="submitButton" class="btn btn-default btn-success" value="Submit" data-bind="click: SubmitForm" />
</div>
<div class="col-md-3"></div>
</div>
<div class="wizard-step">
<h3> Step 3</h3>
</div>
<div class="wizard-step confirm">
<h3> Final Step 4</h3>
</div>
</div>
<div class="col-md-3"></div>
</div>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<input type="button" id="back-step" class="btn btn-default btn-success" value="< Back" data-bind="click: Back" />
<input type="button" id="next-step" class="btn btn-default btn-success" value="Next >" data-bind="click: Next" />
</div>
<div class="col-md-3"></div>
</div>
</div>
</form>
I am able to get the data from controller and bind it using knockout. There is a partial view that loads data from controller. But when submitting the updated data, I do not get the data that was updated, instead getting error that "FirstName" property could not be accessed from null reference. I just need to get pointers where I am going wrong especially the right way to create ViewModels and use them.
When you are submitting the form in self.SubmitForm function you are passing Json object which is converted from Knockout view model.
So make sure you are providing the data-bind attributes in all input tags properly. If you are using Razor syntax then use data_bind in Html attributes of input tags.
Check 2-way binding of KO is working fine. I can't be sure as you have not shared your partial view Razor code.
Refer-
http://knockoutjs.com/documentation/value-binding.html
In Chrome you can see what data you are submitting in Network tab of javascript developer console. The Json data that you are posting and ViewModel data structure you are expecting in controller method should match.
You can also change the parameters to expect FormCollection formCollection and check what data is coming from browser when you are posting.