Response difference on local and EC2 server when hitting request in nodejs - node.js

Okay so I did not get anything from my last SO question may be I was not so clear in detailing the issue I am facing, so hoping this time I'll get solution for my issue:
var request = require("request");
var options = { method: 'GET',
url: 'https://shop.advanceautoparts.com/web/AAPStoresWithAvailableInventoryCmd',
qs:
{ quantity: '1',
partNumber: '2130015-P',
storeId: '10151',
catalogId: '10051',
langId: '-1',
loadStoresComponent: 'true',
latitude: '',
longitude: '',
prefStoreNickName: '8511' },
headers:
{ accept: 'application/json, text/plain, /',
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36',
'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8',
origin: 'https://shop.advanceautoparts.com' } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
I am getting accurate data about what I need when I try this at local ENV.
BUT when the same code is executed at my EC2 server, I get an awkward response from the code above.
RESPONSE AT LOCAL
{
"loadStoresComponent": ["true"],
"langId": ["-1"],
"quantity": ["1"],
"prefStoreNickName": ["8511"],
"latitude": [""],
"longitude": [""],
"authToken": "-1002%2C3a0VN%2Bt240MbliDjfWLSDJxolLA%3D",
"catalogId": ["10051"],
"DM_PersistentCookieCreated": ["true"],
"partNumber": ["2130015-P"],
"storeId": ["10151"],
"Result": {
"view": "availableStoresList",
"storesListMap": [
{
"inStock": true,
"state": "NC",
"isPrefStore": false,
"distance": 0,
"storeBrand": "AAP",
"city": "MATTHEWS",
"latitude": "35.137527",
"address1": "9507 INDEPENDENCE BLVD.",
"zipCode": "281050000",
"nickName": "8511",
"phone": "704-845-5004",
"longitude": "-80.715079"
},
{
"inStock": true,
"state": "NC",
"isPrefStore": false,
"distance": 4,
.....
.....
}
RESPONSE AT SERVER
<div class="pagewidth">
<div class="logo"> </div>
<div id="tagline"> </div>
<div class="clear"> </div>
<div class="header"> </div>
<div class="content">
<p class="heading">We're offline for a tune-up, we'll be up and running smoothly very soon. </p>
<p class="heading">In the meantime, here are some other options available:</p>
<div class="section group">
<div class="col span_1_of_4"> Visit an <br />
<strong>Advance Auto Parts store</strong><br />
<img src="/MaintPage/images/DM150205_maintenance_page_07.png" width="200" height="125" border="0" /> </div>
<div class="col span_3_of_4"> Sign up for <br />
<strong>SpeedPerks Rewards</strong><br />
<img src="/MaintPage/images/sp-logo.png" width="204" height="100" border="0" style="margin-top:20px;" /> </div>
<div class="col span_4_of_4"> View us on Social Media <strong><br />
Facebook/Twitter/Blog</strong><br />
<img src="/MaintPage/images/DM150205_maintenance_page_12.png" width="110" height="125" border="0" /><img src="/MaintPage/images/DM150205_maintenance_page_16.png" width="92" height="125" border="0" /><img src="/MaintPage/images/button-m.png" width="90" height="125" border="0" style="margin-left:0px;" /> </div>
</div>
<div class="clear"> </div>
<p class="heading">We appreciate your patience – on your next visit, <strong>use coupon code PS20 for 20% off your purchase. </strong></p>
<p class="coupon"><img src="/MaintPage/images/DM150205_maintenance_page_21.png" width="354" height="134" /></p>
<p style="margin:30px; text-align:center;">We look forward to serving you,<br />
The Advance Team</p>
<p style="margin:30px; text-align:center;"> </p>
</div>
</div>
Basically it is an offline page(didn't paste whole HTML body and CSS), by which I feel that they are denying to return the response of my request.
I have tried adding x-forwarded-for in my call as well but no luck with that.
And I am getting an accurate response by adding x-forwarded-for in rails, but I guess there may be some extra headers or anything else I need to add in my request to get the desired result.
Any thoughts on my issue?

Related

I can't change the value in the database - array

I need to update the comment status, i.e. the name of the "approve" field. To this end, I created the AJAX script also the backend seems correct because it receives a message after clicking the button. I don't know why I can't save the "approve" value to the database. Please help. I use NodeJS, MongoDB, Express, and EJS for frontend.
My database:
My frontend:
<% post.comments.forEach(function (comment) { %>
<tr>
<td> <%= comment._id %> </td>
<td> <%= comment.username %> </td>
<td> <%= comment.comment %> </td>
<form method="post" onsubmit="return doApprove(this);">
<input type="hidden" name="post_id" value="<%= post._id %>" />
<input type="hidden" id="comment_id" name="comment_id">
<td> <input class="approve-comment" type="checkbox" name="approve" value="true">
<button class="btn btn-info" value="Approve"/>
</td>
</form>
<% }) %>
</tr>
</table>
</div>
<script>
function doApprove(form) {
var formData= {approve:form.approve.value};
$.ajax({
url: "/do-edit-comment",
method: "POST",
data: formData,
success: function (response) {
formData._id =response._id;
alert(response.text);
}
});
return false;
}
</script>
My backend:
app.post("/do-edit-comment", function (req,res) {
blog.collection("posts").updateOne({
"_id": ObjectId(req.body.id),
"comments._id": ObjectId(req.body.id)
},{
$set: {
"comments": {approve: req.body.approve}
}
}, function (error,document) {
res.send({
text: "comment approved"
});
});
});
To update a single element from an array, what you need is the $[<identifer>] array update operator. For the scenario described in your question, the update query in the server should be something like this:
blog.collection("posts").updateOne(
{ "_id": ObjectId(req.body.post_id), },
{ $set: { "comments.$[comment].approve": req.body.approve } },
{ arrayFilters: [{ "comment._id": ObjectId(req.body.commentId) }] },
function (error, document) {
res.send({
text: "comment approved"
});
}
)
EDIT(Front-end issues/fix)
So I figured there are some issues with the front end code too. This edit attempts to explain/fix those issues.
Issues:
1. The backend expects a commentId in the request object(req.body.commentId) to be able to identify the comment to update but you are not sending that from the front end.
2. The backend needs the id of the post to uniquely identify the post to update, but you are not sending that from the front end.
3. The approve value in the form data sent from the front-end is a string and it would always be "true". This is not what you want, you want to send a boolean value(true or false) depending on if the checkbox is checked or not.
Fix:
Update the form template to this:
<form method="post" onsubmit="return doApprove(event);">
<input type="hidden" name="post_id" value="<%= post._id %>" />
<input type="hidden" id="<%= comment._id %>" name="comment_id" />
<td> <input class="approve-comment" type="checkbox" name="approve" value="true">
<button class="btn btn-info" value="Approve"/>
</td>
</form>
Changes:
- The doApprove handler attached to the submit event of the form is now called with event instead of this.
- I updated the value of the id attribute for the input#name="comment_id" element to the actual comment._id value.
And in the script tag, update the doApprove function to this:
function doApprove(event) {
event.preventDefault();
const form = event.target;
var formData = {
approve: form.approve.checked, // form.approve.checked would be true if the checkbox is checked and false if it isn't
post_id: form.post_id.value, // The value of the input#name=post_id element
commentId: form.comment_id.id, // The id attribute of the input#name=comment_id element
};
$.ajax({
url: "/do-edit-comment",
method: "POST",
data: formData,
success: function (response) {
formData._id =response._id;
alert(response.text);
}
});
return false;
}
I hope that helps.

how can I apply multiply each as express handlebars (#each)?

.Handlebars file
{{#each products}}
<div class="col-xs-6 col-sm-4 col-md-4 col-lg-4">
<div class="product_block">
<div class="product-image-container image">
<a href="#" class="product_img_link">
<img src="{{**???**= /images/product-1-1.jpg}}" alt="product-1-1" class="img-product" />
<span class="product-additional">
<img src="{{**???**= /images/product-1-2.jpg}}" alt="product-1-2-back" />
</span>
</a>
.index.js
app.get('/collections/all', (req, res) => { res.render('collections/all', {
title: 'All Products',
products: [
'/images/product-1-1.jpg',
'/images/product-1-2.jpg',
'/images/product-2-1.jpg',
'/images/product-2-2.jpg'
] }); });
Q. As you can see, I want to apply like 'product-1-1' and 'product-1-2' each <img>
But How can I apply each <img> as {#each} ???
Because I have to repeat <div class="col->..
So it is hard to apply ?
Excuse me, please help me ~
I think you have to rearange your data. At least you will have the opportunities of the (dynamic) each loop instead of comparing (static) filenames.
products: [
{
front: '/images/product-1-1.jpg',
back: '/images/product-1-2.jpg',
altFront: 'product-1-1',
altBack: 'product-1-1-back'
},
{
front: '/images/product-2-1.jpg',
back: '/images/product-2-2.jpg',
alt: 'product-2-1',
altBack: 'product-2-1-back'
},
]
{{#products}}
<div class="col-xs-6 col-sm-4 col-md-4 col-lg-4">
<div class="product_block">
<div class="product-image-container image">
<a href="#" class="product_img_link">
<img src="{{front}}" alt="{{altFront}}" class="img-product" />
<span class="product-additional">
<img src="{{back}}" alt="{{altBack}}" class="img-product" />
</span>
</a>
</div>
</div>
</div>
{{/products}}

FineUploader Wrong Getting Azure Blob Storage URI

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.

Logging in to this website using requests module in python3

Here's what I have done so far. I had to extract the CSFRToken manually (I don't know regex, so that part is messy). Is CSFR part of cookies? Because my cookies only shows two other ID type params, so I dropped the cookie part and did it this way.
import requests
URL = r'http://login.cheezburger.com/'
client = requests.session()
login_page = client.get(URL)
index = login_page.text.find("CSRFToken")
token = login_page.text[index:index+90].split('"')[-2] # This works, I guarantee :)
#print(token) I checked it manually
login_data = {'rlm': 'Shopper',
'for': r'http://login.cheezburger.com/',
'username': 'myusername',
'password': 'mypassword',
'CSRFToken': token}
req = client.post(URL, data=login_data)
Now, there is no error per say, but I am not logging in to this site either. The text of this request shows that I am still stuck in the login page!
The parameters send are (as shown in the dev tools of firefox):
rlm: 'Shopper'
for: 'http://login.cheezburger.com/'
username: 'myusername',
password: 'mypassword',
CSRFToken: '8uhhbf67-1233-fff3-123g1-123123fsdfs22'
The websites source is as follows (the part that contains the form data):
<div class="contents-msl">
<h2>Client Login</h2>
<p>Enter username and password</p>
<div class="form-all-msl">
<form action="/login.action" id="loginForm" method="post"
enctype="application/x-www-form-urlencoded"><input type=hidden name=rlm
value="Shopper"><input
type=hidden
name=for
value="http%3a%2f%2flogin%cheezburger%2ecom%2f">
<ul class="form-section-msl">
<label class="form-label-left-msl" for="loginUserName">
Username<span class="form-required">*</span>
</label>
<div class="form-input-msl">
<input type="text" class="form-textbox-msl" id="loginUserName" name="username"
size="20">
</div>
<label class="form-label-left-msl" for="loginPwd">
Password<span class="form-required-msl">*</span>
</label>
<div class="form-input-msl">
<input type="password" class="form-textbox-msl" id="loginPwd" name="password"
size="20">
</div>
<div class="form-input-msl">
<div class="form-single-column-msl">
</div>
</div>
</ul>
</div>
<button class="members-btn-msl" type="submit">Login</button>
<input type="hidden" name="CSRFToken" class="CSRFToken" value="8uhhbf67-1233-fff3-123g1-123123fsdfs22" /> </form>
</div>
</div>
You maybe want to add some headers
headers = {
'Accept':
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Encoding':
'gzip, deflate, sdch',
'Accept-Language':
'en-US,en;q=0.8,vi;q=0.6',
'Cache-Control':
'max-age=0',
'Connection':
'keep-alive',
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36',
}
Then add headers to your code:
req = client.post(URL, data=login_data)
Good luck !

Durandal and Zurb foundation, where should I initialize foundation?

I have been using the basic Durandal HTML template but I have replaced Bootstrap with Zurb Foundation. Everything is working well, except for the issue where I can not open any modals, drop downs or anything requiring foundation to be initialized. Generally one would place the following in the body of your webpage to do this:
<script>
$(document).foundation();
</script>
I have attempted to place this in the Index.html, Shell.html and even randomly in the viewmodel and view. Furthermore I have tried to init it from JS in the main.js, and the viewmodel itself in the activate section. None of this worked. The (hopefully very temporary) workaround that I have found is to actually init Foundation form my javascript, when clicking a button to open the modal:
$(document).foundation();
$("#myModal").foundation("reveal", "open");
Where am I going wrong?
My main.js for reference:
(function() {
requirejs.config({
paths: {
text: "../lib/require/text",
durandal: "../lib/durandal/js",
plugins: "../lib/durandal/js/plugins",
transitions: "../lib/durandal/js/transitions",
knockout: "../lib/knockout/knockout-2.3.0",
jquery: "../lib/jquery/jquery-1.9.1",
foundation: "../lib/foundation/js/foundation/foundation",
"foundation.abide": "../lib/foundation/js/foundation/foundation.abide",
"foundation.accordion": "../lib/foundation/js/foundation/foundation.accordion",
"foundation.alert": "../lib/foundation/js/foundation/foundation.alert",
"foundation.clearing": "../lib/foundation/js/foundation/foundation.clearing",
"foundation.dropdown": "../lib/foundation/js/foundation/foundation.dropdown",
"foundation.interchange": "../lib/foundation/js/foundation/foundation.interchange",
"foundation.joyride": "../lib/foundation/js/foundation/foundation.joyride",
"foundation.magellan": "../lib/foundation/js/foundation/foundation.magellan",
"foundation.offcanvas": "../lib/foundation/js/foundation/foundation.offcanvas",
"foundation.orbit": "../lib/foundation/js/foundation/foundation.orbit",
"foundation.reveal": "../lib/foundation/js/foundation/foundation.reveal",
"foundation.tab": "../lib/foundation/js/foundation/foundation.tab",
"foundation.tooltip": "../lib/foundation/js/foundation/foundation.tooltip",
"foundation.topbar": "../lib/foundation/js/foundation/foundation.topbar"
},
shim: {
jquery: {
exports: "jQuery"
},
foundation: {
deps: ["jquery"]
},
"foundation.abide": {
deps: ["foundation"]
},
"foundation.accordion": {
deps: ["foundation"]
},
"foundation.alert": {
deps: ["foundation"]
},
"foundation.clearing": {
deps: ["foundation"]
},
"foundation.dropdown": {
deps: ["foundation"]
},
"foundation.interchange": {
deps: ["foundation"]
},
"foundation.joyride": {
deps: ["foundation"]
},
"foundation.magellan": {
deps: ["foundation"]
},
"foundation.offcanvas": {
deps: ["foundation"]
},
"foundation.orbit": {
deps: ["foundation"]
},
"foundation.reveal": {
deps: ["foundation"]
},
"foundation.tab": {
deps: ["foundation"]
},
"foundation.tooltip": {
deps: ["foundation"]
},
"foundation.topbar": {
deps: ["foundation"]
}
}
});
define(["durandal/system", "durandal/app", "durandal/viewLocator", "foundation"], function(system, app, viewLocator, foundation) {
system.debug(true);
app.title = "Durandal Starter Kit";
app.configurePlugins({
router: true,
dialog: true,
widget: true
});
app.start().then(function() {
viewLocator.useConvention();
app.setRoot("viewmodels/shell", "entrance");
});
});
}).call(this);
shell.html:
<div>
<nav class="top-bar hide-for-small">
<ul class="title-area">
<!-- Title Area -->
<li class="name">
<h4 style="color: white">KRS Template</h4>
</li>
<!-- Remove the class "menu-icon" to get rid of menu icon. Take out "Menu" to just have icon alone -->
<li class="toggle-topbar menu-icon"><span>Menu</span></li>
</ul>
<div class="top-bar-section">
<!-- Right Nav Section -->
<ul class="right" data-bind="foreach: router.navigationModel">
<li data-bind="css: { active: isActive }">
<a data-bind="attr: { href: hash }, html: title"></a>
</li>
</ul>
</div>
</nav>
<div class="container-fluid page-host" data-bind="router: { transition: 'entrance', cacheViews: true }"></div>
</div>
shell.js:
(function() {
define(["plugins/router", "durandal/app", "foundation"], function(router, app, foundation) {
return {
router: router,
activate: function() {
router.map([
{
route: "",
title: "Welcome",
moduleId: "viewmodels/welcome",
nav: true
}, {
route: "List",
moduleId: "viewmodels/ListOfItems",
nav: true
}
]).buildNavigationModel();
return router.activate();
}
};
});
}).call(this);
Where the actual problem lies: ListOfItems.html:
<section>
<div>
<h2 data-bind="text: displayName" style="text-align: center"></h2>
<div class="row">
<div class="small-12 column">
<div data-bind="foreach: items">
<div class="panel">
<div class="row">
<div class="small-6 column">
<b>Number: <span data-bind="text: NumberA"></span></b>
<br />
<b>Number B: <span data-bind="text: NumberB"></span></b>
<br />
</div>
<div class="small-6 column">
<b>Text A: <span data-bind="text: StringA"></span></b>
<br />
<b>Text B: <span data-bind="text: StringB"></span></b>
<br />
<b>Text C: <span data-bind="text: StringC"></span></b>
<br />
</div>
<div class="row">
<div class="small-12 column">
<a class="button" data-bind="click: function () { $parent.openModal(this); }">Open Modal</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="myModal" class="reveal-modal" data-reveal>
<h2>Edit Item</h2>
<p class="lead">Please alter the item</p>
<input id="Stepper" type="number" data-bind="value: ModalNumberA" />
<input id="Number1" type="number" data-bind="value: ModalNumberB" />
<input type="text" placeholder="Scope name" data-bind="value: ModalStringA" />
<input type="text" placeholder="Scope name" data-bind="value: ModalStringB" />
<input type="text" placeholder="Scope name" data-bind="value: ModalStringC" />
<a class="small button" data-bind="click: saveModalChanges">Save</a>
<a class="small button" data-bind="click: closeModal">Close</a>
<a class="close-reveal-modal">×</a>
</div>
</div>
</section>
ListOfItems.js (generated from CoffeeScript):
(function() {
var _this = this;
define(["durandal/app", "knockout", "krs", "jquery", "foundation.reveal", "foundation.dropdown"], function(app, ko, krs, $, reveal, dropdown) {
return {
displayName: "List of Items",
items: ko.observableArray([]),
result: ko.observable(),
ModalNumberA: ko.observable(),
ModalNumberB: ko.observable(),
ModalStringA: ko.observable(),
ModalStringB: ko.observable(),
ModalStringC: ko.observable(),
ItemBeingEdited: ko.observable(),
activate: function() {
var _this = this;
return callTheWebApi("http://localhost:54129/api/mocked/GetMockedViewModelList", "GET", "", function(result) {
console.log(result);
_this.items(result);
});
},
openModal: function(item) {
this.ItemBeingEdited(item);
this.ModalNumberA(item.NumberA);
this.ModalNumberB(item.NumberB);
this.ModalStringA(item.StringA);
this.ModalStringB(item.StringB);
this.ModalStringC(item.StringC);
$(document).foundation();
return $("#myModal").foundation("reveal", "open");
},
saveModalChanges: function() {
var itemBeingEdited,
_this = this;
itemBeingEdited = new Object();
itemBeingEdited.NumberA = this.ModalNumberA();
itemBeingEdited.NumberB = this.ModalNumberB();
itemBeingEdited.StringA = this.ModalStringA();
itemBeingEdited.StringB = this.ModalStringB();
itemBeingEdited.StringC = this.ModalStringC();
return callTheWebApi("http://localhost:54129/api/mocked/SaveMockedViewModel", "GET", "{'model':" + JSON.stringify(itemBeingEdited) + "}", function(success) {
var templist;
if (success) {
_this.items()[_this.items().indexOf(_this.ItemBeingEdited())] = itemBeingEdited;
templist = _this.items();
_this.items(null);
_this.items(templist);
return $("#myModal").foundation("reveal", "close");
}
});
},
closeModal: function() {
return $("#myModal").foundation("reveal", "close");
}
};
});
}).call(this);
If anyone could point out where I am going wrong I would really appreciate it.
When Foundation is initialized in the block below it requires all objects it binds methods to like modals, dropdowns, etc. to be within the DOM. This means that you can do the following to insure most of foundations functionality is present.
Initializing Code
$(document).foundation();
Giving it some KISS and keeping it simple you can add this to each 'viewModel' within the attached or compositionComplete callback as listed here. This will insure all the elements are within the DOM when Foundation is initialized.
Example
define(function(require){
var backend = require('backend');
return {
customers:ko.observableArray([]),
activate:function(){
// This runs as soon as the viewModel is loaded
}
attached:function(){
// This runs as soon as the view is full rendered
$(document).foundation();
}
};
});
Additionally I use micro-templating for modal (which often are not in the DOM during Foundation initialization) and one trick I have found is to initialize foundation against the object itself.
Example
$('#dynamicElement').foundation();
This works for things that use data attributes such as modals and allows you to run Foundation methods against these objects.
Regarding the Foundation with KnockOut.js mentions in other answers. I to have looked into this as I love Foundation and KnockOut is my bread and butter, but the two just do not work nicely together. In the end you are rewriting all of Foundation to work with KO and well I personally do not have the time for that, but would love to see it happen.
If you really need the responsive design and all the bells and whistles of Foundation incorporated into Durandal you really need to use Bootstrap as it does offer those features in a compatible way.
You may want to consider creating custom Knockout bindings for Foundation. Otherwise, you're going to wind up with DOM references all throughout your viewmodels, which is, in most cases, an anti-pattern for Durandal.
If you're not familiar with custom Knockout bindings, you can read about them at http://knockoutjs.com/documentation/custom-bindings.html. If you need help with custom bindings, return here, and I'll be happy to help.
If I recall, foundation() is a heavyweight initialization call. You need to do it only once. A simple custom binding placed on your shell.html would trivialize initialization--the Durandal/Knockout way.
I believe that script tags get stripped out from views by durandal so that is probably why that is not working out.
Not sure of the structure or nature of your project but if your durandal app is being delivered via a web page you could initialise foundation in the web page.
Alternatively you could initialise it in the compositionComplete or attached methods of your shell view model.

Resources