I'm trying to retrieve my email messages from an Outlook account through Javascript.
I use ADAL to authenticate myself and that seems to work. However if I try to use the returned token in a GET request to the outlook REST API I get an unauthorised error back with following additional info in the header:
x-ms-diagnostics:2000001;reason="This token profile 'V1IdToken' is not applicable for the current protocol.";error_category="invalid_token"
My request looks like:
function test2(token) {
try
{
var xhr = new XMLHttpRequest();
xhr.open("GET", 'https://outlook.office.com/api/v1.0/me/messages');
// The APIs require an OAuth access token in the Authorization header, formatted like this: 'Authorization: Bearer <token>'.
xhr.setRequestHeader("Authorization", "Bearer " + token);
// Process the response from the API.
xhr.onload = function () {
// ...
}
// Make request.
xhr.send();
I both tried v1.0 and v2.0 REST api.
Based on the error message you were request the Outlook online REST using the id_token. The id_token is only used for the client to authenticate users. To request the Outlook online REST, we need to use the access token.
And if you were developing with angularJS, there is no need to append the token manually. Here is a code sample for your reference:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.13/js/adal.min.js"></script>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.13/js/adal-angular.min.js"></script>
<script src="//unpkg.com/angular-ui-router/release/angular-ui-router.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-route.js"></script>
<div ng-app="myApp">
<div> Hello {{ userInfo.userName }}</div>
<div ng-controller="homeCtrl">
<ul class="nav navbar-nav navbar-right">
<li><a ng-click="getMessages()">Get Messages</a></li>
<li><a class="btn btn-link" ng-show="userInfo.isAuthenticated" ng-click="logout()">Logout</a></li>
<li><a class="btn btn-link" ng-hide="userInfo.isAuthenticated" ng-click="login()">Login</a></li>
</ul>
<div>
<table class="table table-striped">
<tbody>
<tr data-ng-repeat="item in messages">
<td>
<p>{{item.Subject}}</p>
</td>
<td>
<p>{{item.Sender.EmailAddress.Address}}</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
var myApp = angular.module('myApp', ['AdalAngular'])
.config(['$httpProvider', 'adalAuthenticationServiceProvider', function ($httpProvider, adalProvider) {
var endpoints = {
"https://outlook.office.com": "https://outlook.office.com",
};
adalProvider.init(
{
instance: 'https://login.microsoftonline.com/',
tenant: '{yourTenant}.onmicrosoft.com',
clientId: '{yourAppId}',
extraQueryParameter: 'nux=1',
endpoints: endpoints,
},
$httpProvider
);
}])
myApp.controller('homeCtrl', ['$scope', '$http', 'adalAuthenticationService', '$location', 'toGetMessagesSvc', function ($scope, $http, adalService, $location, toGetMessagesSvc) {
$scope.login = function () {
adalService.login();
};
$scope.logout = function () {
adalService.logOut();
};
$scope.getMessages = function () {
toGetMessagesSvc.getMessages().success(function (results) {
$scope.messages = results.value;
$scope.loadingMessage = "";
});
}
}]);
myApp.factory('toGetMessagesSvc', ['$http', function ($http) {
var apiEndpoint = "https://outlook.office.com/";
$http.defaults.useXDomain = true;
delete $http.defaults.headers.common['X-Requested-With'];
return {
getMessages: function () {
return $http.get(apiEndpoint + 'api/v1.0/me/messages');
}
};
}]);
</script>
Related
Adding custom script into my cart.html file in bigcommerce to include a script that will redirect card holder to google.com when they click checkout (only going to google for now while testing script)
When the script is loaded I see the following error in the console (self.checkoutButton.on is not a function)
Here is the script + file
cart: true
<script>
document.addEventListener("DOMContentLoaded", function () {
var debug = true ? console.log.bind(console, '[DEBUG][Cart]') : function () {};
debug('Script loaded');
window.Cart = function (options) {
var self = {}
function init() {
self.options = Object.assign({
checkoutButtonSelector: document.getElementById("checkout"),
checkoutUrl: 'https://google.com',
}, options);
self.checkoutButton = (self.options.checkoutButtonSelector);
debug('Initialized with options', self.options);
inject();
}
function inject() {
debug('Inject');
self.checkoutButton.on('click', checkout);
}
function checkout(event) {
var checkoutUrl = getCheckoutURL(self.options.products);
debug('Checkout ->', checkoutUrl);
event.preventDefault();
window.location.href = checkoutUrl;
}
function getCartCookie(name) {
var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
if (match){
return match[2];
}
}
function getCheckoutURL(products) {
cookie = getCartCookie('cart');
var urlLineItems = Object.keys(products).reduce(function (output, productId) {
var quantity = products[productId];
return output.concat([ productId + ':' + quantity ]);
}, []).join(';');
return self.options.checkoutUrl + '?products=' + urlLineItems + '&cartId='+cookie;
}
init();
return self;
};
var instance = new Cart();
});
</script>
<div class="page">
<main class="page-content" data-cart>
{{> components/common/breadcrumbs breadcrumbs=breadcrumbs}}
{{> components/cart/page-title}}
<div data-cart-status>
{{> components/cart/status-messages}}
</div>
{{#if cart.items.length}}
<div class="loadingOverlay"></div>
<div data-cart-content class="cart-content-padding-right">
{{> components/cart/content}}
</div>
<div data-cart-totals class="cart-content-padding-right">
{{> components/cart/totals}}
</div>
{{#if cart.show_primary_checkout_button}}
<div class="cart-actions cart-content-padding-right">
<a class="button button--primary" id='checkout' title="{{lang 'cart.checkout.title'}}">{{lang 'cart.checkout.button'}}</a>
{{#if cart.show_multiple_address_shipping}}
<a class="checkoutMultiple" href="{{urls.checkout.multiple_address}}">
{{lang 'cart.preview.checkout_multiple'}}
</a>
{{/if}}
</div>
{{else}}
<div class="cart-actions cart-content-padding-right">
<a class="button" href="{{urls.home}}" title="{{lang 'cart.continue_shopping'}}">{{lang 'cart.continue_shopping'}}</a>
</div>
{{/if}}
{{#if cart.additional_checkout_buttons}}
<div class="cart-additionalCheckoutButtons cart-content-padding-right">
{{#each cart.additional_checkout_buttons}}
{{{this}}}
{{/each}}
</div>
{{/if}}
{{else}}
<h3 tabindex="0">{{lang 'cart.checkout.empty_cart'}}</h3>
{{/if}}
</main>
</div>
{{/partial}}
{{> layout/base}}
Would you have any idea why I would be getting the following error? Thanks in advance
You are using .on which is a JQuery function. You’re not using JQuery to wrap your selector, you’re just using the vanilla JS getElementById. You need to use a vanilla JS function to add the event, such as addEventListener.
I'm a bit new to ReactJS,
I've created a simple form and table, each time I hit the submit the state of the table change with the new data.
I'm using tornado as a backend server and mongodb as my DB.
I'm looking for a way to send the same JSON to the server on a second pipe (without rerendering the page again.)
How can I do it?
---- edit - react component -----
import Re
act from 'react';
export default class Reblaze_Dashboard extends React.Component {
constructor(props) {
super(props);
this.onNewUser = this.onNewUser.bind(this);
this.state = {
data: window.obj,
};
}
onNewUser(e) {
e.preventDefault();
const formData = {};
for (const field in this.refs) {
formData[field] = this.refs[field].value;
}
this.state.data.push(formData);
this.setState({
data: this.state.data,
});
}
render() {
return(
<div>
<h2>Dashboard page</h2>
<form onSubmit={this.onNewUser}>
<div className="form-group">
<label htmlFor="first-name-input">First name:</label>
<input type="text" className="form-control" ref="firstname" id="first-name-input" />
</div>
<div className="form-group">
<label htmlFor="last-name-input">Last name:</label>
<input type="text" className="form-control" ref="lastname" id="last-name-input" />
</div>
<input type="submit" value="Submit" className="btn btn-primary pull-right" />
</form>
<br /><br />
<div className="table-responsive">
<table className="table table-striped table-hover">
<thead>
<tr>
<td><h4>First name</h4></td>
<td><h4>Last name</h4></td>
</tr>
</thead>
<tbody>
{this.state.data.map((line, key) =>
<tr key={key}>
<td>{line.firstname}</td>
<td>{line.lastname}</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
)
}
}
You're submitting form data and immediately updating table without checking if your data reaches the server. This way it is possible to update user view while failing to update the database.
I strongly recommend to use some promise-based HTTP-client like Axios (https://github.com/mzabriskie/axios). Send some request, then wait for the promise resolve, and only after that update your component state.
You need something like this:
import React from 'react';
import axios from 'axios';
/*...*/
onNewUser(e) {
e.preventDefault();
const formData = {};
for (const field in this.refs) {
formData[field] = this.refs[field].value;
}
axios.post('yourApiUrl', formData)
// if request is sent and status is ok, update form and send second request
.then((res) => {
this.state.data.push(formData);
this.setState({
data: this.state.data,
});
return axios.post('yourApiUrl', formData);
})
// if second request is ok, receive a notification
.then((res) => {
console.log('second request is ok');
})
// if there is an error, receive a notification
.catch((err) => {
console.log(err);
})
}
/*...*/
Ok, this is related to the question I asked a short while ago: Silverstripe/PHP/jQuery - Once form has been filled out by user, prevent it from automatically appearing for each visit
Something has changed since then. Per request of the client, the form must not automatically appear if the user has already filled it out and has thus been placed into SharpSpring. Originally, I was creating a cookie on successful form submission using JavaScript. However, the latest concern is that it's not effective enough as cookies are registered only to certain devices and browsers, and users can clear their cookies at any time.
Essentially, the desired result is to prevent the form from automatically appearing if the user has been registered in SharpSpring (a separate domain) without having to rely on cookies.
Has anyone ever attempted something like this, checking to see if a user has submitted a form to another domain?
For reference, here is the form code I have setup:
<?php
/*
Plugin Name: SharpSpring Form Plugin
Description: A custom form plugin that is SharpSpring-compatible and uses HTML, CSS, jQuery, and AJAX
Version: 1.0
*/
define('SSCFURL', WP_PLUGIN_URL . "/" . dirname(plugin_basename(__FILE__)));
define('SSCFPATH', WP_PLUGIN_DIR . "/" . dirname(plugin_basename(__FILE__)));
function sharpspringform_enqueuescripts()
{
wp_enqueue_script('jquery-src', SSCFURL . '/js/jquery.js', array('jquery'));
wp_enqueue_script('jquery-ui', SSCFURL . '/js/jquery-ui.js', array('jquery'));
wp_enqueue_script('boootstrap', SSCFURL . '/js/bootstrap.js', array('jquery'));
wp_localize_script('sharpspringform', 'sharpspringformajax', array('ajaxurl' => admin_url('admin-ajax.php')));
}
add_action('wp_enqueue_scripts', 'sharpspringform_enqueuescripts');
function sharpspringform_show_form()
{
wp_enqueue_style( 'boilerplate', SSCFURL.'/css/boilerplate.css');
wp_enqueue_style( 'bootstrapcss', SSCFURL.'/css/bootstrap.css');
wp_enqueue_style( 'bookregistration', SSCFURL.'/css/Book-Registration.css');
wp_enqueue_style( 'formstyles', SSCFURL.'/css/styles.css');
?>
<div class="mobile-view" style="right: 51px;">
<a class="mobile-btn">
<span class="glyphicon glyphicon-arrow-left icon-arrow-mobile mobile-form-btn"></span>
</a>
</div>
<div class="slider register-photo">
<div class="form-inner">
<div class="form-container">
<form method="post" enctype="multipart/form-data" class="signupForm" id="browserHangFormPV">
<a class="sidebar">
<span class="glyphicon glyphicon-arrow-left icon-arrow arrow"></span>
</a>
<a class="closeBtn">
<span class="glyphicon glyphicon-remove"></span>
</a>
<h2 class="text-center black">Sign up for our newsletter.</h2>
<p class="errors-container light">Please fill in the required fields.</p>
<div class="success">Thank you for signing up!</div>
<div class="form-field-content">
<div class="form-group">
<input class="form-control FirstNameTxt" type="text" name="first_name" placeholder="*First Name"
autofocus="">
</div>
<div class="form-group">
<input class="form-control LastNameTxt" type="text" name="last_name" placeholder="*Last Name"
autofocus="">
</div>
<div class="form-group">
<input class="form-control EmailTxt" type="email" name="email" placeholder="*Email"
autofocus="">
</div>
<div class="form-group">
<input class="form-control CompanyTxt" type="text" name="company" placeholder="*Company"
autofocus="">
</div>
<div class="form-group submit-button">
<button class="btn btn-primary btn-block button-submit" type="button">SIGN ME UP</button>
<img src="/wp-content/plugins/sharpspring-form/img/ajax-loader.gif" class="progress" alt="Submitting...">
</div>
</div>
<br/>
<div class="privacy-link">
<a href="[privacy policy link]" class="already" target="_blank"><span
class="glyphicon glyphicon-lock icon-lock"></span>We will never share your information.</a>
</div>
</form>
<input type="hidden" id="gatewayEmbedID" value="<?php echo get_option( 'pv_signup_sharpspring_ID' ); ?>" />
<script type="text/javascript">
var embedID = document.getElementById("gatewayEmbedID").value;
var __ss_noform = __ss_noform || [];
__ss_noform.push(['baseURI', 'https://app-3QNAHNE212.marketingautomation.services/webforms/receivePostback/[redacted]']);
__ss_noform.push(['form', 'browserHangFormPV', embedID]);
__ss_noform.push(['submitType', 'manual']);
</script>
<script type="text/javascript" src="https://koi-3QNAHNE212.marketingautomation.services/client/noform.js?ver=1.24" ></script>
</div>
</div>
</div>
<?php
}
function sharpspringform_shortcode_func( $atts )
{
ob_start();
sharpspringform_show_form();
$output = ob_get_contents();
ob_end_clean();
return $output;
}
add_shortcode( 'sharpspringform', 'sharpspringform_shortcode_func' );
The form submission code with generates a cookie using JS:
;
(function ($) {
$(document).ready(function () {
var successMessage = $('.success');
var error = $('.errors-container');
var sharpSpringID = $('#gatewayEmbedID').val();
var submitbtn = $('.button-submit');
var SubmitProgress = $('img.progress');
var formdata = {};
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
submitbtn.click(function (e) {
resetErrors();
postForm();
});
function resetErrors() {
$('.signupForm input').removeClass('error-field');
}
function postForm() {
$.each($('.signupForm input'), function (i, v) {
if (v.type !== 'submit') {
formdata[v.name] = v.value;
}
});
submitbtn.hide();
error.hide();
SubmitProgress.show();
$.ajax({
type: "POST",
data: formdata,
url: '/wp-content/plugins/sharpspring-form/sharpsring-form-submission.php',
dataType: "json"
}).done(function (response) {
submitbtn.show();
SubmitProgress.hide();
if (response.errors) {
error.show();
var errors = response.errors;
errors.forEach(function (error) {
$('input[name="' + error + '"]').addClass('error-field');
})
}
else {
__ss_noform.push(['submit', null, sharpSpringID]);
setCookie('SignupSuccess', 'NewsletterSignup', 3650);
$('#browserHangFormPV')[0].reset();
$('.form-field-content').hide();
successMessage.show();
$('.button-submit').html("Submitted");
}
});
}
});
}(jQuery));
The jQuery code that sets up the form sliding animation and popup feature, as well as checks for the existence of the JS cookie created on successful form submit:
jQuery.noConflict();
(function ($) {
$(document).ready(function () {
//This function checks if we are in mobile view or not to determine the
//UI behavior of the form.
checkCookie();
window.onload = checkWindowSize();
var arrowicon = $(".arrow");
var overlay = $("#overlay");
var slidingDiv = $(".slider");
var closeBtn = $(".closeBtn");
var mobileBtn = $(".mobile-btn");
//When the page loads, check the screen size.
//If the screen size is less than 768px, you want to get the function
//that opens the form as a popup in the center of the screen
//Otherwise, you want it to be a slide-out animation from the right side
function checkWindowSize() {
if ($(window).width() <= 768) {
//get function to open form at center of screen
if(sessionStorage["PopupShown"] != 'yes' && !checkCookie()){
setTimeout(formModal, 5000);
function formModal() {
slidingDiv.addClass("showForm")
overlay.addClass("showOverlay");
overlay.removeClass('hideOverlay');
mobileBtn.addClass("hideBtn");
}
}
}
else {
//when we aren't in mobile view, let's just have the form slide out from the right
if(sessionStorage["PopupShown"] != 'yes' && !checkCookie()){
setTimeout(slideOut, 5000);
function slideOut() {
slidingDiv.animate({'right': '-20px'}).addClass('open');
arrowicon.addClass("glyphicon-arrow-right");
arrowicon.removeClass("glyphicon-arrow-left");
overlay.addClass("showOverlay");
overlay.removeClass("hideOverlay");
}
}
}
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
function checkCookie() {
var user = getCookie("SignupSuccess");
if (user != "") {
return true;
} else {
return false;
}
}
/*
------------------------------------------------------------
Functions to open/close form like a modal in center of screen in mobile view
------------------------------------------------------------
*/
mobileBtn.click(function () {
slidingDiv.addClass("showForm");
slidingDiv.removeClass("hideForm");
overlay.addClass("showOverlay");
overlay.removeClass('hideOverlay');
mobileBtn.addClass("hideBtn");
});
closeBtn.click(function () {
slidingDiv.addClass("hideForm");
slidingDiv.removeClass("showForm");
overlay.removeClass("showOverlay");
overlay.addClass("hideOverlay")
mobileBtn.removeClass("hideBtn");
sessionStorage["PopupShown"] = 'yes'; //Save in the sessionStorage if the modal has been shown
});
/*
------------------------------------------------------------
Function to slide the sidebar form out/in
------------------------------------------------------------
*/
arrowicon.click(function () {
if (slidingDiv.hasClass('open')) {
slidingDiv.animate({'right': '-390px'}, 200).removeClass('open');
arrowicon.addClass("glyphicon-arrow-left");
arrowicon.removeClass("glyphicon-arrow-right");
overlay.removeClass("showOverlay");
overlay.addClass("hideOverlay");
sessionStorage["PopupShown"] = 'yes'; //Save in the sessionStorage if the modal has been shown
} else {
slidingDiv.animate({'right': '-20px'}, 200).addClass('open');
arrowicon.addClass("glyphicon-arrow-right");
arrowicon.removeClass("glyphicon-arrow-left");
overlay.addClass("showOverlay");
overlay.removeClass("hideOverlay");
}
});
});
}(jQuery));
I'm confused by the WordPress code here, rather than SilverStripe, it's not clear in your question which platform you're using.
Basically, if you want something more robust than cookies, you'll need to store the registration in the database and check it there (assuming the registration form is on your site). This means you handle the form submission on your site, send the data to the remote site and check the responses, and if all goes well, save the fact that the user has registered remotely into your database where you can check when deciding whether to show the form or not, next time.
If you don't have access to the registration form, or you want to also notice registrations made independently of your site, then you need to have an API you can query on the remote site in order to see if the user is registered.
I found a sharpspring API, but I'm not sure if it's relevant.
I am super new to React and quite new to Meteor.
I am doing a Meteor.call to a function ('getTheThing'). That function is fetching some information and returns the information as a response. In my browser I can see that the method is returning the correct information (a string), but how do I get that response into the DOM?
(As you can see, I have tried to place it in the DOM with the use of ReactDOM.findDOMNode(this.refs.result).html(response);, but then I get this error in my console: Exception in delivering result of invoking 'getTheThing': TypeError: Cannot read property 'result' of undefined)
App = React.createClass({
findTheThing(event) {
event.preventDefault();
var username = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
Meteor.call("getTheThing", username, function(error, response){
console.log(response);
ReactDOM.findDOMNode(this.refs.result).html(response);
});
ReactDOM.findDOMNode(this.refs.textInput).value = "";
},
render(){
return(
<div className="row">
<div className="col-xs-12">
<div className="landing-container">
<form className="username" onSubmit={this.findTheThing} >
<input
type="text"
ref="textInput"
placeholder="what's your username?"
/>
</form>
</div>
<div ref="result">
</div>
</div>
</div>
);
}
});
this is under the different context, thus does not contain the refs there. Also, you cannot set html for the Dom Element. You need to change into Jquery element
var _this = this;
Meteor.call("getTheThing", username, function(error, response){
console.log(response);
$(ReactDOM.findDOMNode(_this.refs.result)).html(response);
});
Though i recommend you to set the response into the state and let the component re-rendered
For a complete React way
App = React.createClass({
getInitialState() {
return { result: "" };
},
shouldComponentUpdate (nextProps: any, nextState: any): boolean {
return (nextState['result'] !== this.state['result']);
},
findTheThing(event) {
event.preventDefault();
var username = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
Meteor.call("getTheThing", username, function(error, response){
console.log(response);
_this.setState({ result: response });
});
ReactDOM.findDOMNode(this.refs.textInput).value = "";
},
render(){
return(
<div className="row">
<div className="col-xs-12">
<div className="landing-container">
<form className="username" onSubmit={this.findTheThing} >
<input
type="text"
ref="textInput"
placeholder="what's your username?"
/>
</form>
</div>
<div ref="result">{this.state['result']}</div>
</div>
</div>
</div>
);
}
});
#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.