When making an ajax request in JavaScript, it appears that the view has received a post of raw data into web page, i would say this is like looking at a csv file in notepad, just lines and lines of data, albeit the correct data, just not in the jquery datatable i would expect to see.
When just using Jquery to display all the data correctly in the table like so:
<script>
$('#IndexTable').DataTable({ responsive: true });
</script>
There is no issue.
I have also tried this in the view:
<script>
$(document).ready(function () {
test();
})
var test = function () {
$ajax({
type: 'GET',
url: '/ClinicalASSPATVM/test',
success: function (response) {
BindDataTable(response);
}
})
}
var BindDataTable = function (response) {
$("#IndexTable").DataTable({
"aadata": response,
"aoColumn": [
{"mData": "Product:"},
]
});
}
</script>
This is my Controller portion of the code:
public ActionResult test(int? ClinicalAssetID)
{
var ClinicalASSPATVM = (from s in db.ClinicalAssets
join cp in db.ClinicalPATs on s.ClinicalAssetID equals cp.ClinicalAssetID into AP
from subpat in AP.DefaultIfEmpty()
orderby s.ClinicalAssetID descending
select new ClinicalASSPATVM
{
ClinicalAssetID = s.ClinicalAssetID,
ProductName = s.ProductName,
ModelName = s.ModelName,
SupplierName = s.SupplierName,
ManufacturerName = s.ManufacturerName,
SerialNo = s.SerialNo,
PurchaseDate = s.PurchaseDate,
PoNo = s.PoNo,
Costing = s.Costing,
TeamName = s.TeamName,
StaffName = s.StaffName,
InspectionDocumnets = subpat.InspectionDocumnets ?? String.Empty,
InspectionOutcomeResult = subpat.InspectionOutcomeResult
});
return Json(new { data = ClinicalASSPATVM }, JsonRequestBehavior.AllowGet);
}
This is the View:
#model IEnumerable<Assets.Areas.Clinical.Models.ClinicalASSPATVM>
<link href="~/content/datatables/media/css/datatables.bootstrap.min.css" rel="stylesheet" />
<link href="~/Content/DataTables/extensions/Responsive/css/responsive.dataTables.min.css" rel="stylesheet" />
#{
ViewBag.Title = "View Clinical";
}
<div class="pageheader">
<h2><i class="fa fa-pencil"></i> Clinical Asset Dashboard</h2>
<div class="breadcrumb-wrapper">
<span class="label">You are here:</span>
<ol class="breadcrumb">
<li>Asset Management System</li>
<li class="active">Clinical Assets - Overview</li>
</ol>
</div>
</div>
<div class="contentpanel">
<div class="panel panel-default">
<div class="panel-heading">
<div class="panel-btns">
−
</div><!-- panel-btns -->
<h3 class="panel-title">Last 10 Assets:</h3>
</div>
<div class="panel-body">
<div class="table-responsive">
<table id="IndexTable" class="table table-striped table-bordered" >
<thead>
<tr>
<th>Product:</th>
<th>Model:</th>
<th>Supplier:</th>
<th>Manufacturer:</th>
<th>Serial No:</th>
<th>Purchase Date:</th>
<th>Purchase Order No:</th>
<th>Cost:</th>
<th>Team Location:</th>
<th>Owner:</th>
<th>Inspection OutcomeResult:</th>
</tr>
</thead>
</table>
</div><!-- table-responsive -->
</div><!-- panel-body -->
</div><!-- panel -->
</div>
#section Scripts {
<script src="~/Scripts/DataTables/media/js/jquery.dataTables.min.js"></script>
<script src="~/Scripts/DataTables/media/js/dataTables.bootstrap.min.js"></script>
<script src="~/Scripts/DataTables/extensions/Responsive/js/dataTables.responsive.js"></script>
<script>
$(document).ready(function () {
$ajax({
url: '/ClinicalASSPATVM/test',
method: 'GET',
datatype: 'json',
sucess: function (data) {
$('#IndexTable').datatable({
data: data,
columns: [
{ "data": "ProductName" },
]
});
}
});
});
</script>
}
This is the crazy output example, also i see no layout page headers, footers or side bars.
{"data":
[{"ClinicalAssetID":75,"SerialNo":"34563463453453453","PurchaseDate":"\/Date(1551657600000)\/","PoNo":"567","Costing":1500,"InspectionDocumnets":"","ProductName":{"ProductID":2,"AssetAssignmentID":2,"ProductName":"Bed"},"InspectionOutcomeResult":null,"ModelName":{"ModelID":1,"AssetAssignmentID":2,"ProductID":8,"ModelName":"M1"},"Code":null,"AssetTypeName":null,"ManufacturerName":{"ManufacturerID":1,"AssetAssignmentID":2,"ProductID":8,"ManufacturerName":"Omron"},"StaffName":{"StaffID":1,"StaffName":
I Hope someone can shed some light on what i have done wrong.
Related
I want to render ejs using the forEach loop inside the script tag but I get product is not defined error. I cant pass variable into ejs.render() function correctly.
Here is my ejs template for product card:
<div class="card mb-3" style="max-width: 540px">
<div class="row no-gutters">
<div class="col-md-4">
<img
src="/images/<%= product.image_groups[0].images[0].link%>"
class="card-img"
alt="<%= product.image_groups[0].images[0].alt%>"
/>
</div>
<div class="col-md-8">
<div class="card-body d-flex flex-column">
<h5 class="card-title"><%= product.name %></h5>
<p class="card-text">
<%- product.short_description %>
</p>
<div class="card-footer bg-transparent border-dark mt-auto">
<div class="row">
<div class="col sm-6">
<p>Price:<%= product.currency %> <%= product.price %></p>
</div>
<div class="col sm-6">
More Info
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Here is my script:
<script>
const form = document.querySelector("form");
form.addEventListener("submit", async (e) => {
e.preventDefault();
// get values
const productName = form.productname.value
const searchResultsEl = document.getElementById('searchResults')
try {
const res = await fetch("/search", {
method: "POST",
body: JSON.stringify({ productName }),
headers: { "Content-Type": "application/json" },
})
const foundProducts = await res.json()
foundProducts.data.forEach(product => {
let html = ejs.render("<%- include('../product/productCard.ejs') %>",{product:product})
searchResultsEl.innerHTML += html
})
} catch (err) {
console.log(err)
}
});
</script>
Error: product is not defined
I can print products by using console.log(product) so there are products. I cant figure out what is the problem. Any help?
Image of Error:
Try
let html = ejs.render('<%- include("../product/productCard.ejs") %>',{product:product})
I'm trying to implement FineUploader React library into my React app to upload files to my Azure Blob Storage.
Looks like for some reason, FineUploader is getting the blob storage URI wrong.
This is how I instanciate a FineUploader in my test component:
import React, { Component } from 'react';
import FineUploaderAzure from 'fine-uploader-wrappers/azure'
import Gallery from './gallery/index';
const uploader = new FineUploaderAzure({
options: {
cors: {
expected: true,
sendCredentials: true
},
signature: {
endpoint: 'https://myapp.com/api/getsas'
},
request: {
endpoint: 'https://myaccount.blob.core.windows.net/test-container'
},
uploadSuccess: {
endpoint: 'https://myapp.com/api/done'
}
}
})
class Index extends Component {
render() {
return (
<Gallery uploader={uploader} />
)
}
}
export default Index;
Here's the error I'm seeing in the console. Looks like FineUploader is using the wrong URI for the blob storage.
Any idea what may be causing this?
UPDATE:
As per #GauravMantri's suggestion, I changed endpoint to containerUrl in the options section but that didn't seem to help either. Here's what it looks like:
const uploader = new FineUploaderAzure({
options: {
cors: {
expected: true,
sendCredentials: true
},
signature: {
endpoint: 'https://myapp.com/api/getsas'
},
request: {
containerUrl: 'https://myaccount.blob.core.windows.net/test-container'
},
uploadSuccess: {
endpoint: 'https://myapp.com/api/done'
}
}
})
Here's the SAS I'm getting when I send a request through Postman:
The request I'm sending is:
http://example.com/api/files/get/sas?blobUri=https://myaccount.blob.core.windows.net/test-container/test.txt&_method=put
And here's the SAS I receive:
"?sv=2017-04-17&sr=b&sig=7pXTnI2r8uGyZms12T9cRvHg1XlLI53ZJtwPUwGElnY%3D&st=2017-12-28T14%3A02%3A56Z&se=2017-12-28T14%3A22%3A56Z&sp=w"
I was able to make it work. Basically there're a few things that one need to keep in mind:
In your FineUploader config, you will need endpoint attribute and that should have the URL of the blob container where you want to upload. This is how configuration looks like in my code:
var uploader = new qq.azure.FineUploader({
debug: true,
element: document.getElementById("uploader"),
cors: {
expected: true,
sendCredentials: false
},
signature: {
endpoint: 'http://localhost:63194/users/sas'
},
request: {
endpoint: 'https://account-name.blob.core.windows.net/container-name'
},
})
The API for getting Shared Access Signature (SAS) should return blob URL + SAS Token. The blobUrl parameter to the API should be the absolute URL of the blob. This is the code I used for API (please don't use this as is because the code below does not take into consideration the _method parameter):
[Route("sas")]
[HttpGet]
public async Task<HttpResponseMessage> Sas(string blobUri)
{
var credentials = new StorageCredentials("account-name", "account-key");
var blob = new CloudBlockBlob(new Uri(blobUri), credentials);
var sasParameters = new SharedAccessBlobPolicy()
{
SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),
Permissions = SharedAccessBlobPermissions.Write
};
var sasToken = blob.GetSharedAccessSignature(sasParameters);
var returnValue = blob.Uri.AbsoluteUri + sasToken;
var resp = new HttpResponseMessage(HttpStatusCode.OK);
resp.Content = new StringContent(returnValue, System.Text.Encoding.UTF8, "text/plain");
return resp;
}
I downloaded Fine Uploader Azure related files from here: https://fineuploader.com/customize.html and used it to create a simple HTML page to test it. Here's what my HTML page looks like:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="fine-uploader-gallery.min.css" rel="stylesheet">
<script src="azure.fine-uploader.min.js""></script>
<script type="text/template" id="qq-template">
<div class="qq-uploader-selector qq-uploader qq-gallery" qq-drop-area-text="Drop files here">
<div class="qq-total-progress-bar-container-selector qq-total-progress-bar-container">
<div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-total-progress-bar-selector qq-progress-bar qq-total-progress-bar"></div>
</div>
<div class="qq-upload-drop-area-selector qq-upload-drop-area" qq-hide-dropzone>
<span class="qq-upload-drop-area-text-selector"></span>
</div>
<div class="qq-upload-button-selector qq-upload-button">
<div>Upload a file</div>
</div>
<span class="qq-drop-processing-selector qq-drop-processing">
<span>Processing dropped files...</span>
<span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span>
</span>
<ul class="qq-upload-list-selector qq-upload-list" role="region" aria-live="polite" aria-relevant="additions removals">
<li>
<span role="status" class="qq-upload-status-text-selector qq-upload-status-text"></span>
<div class="qq-progress-bar-container-selector qq-progress-bar-container">
<div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-progress-bar-selector qq-progress-bar"></div>
</div>
<span class="qq-upload-spinner-selector qq-upload-spinner"></span>
<div class="qq-thumbnail-wrapper">
<img class="qq-thumbnail-selector" qq-max-size="120" qq-server-scale>
</div>
<button type="button" class="qq-upload-cancel-selector qq-upload-cancel">X</button>
<button type="button" class="qq-upload-retry-selector qq-upload-retry">
<span class="qq-btn qq-retry-icon" aria-label="Retry"></span>
Retry
</button>
<div class="qq-file-info">
<div class="qq-file-name">
<span class="qq-upload-file-selector qq-upload-file"></span>
<span class="qq-edit-filename-icon-selector qq-btn qq-edit-filename-icon" aria-label="Edit filename"></span>
</div>
<input class="qq-edit-filename-selector qq-edit-filename" tabindex="0" type="text">
<span class="qq-upload-size-selector qq-upload-size"></span>
<button type="button" class="qq-btn qq-upload-delete-selector qq-upload-delete">
<span class="qq-btn qq-delete-icon" aria-label="Delete"></span>
</button>
<button type="button" class="qq-btn qq-upload-pause-selector qq-upload-pause">
<span class="qq-btn qq-pause-icon" aria-label="Pause"></span>
</button>
<button type="button" class="qq-btn qq-upload-continue-selector qq-upload-continue">
<span class="qq-btn qq-continue-icon" aria-label="Continue"></span>
</button>
</div>
</li>
</ul>
<dialog class="qq-alert-dialog-selector">
<div class="qq-dialog-message-selector"></div>
<div class="qq-dialog-buttons">
<button type="button" class="qq-cancel-button-selector">Close</button>
</div>
</dialog>
<dialog class="qq-confirm-dialog-selector">
<div class="qq-dialog-message-selector"></div>
<div class="qq-dialog-buttons">
<button type="button" class="qq-cancel-button-selector">No</button>
<button type="button" class="qq-ok-button-selector">Yes</button>
</div>
</dialog>
<dialog class="qq-prompt-dialog-selector">
<div class="qq-dialog-message-selector"></div>
<input type="text">
<div class="qq-dialog-buttons">
<button type="button" class="qq-cancel-button-selector">Cancel</button>
<button type="button" class="qq-ok-button-selector">Ok</button>
</div>
</dialog>
</div>
</script>
<title>Fine Uploader Gallery UI</title>
</head>
<body>
<div id="uploader"></div>
<script>
// Some options to pass to the uploader are discussed on the next page
var uploader = new qq.azure.FineUploader({
debug: true,
element: document.getElementById("uploader"),
cors: {
expected: true,
sendCredentials: false
},
signature: {
endpoint: 'http://localhost:63194/users/sas'
},
request: {
endpoint: 'https://account-name.blob.core.windows.net/container-name'
},
})
</script>
</body>
</html>
Once I ran this code, I was able to upload files in my blob container without any problems.
I have some code that I wrote to essentially handle a login with a back end API supporting it.
My problem is that after the form POSTS the data to the server (and it is accepted), the index page (which is the same place this form is located on) still shows the login form instead of the different if statement I have in the file.
BUT, if I refresh the page, the correct part of the if statement displays. I have been at this all day, and I need a fresh pair of eyes to look over it and see what I'm doing something wrong:
define([
'lodash',
'log4js',
'path',
'when',
'common/lang/Disposable',
'common/commands/CommandHandler',
'common-node/network/http/Verb',
'common-node/network/server/endpoints/html/PageContainer',
'common-node/network/server/endpoints/html/PageEndpoint',
'common-node/network/server/ServerDefinition',
'common-node/network/server/ServerFactory.instance',
], function(_, log4js, path, when, Disposable, CommandHandler, Verb, PageContainer, PageEndpoint, ServerDefinition, serverFactory) {
//'use strict';
var m;
var session = require('client-sessions');
session({
cookieName: 'GBE_OWNED',
secret: '12112asdfasdf',
duration: 30 * 60 * 1000,
activeDuration: 5 * 60 * 1000,
});
var logger = log4js.getLogger('server/GbeSeasonalsWebServer');
var GbeSeasonalsWebServer = Disposable.extend({
init: function() {
},
start: function(configuration) {
var port = getContainerPort(configuration.container);
var state = false;
var indexHandler = new CommandHandler.fromFunction(function(input) {
console.log(input || { });
if(input.logout == ''){
session.qry = '';
session.Name = '';
return {
name: '',
currentState: false,
reloadLogout: true
};
}
if(typeof session.qry === 'undefined' || session.qry === null || session.qry === ''){
//the user isn't logged in
state = false;
}
else{
console.log(session.qry);
state = true;
}
return {
name: session.Name,
currentState: state
};
});
var loginHandler = new CommandHandler.fromFunction(function(input) {
var userName = input.username;
var userPass = input.password;
var rememberMe = input.remember;
var retro = false;
var iResponse;
var productIDs = 'USC_SEASONAL_CB,USC_SEASONAL_CB'; // this will be changed when jose gets back to me with the specifics
var https = require('https');
var callback = function(response){
var str = '';
response.on('data', function(chunk){
str += chunk;
});
response.on('end', function () {
try{
var DOMParser = require('xmldom').DOMParser;
}catch(err){
return {error: "There appeared to be an error. Please try again."};
}
var doc = new DOMParser().parseFromString(str,'text/xml');
var isSuccessful = doc.firstChild.attributes[1].nodeValue;
if(isSuccessful == 'True'){
retro = true;
//I need to set a session here
console.log("The account was logged in successfully.");
session.qry = '?username=' + userName + '&password=' + userPass + '&productids=' + productIDs;
//console.log(session.username);
if(typeof str === 'undefined' || str === null){return {error: "There appeared to be an error. Please try again."};}
session.Name = doc.firstChild.attributes[4].nodeValue + ' ' + doc.firstChild.attributes[5].nodeValue;
var state = true;
iResponse = function (){
return {
name: session.Name,
currentState: true,
reload: true,
};
};
}
else{
iResponse = function (){
return {
error: "There appeared to be a problem while trying to log you in.",
name: "WHATTHEHELL",
state: false
};
};
}
return iResponse;
});
response.on('error', function (e) {
console.log(e);
});
};
return https.request('https://secure.barchart.com/banker/getuserpermissions.ashx?username=' + userName + '&password=' + userPass + '&productids=' + productIDs, callback).end();
});
var definition = ServerDefinition
.withContainer(
new PageContainer(port, '/')
.addEndpoint(new PageEndpoint(Verb.GET, '', 'index', indexHandler))
.addEndpoint(new PageEndpoint(Verb.POST, '', 'index', loginHandler))
.addEndpoint(new PageEndpoint(Verb.GET, '/index', 'index', indexHandler))
.addEndpoint(new PageEndpoint(Verb.POST, '/index', 'index', loginHandler))
.addEndpoint(new PageEndpoint(Verb.GET, '/signup', 'signup'))
.addEndpoint(new PageEndpoint(Verb.POST, '/signup', 'signup'))
.addEndpoint(new PageEndpoint(Verb.GET, '/more', 'more'))
);
definition.withTemplatePath(path.join(configuration.server.path, configuration.container.http.templatePath));
_.forEach(configuration.container.http.staticPaths, function(filePath, serverPath) {
definition.withStaticPath(path.join(configuration.server.path, filePath), serverPath);
});
return when.try(function() {
serverFactory.build(definition);
}).then(function() {
return true;
});
},
_onDispose: function() {
logger.warn('GBE Seasonals web server is being disposed');
},
toString: function() {
return '[GbeSeasonalsWebServer]';
}
});
function getContainerPort(containerConfiguration) {
var port = parseInt(process.env.PORT);
if (_.isNaN(port)) {
port = null;
}
return port || containerConfiguration.port || 8080;
}
return GbeSeasonalsWebServer;
});
And the index page:
<!DOCTYPE html>
<html lang="en">
<head>
<title>GBE SEASONALS STAGING SITE © Barchart™</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<!-- Custom theme -->
<link rel="stylesheet" type="text/css" href="static/css/custom.css">
</head>
<body>
<script type="text/javascript">
var c = '{{name}}';
var state = '{{currentState}}';
{{#if reload}}
window.location = window.location.href;
{{/if}}
{{#if reloadLogout}}
window.location = window.location.href.split("?")[0];
{{/if}}
if(state==''){window.location = window.location.href; }
</script>
<div class="container">
<div class = "row">
<div class="col-md-10 col-md-offset-1">
<a href="index">
<img style = "vertical-align:middle;display:inline-block" src="http://www.gbemembers.com/images/GBE_Logo_horiz.png" class="img-responsive">
<p style="color:white;margin-top:50px;margin-right:20px;font-size:20px" class="pull-right">Seasonals</p>
</a>
</div>
</div>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="panel panel-default">
<div class="panel-heading"></div>
<div class="panel-body">
<div class = "row">
{{#if error}}
<div class="alert alert-danger" role="alert">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class=""><b>Error:</b></span>
{{error}}
</div>
{{/if}}
{{#if currentState}}
<div class = "col-md-12">
<p class = "pull-right">Welcome back {{name}}! Click here to log out.</p>
</div>
{{else}}
<center>
<div class = "col-md-12">
<form class="form-inline" style = "width:100%;margin:0 auto;" method="post" action = "?"><label>Already a member? Login here: </label>
<div class="form-group">
<label class="sr-only" for="exampleInputEmail3">Email address</label>
<input type="" class="form-control" id="exampleInputEmail3" placeholder="Email" name = "username">
</div>
<div class="form-group">
<label class="sr-only" for="exampleInputPassword3">Password</label>
<input type="password" class="form-control" id="exampleInputPassword3" placeholder="Password" name = "password">
</div>
<div class="checkbox">
<label>
<input type="checkbox" name = "remember"> Remember me
</label>
</div>
<button type="submit" class="btn btn-default">Sign in</button>
<input type = "button" value = "Register" class="btn btn-default">
</form>
<p></p>
</div>
</center>
{{/if}}
</div>
<div class = "row">
<div class = "col-md-12">
<div class="jumbotron">
<h1>Hello, world!</h1>
<p>This is an example of a jumbotron....because you're worth it.</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div>
</div>
</div>
<div class = "row">
<div class = "col-md-12">
<p>
Freegan tacos before they sold out, health goth sriracha chartreuse kinfolk jean shorts man braid artisan literally. Brooklyn vice hashtag, meh tumblr kombucha marfa readymade. Ennui cold-pressed distillery freegan. Kale chips tilde +1, mumblecore franzen migas paleo. Offal 3 wolf moon before they sold out, health goth disrupt fixie bitters flannel meditation pop-up flexitarian irony meh. Deep v put a bird on it pork belly cardigan etsy. Lumbersexual literally crucifix slow-carb cardigan.
</p>
</div>
</div>
<div class = "row">
<div class = "col-md-12">
<p>Asymmetrical readymade brooklyn, blue bottle master cleanse disrupt artisan +1 actually affogato roof party DIY polaroid next level retro. Brooklyn poutine vegan bitters you probably haven't heard of them. Celiac helvetica master cleanse williamsburg, synth shabby chic fixie. Viral typewriter cred, roof party kombucha readymade offal shabby chic meggings. Gochujang chillwave VHS food truck. Ennui ugh twee, mumblecore sriracha DIY gastropub hella 3 wolf moon pabst kale chips typewriter trust fund direct trade. Neutra microdosing selfies listicle.</p>
</div>
</div>
<div class = "row">
<div class = "col-md-12">
<p>
Gochujang farm-to-table offal, distillery tofu migas skateboard 90's. Ethical ramps hoodie, YOLO vice before they sold out four loko literally mustache post-ironic. Fixie ennui literally lumbersexual photo booth umami disrupt messenger bag man braid polaroid. Cold-pressed aesthetic marfa, vinyl truffaut squid 3 wolf moon sriracha keytar knausgaard echo park. Chambray leggings microdosing mustache migas. Keytar portland chambray, quinoa ugh farm-to-table mustache cred mixtape craft beer. Thundercats chia keytar beard, drinking vinegar mustache man bun slow-carb wayfarers polaroid lo-fi chicharrones.
</p>
</div>
</div>
<div class = "row">
<div class = "col-md-12">
<p>
Quinoa cred taxidermy, cold-pressed microdosing offal mustache gluten-free small batch tousled twee wayfarers. Wolf williamsburg normcore lo-fi, tilde seitan hammock bushwick DIY organic single-origin coffee quinoa microdosing man braid. Fap small batch PBR&B microdosing, migas pork belly occupy aesthetic pop-up slow-carb 3 wolf moon. Blue bottle XOXO occupy +1, pabst lomo chicharrones ethical heirloom helvetica asymmetrical. Bushwick shabby chic yr kombucha, flannel truffaut raw denim banh mi bitters gluten-free pickled hoodie letterpress sartorial. YOLO whatever tacos meggings venmo, keytar knausgaard mumblecore. Tilde waistcoat offal, locavore cred umami mlkshk vice lomo lo-fi tousled selvage blog tattooed poutine.
</p>
</div>
</div>
<div class = "row">
<div class = "col-md-12">
<button type="button" class="btn btn-primary btn-lg btn-block">Click here to sign up!</button>
</div>
</div>
</div>
<div class = "panel-footer">GBE Seasonal © 2016 Barchart</div>
</div>
</div>
</div>
</div> <!-- /container -->
<!-- Let's load the javascript dendencies now -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
<script type="text/javascript" src = "http://getbootstrap.com/assets/js/ie10-viewport-bug-workaround.js"></script>
</body>
</html>
I believe it is a session issue, but I cannot seem to get around the issue. What's more, I cannot even get the callback function to return the needed data.
I know I've asked a lot of questions lately and for that I apologize, I'm in the process of learning the ins and outs of MVC. Anyways, when I select a quantity and hit add to cart it doesn't add the quantity I select, almost like it just grabs a random number and adds it. Here's the code for the view:
#model IEnumerable<AccessorizeForLess.ViewModels.DisplayProductsViewModel>
#{
ViewBag.Title = "Products > Necklaces";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<link href="~/Content/Site.css" rel="stylesheet" />
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
<link href="~/Content/jquery.fancybox.css?v=2.1.5" rel="stylesheet" />
<link href="~/Content/jquery.fancybox-buttons.css?v=1.0.5" rel="stylesheet" />
<link href="~/Content/jquery.fancybox-thumbs.css?v=1.0.7" rel="stylesheet" />
<h2>Products > Necklaces</h2>
<div id="update-message"></div>
<p class="button">
#Html.ActionLink("Create New", "Create")
</p>
#using (Html.BeginForm("AddToCart", "Orders", FormMethod.Post))
{
<div id="container">
<div class="scroll">
#foreach (var item in Model)
{
<div class="scroll">
<div class="itemcontainer">
<table>
<tr>
<td id="#item.Id">
<div class="DetailsLink"> #Html.ActionLink(#item.Name, "Details", new { id = item.Id })</div>
<br />
<div id="#item.Id"></div>
<div class="divPrice" id="#item.Price">#Html.DisplayFor(modelItem => item.Price)</div>
<div class="divImg"><a class="fancybox-thumbs" href="#item.Image.ImagePath" title="#item.Image.AltText" data-fancybox-group="thumb"><img src="#item.Image.ImagePath" alt="#item.Image.AltText" title="#item.Image.AltText" /></a></div>
<div> </div>
<div class="divQuantity"> Quantity: #Html.TextBoxFor(modelItem => item.Quantity, new { #id = "quantity", #style = "width:50px;", #class = "formTextBox" })</div>
<div class="divAddToCart">
<p class="button">
#Html.ActionLink("Add to cart", "AddToCart", "Orders", new { id = item.Id }, new { #class = "AddToCart" })
</p>
</div>
<div style="height:15px;"></div>
</td>
</tr>
</table>
</div>
</div>
}
<div class="button">#Html.ActionLink("Back To Categories","Categories")</div>
<br />
</div>
</div>
}
#section scripts {
<script src="~/Scripts/jQuery-jScroll.js"></script>
<script src="~/Scripts/jquery.fancybox.js?v=2.1.5"></script>
<script src="~/Scripts/jquery.fancybox-thumbs.js?v=1.0.7"></script>
<script src="~/Scripts/jquery.fancybox-buttons.js?v=1.0.5"></script>
<script type="text/javascript">
//$(function () {
// $('.scroll').jscroll({
// autoTrigger: true
// });
$('.fancybox-thumbs').fancybox({
prevEffect: 'none',
nextEffect: 'none',
closeBtn: true,
arrows: false,
nextClick: false
});
});
</script>
}
Here's the code for AddToCart in my controller:
// GET: /Orders/AddToCart/5
public ActionResult AddToCart(int id)
{
// Retrieve the product from the database
var productAdded = db.Products
.Single(p => p.ProductId == id);
// Add it to the shopping cart
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.AddToCart(productAdded);
// Go back to the main store page for more shopping
return RedirectToAction("Index");
}
And finally the code for AddToCart in my ShoppingCart model:
public void AddToCart(Product item)
{
// Get the matching cart and product instances
var order = entities.Orders.FirstOrDefault(
c => c.OrderGUID == ShoppingCartId
&& c.OrderItems.Where(p=>p.ProductId == item.ProductId).FirstOrDefault().ProductId == item.ProductId);
if (order == null)
{
// Create a new order since one doesn't already exist
order = new Order
{
InvoiceNumber = Guid.NewGuid().ToString(),
OrderDate=DateTime.Now,
OrderGUID = ShoppingCartId,
IsShipped = false
};
entities.Orders.Add(order);
// Save changes
entities.SaveChanges();
//add the OrderItem for the new order
OrderItem oi = new OrderItem()
{
OrderId = order.OrderId,
OrderGUID = ShoppingCartId,
ProductId = item.ProductId,
ProductQuantity = item.Quantity,
ProductPrice = item.ProductPrice
};
entities.OrderItems.Add(oi);
entities.SaveChanges();
}
else
{
// If the item does exist in the cart,
// then add one to the quantity
order.OrderItems.Where(p => p.ProductId == item.ProductId).FirstOrDefault().ProductQuantity++;
entities.SaveChanges();
}
}
Code for DisplayProductsViewModel:
using AccessorizeForLess.Data;
using System.ComponentModel.DataAnnotations;
namespace AccessorizeForLess.ViewModels
{
public class DisplayProductsViewModel
{
public int Id { get; set; }
public string Name { get; set; }
[DisplayFormat(DataFormatString = "{0:C}")]
public decimal? Price { get; set; }
public ProductImage Image { get; set; }
public int ProductQuantity { get; set; }
}
}
Can anyone possibly see what I'm doing wrong
EDIT
This is what I see when I use the debugging tools
EDIT
'Here is the new view with the submit button added
#model IEnumerable<AccessorizeForLess.ViewModels.DisplayProductsViewModel>
#{
ViewBag.Title = "Products > Necklaces";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<link href="~/Content/Site.css" rel="stylesheet" />
<link href="~/Content/jquery.fancybox.css?v=2.1.5" rel="stylesheet" />
<link href="~/Content/jquery.fancybox-buttons.css?v=1.0.5" rel="stylesheet" />
<link href="~/Content/jquery.fancybox-thumbs.css?v=1.0.7" rel="stylesheet" />
<h2>Products > Necklaces</h2>
<div id="update-message"></div>
<p class="button">
#Html.ActionLink("Create New", "Create")
</p>
#using (Html.BeginForm("AddToCart", "Orders", FormMethod.Post))
{
<div id="container">
<div class="scroll">
#foreach (var item in Model)
{
<div class="scroll2">
<div class="itemcontainer">
<table>
<tr>
<td id="#item.Id">
<div class="DetailsLink"> #Html.ActionLink(#item.Name, "Details", new { id = item.Id })</div>
<br />
<div id="#item.Id"></div>
<div class="divPrice" id="#item.Price">#Html.DisplayFor(modelItem => item.Price)</div>
<div class="divImg"><a class="fancybox-thumbs" href="#item.Image.ImagePath" title="#item.Image.AltText" data-fancybox-group="thumb"><img src="#item.Image.ImagePath" alt="#item.Image.AltText" title="#item.Image.AltText" /></a></div>
<div> </div>
<div class="divQuantity"> Quantity: #Html.TextBoxFor(modelItem => item.Quantity, new { #id = "quantity", #style = "width:50px;", #class = "formTextBox" })</div>
<div class="divAddToCart">
<input type="submit" value="Add To Cart" class="btn btn-default" /> #*#Html.ActionLink("Add to cart", "AddToCart", "Orders", new { id = item.Id }, new { #class = "AddToCart" })*#
</div>
<div style="height:15px;"></div>
</td>
</tr>
</table>
</div>
</div>
}
<div class="button">#Html.ActionLink("Back To Categories","Categories")</div>
<br />
</div>
</div>
}
#section scripts {
<script src="~/Scripts/jQuery-jScroll.js"></script>
<script src="~/Scripts/jquery.fancybox.js?v=2.1.5"></script>
<script src="~/Scripts/jquery.fancybox-thumbs.js?v=1.0.7"></script>
<script src="~/Scripts/jquery.fancybox-buttons.js?v=1.0.5"></script>
<script type="text/javascript">
//$(function () {
// $('.scroll').jscroll({
// autoTrigger: true
// });
$('.fancybox-thumbs').fancybox({
prevEffect: 'none',
nextEffect: 'none',
closeBtn: true,
arrows: false,
nextClick: false
});
// Document.ready -> link up remove event handler
//$(".AddToCart").click(function () {
// alert('Clicked!');
//});
//s});
</script>
}
And AddToCart
[HttpPost]
public ActionResult AddToCart(DisplayProductDetailsViewModel model)
{
// Retrieve the product from the database
var productAdded = db.Products
.Single(p => p.ProductId == model.Id);
// Add it to the shopping cart
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.AddToCart(productAdded);
// Go back to the main store page for more shopping
return RedirectToAction("Index");
}
When I set a breakpoint in AddToCart model is always null, what am I missing here?
You are always sending the same quantity because you always trigger the ActionLink's GET request that only passes the query string parameter id. The quantity field in your form is never sent as that's part of the POST form body submission.
// you are not triggering this submission: POST AddToCart
#using(Html.BeginForm("AddToCart", "Orders", FormMethod.Post))
{
// quantity is passed on the request's body
#Html.TextBoxFor(modelItem => item.Quantity)
// but GET AddToCart/id is send instead
#Html.ActionLink("Add to cart", "AddToCart", "Orders", new { id = item.Id }, new { #class = "AddToCart" })
}
As indicated by your screenshot
Request URL: http://localhost:24177/Orders/AddToCart/21
Request Method: GET
Status Code: 302 Found
Notice "Form Data" or "Request Body" is missing from your request.
Each time you call GET AddToCart you query the same product from the data store and set the quantity property to the same value.
You ought to send this GET request as POST instead because:
It can be cached by the browser and it won't make subsequent requests to the
server
Web crawlers can trigger the ActionLink request
So add a submit button to your form and make a POST action. Then be sure the assign a new quantity from the form.
[HttpPost]
public ActionResult AddToCart(FormModel model)
{
var product = db.Products.Where(... && p => p.Id == model.ProductId);
var quantity = model.ProductQuantity;
cart.AddToCart(product, quantity);
return RedirectToAction("Index");
}
I'm currently teaching myself the ins and outs of building a MEAN app. I started with the basic todo app and modified it pretty heavily to support multiple key value pairs and have updated the UI using bootstrap.
It's currently hosted here: http://surveymanager-30817.onmodulus.net/
I've implemented ui-sortable, and it works perfectly by itself.
The challenge I'm having, that I cannot seem to find any relevant documentation or tutorials for - is how to communicate the updated sort order to mongo so when I refresh the page, ng-repeat will repeat question in questions with the order that I had previously created.
Here is the HTML
<!-- index.html -->
<!doctype html>
<!-- ASSIGN OUR ANGULAR MODULE -->
<html ng-app="QuestionManager">
<head>
<!-- META -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->
<title>Question Manager</title>
<!-- SCROLLS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/question-bootstrap.css" rel="stylesheet">
<style>
html { overflow-y:scroll; }
body { padding-top:30px; }
#todo-list { margin-bottom:30px; }
</style>
<!-- SPELLS -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script><!-- load jquery -->
<script src="//code.jquery.com/ui/1.9.1/jquery-ui.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script><!-- load angular -->
<script src="js/sortable.js"></script>
<script src="core.js"></script>
</head>
<!-- SET THE CONTROLLER AND GET ALL TODOS -->
<body ng-controller="mainController">
<div class="container sm_head">
<div class="col-sm-6">
</div>
<div class="col-sm-6">
<h2 class="pull-right">Survey Manager</h2>
</div>
</div>
<div class="container">
<!-- Nav tabs -->
<ul id="navtabs" class="nav nav-tabs" role="tablist">
<li class="active">Manage</li>
<li>Create</li>
<li>Render Code</li>
<li>About</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane active" id="manage">
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading">Manage Question Order<span class="badge pull-right">{{ questions.length }} Questions</span></div>
<!-- Table -->
<table class="table">
<thead>
<tr>
<th>Order</th>
<th>Question Name</th>
<th>Evergage Field</th>
<th>Username</th>
<th>Options</th>
</tr>
</thead>
<tbody ui-sortable="sortableOptions" ng-model="questions">
<tr ng-repeat="question in questions">
<td>{{ question.order }}</td>
<td>{{ question.meanName }}</td>
<td>{{ question.fieldName }}</td>
<td>#mdo</td>
<td>
<button type="button" class="btn btn-default btn-sm" ng-click="deleteQuestion(question._id)">
<span class="ques-list glyphicon glyphicon-remove"></span> delete </button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-8">
<form class="form-horizontal">
<fieldset>
<!-- Form Name -->
<legend>Question Details</legend>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="Question Name">Question Order</label>
<div class="col-md-8">
<input id="Question Order" name="Question Order" type="text" placeholder="Question Order" class="form-control input-md" ng-model="formData.order">
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="Question Name">Question Name</label>
<div class="col-md-8">
<input id="Question Name" name="Question Name" type="text" placeholder="Write something meaningful" class="form-control input-md" ng-model="formData.meanName">
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="Custom Field Name">Custom Field</label>
<div class="col-md-8">
<input id="Custom Field Name" name="Custom Field Name" type="text" placeholder="Format: User.Profile.xx.xx.xx ( 1 or 3 additional words)" class="form-control input-md" ng-model="formData.fieldName">
</div>
</div>
<!-- Button -->
<div class="form-group">
<label class="col-md-4 control-label" for="Create"></label>
<div class="col-md-4">
<button id="Create" name="Create" class="btn btn-primary" ng-click="createQuestion()">Create</button>
</div>
</div>
</fieldset>
</form>
</div>
</div>
<div class="tab-pane" id="create">
</div>
<div class="tab-pane" id="render">...</div>
<div class="tab-pane" id="about">...</div>
</div>
</div>
<script src="js/bootstrap.min.js"></script>
</body>
</html>
Here is the client side:
// public/core.js
var QuestionManager = angular.module('QuestionManager', ['ui.sortable']);
function mainController($scope, $http) {
$scope.formData = {};
// when landing on the page, get all questions and show them
$http.get('/api/questions')
.success(function(data) {
$scope.questions = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
// when submitting the add form, send the text to the node API
$scope.createQuestion = function() {
$http.post('/api/questions', $scope.formData)
.success(function(data) {
$scope.formData = {};
console.log('fuck you!');
$scope.questions = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
// delete a question after checking it
$scope.deleteQuestion = function(id) {
$http.delete('/api/questions/' + id)
.success(function(data) {
$scope.questions = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
$scope.$watch("questions", function(newVal, oldVal) {
console.log("oldval", oldVal);
console.log("newVal", newVal);
});
$scope.sortableOptions = {
update: function(e, ui) {
$http.put('/api/questions', $scope.questions)
console.log($scope.questions);
},
axis: 'y'
};
}
// Some Bootstrap Initializers
$('#navtabs>li a').click(function (e) {
e.preventDefault()
$(this).tab('show')
})
And here is the server side with my random test and console logs starting at line 44
// server.js
var express = require('express');
var app = express(); // create our app w/ express
var mongoose = require('mongoose'); // mongoose for mongodb
var morgan = require('morgan'); // log requests to the console (express4)
var bodyParser = require('body-parser'); // pull information from HTML POST (express4)
var methodOverride = require('method-override'); // simulate DELETE and PUT (express4)
mongoose.connect('mongodb://ewill3532:12qwaszx#proximus.modulusmongo.net:27017/pUxo2hir'); // connect to mongoDB database locally
app.use(express.static(__dirname + '/public')); // set the static files location /public/img will be /img for users
app.use(morgan('dev')); // log every request to the console
app.use(bodyParser.urlencoded({'extended':'true'})); // parse application/x-www-form-urlencoded
app.use(bodyParser.json()); // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json
app.use(methodOverride());
var question = mongoose.model('question', {
order : String,
fieldName : String,
meanName : String
});
app.get('/api/questions', function(req, res) {
// use mongoose to get all questions in the database
question.find(function(err, questions) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err)
res.send(err)
res.json(questions); // return all questions in JSON format
});
});
// create todo and send back all questions after creation
app.post('/api/questions', function(req, res) {
// create a todo, information comes from AJAX request from Angular
question.create({
order : req.body.order,
fieldName : req.body.fieldName,
meanName : req.body.meanName,
done : false
}, function(err, todo) {
if (err)
res.send(err);
// get and return all the questions after you create another
question.find(function(err, questions) {
if (err)
res.send(err)
res.json(questions);
});
});
});
// delete a todo
app.delete('/api/questions/:todo_id', function(req, res) {
question.remove({
_id : req.params.todo_id
}, function(err, todo) {
if (err)
res.send(err);
// get and return all the questions after you create another
question.find(function(err, questions) {
if (err)
res.send(err)
res.json(questions);
});
});
});
// let's just put this here so that it comes back as found
app.put('/api/questions', function(req, res) {
});
app.get('*', function(req, res) {
res.sendfile('./public/index.html'); // load the single view file (angular will handle the page changes on the front-end)
});
// listen (start app with node server.js) ======================================
app.listen(8080);
console.log("App listening on port 8080");
Any help here, or if someone can point me to some good documentation or tutorials - either way would be very helpful!'
Thanks!