How to programmatically add items from a page in KeystoneJS - javascript

I'm trying out KeystoneJS to build a site in which people can submit words and ask other users for synonyms. So I've build a simple Word model:
var keystone = require('keystone');
var Types = keystone.Field.Types;
var Word = new keystone.List('Word', {
map: { name: 'word' }
});
Word.add({
word: { type: Types.Text, required: true, initial: "New word", index: true }
});
Word.defaultColumns = 'word';
Word.register();
The idea is that a user enters a word in an input box on the homepage, clicks "Submit," and the word gets added as an item in the Word model. But I can't figure out the interplay between the Javascript on the page that handles the event that fires on clicking "Submit" and the code that actually creates a new items in the DB, like so:
var keystone = require('keystone'),
Word = keystone.list('Word');
var newWord = new Word.model({
word: newWord // read from the input box on the home page
});
newWord.save(function(err) {
// post has been saved
});
I originally naively supposed that I could make Word part of the locals object and create the new item from the JS that lives on the page, but this doesn't work. So I imagine I need to read the word from the input box, then make an AJAX call to a route that saves the word to the DB. Here is where my understanding of KeystoneJS breaks down. Where would I put the code to accept that AJAX call and create the new item?

The javascript in the page, and keystone on the server side are indeed two seperate domains. To interact with keystone from your page you have to make HTTP calls. These calls are routed to keystone views.You can find an example in de keystone demo site:
in keystone.js the routes are set:
keystone.set('routes', require('./routes'));
in routes/index.js the routes are defined, ie:
app.get('/blog/:category?', routes.views.blog);
In the example above the url "/blog/news" is handled by the view routes.views.blog, and "news" is an (optional) parameter.
In your case you would end up (if you want you use REST style) with something like:
app.post('/word/:newword?', routes.views.word);
The .post method routes urls to this view if and only if the request is a POST request.
The solution that you suggest, making a AJAX call is one of the possible solutions.You could also use a form and post the whole page.
Within the view you create a handler for the view like this:
view.on('post', function(next){
if (req.params.newword){
//store and create answer
next();
}
});

Related

Flashcard webapp: want to load a deck to study based on the deck selected

I am working on a flashcards app using flask. It is similar to Anki if you know about that. When I click on a deck, I want it to go to study_session.html where it displays the first card of the selected deck. Currently I have a home screen that lists the user's decks. Each list item is a button element with an onclick attribute that calls a javascript method that takes the selected deck's name as a parameter. The method sends a post request to the flask route "/study-session" along with the name of the deck. This allows the route's method to get the selected deck and send the appropriate flashcard to the "study_session.html" page to be displayed. What's wrong is that either the javascript method or the server-side route method, or both do not seem to be executing. When I click the deck I want to study, nothing happens. I would like someone to point out my mistake and offer the appropriate fix. If I need ajax, please show me how the javascript would look like. Thank you.
Here is the html that has the button with an onclick attribute:
{% for deck in user.decks %}
<li class="list-group-item">
<button class="btn btn-link pl-0" onclick="startStudySession({{ deck.deck_name }})">{{deck.deck_name}}</button>
Here is the javascript:
// Takes deckId and sends post req to study-session route.
// Then runs study-session view
function startStudySession(deckName) {
// Specify what route to send request to
fetch("/study-session", {
method: "POST",
body: JSON.stringify({deckName: deckName})
}).then((_res) => {
window.location.href = "/study-session"
});
}
Here is the route method on the python server:
#views.route('/study-session', methods=['GET', 'POST'])
def study_session():
"""
Renders study_session page with one flashcard displayed. Handles all requests on study_session page.
"""
request_data = json.loads(request.data)
cur_deck = current_user.get_deck(request_data['deckName'])
cur_card = cur_deck.notes[0].flashcards[0]
front = cur_card.front_text
back = cur_card.back_text
print("is this working?")
return jsonify('', render_template("study_session.html", user=current_user, front=front, back=back))

Displaying ajax data in template page - Framework7

Am playing with Framework7 to do hybrid mobile app development. I have three tabs (bottom fixed), which are Home, Contacts and Settings/Profile
My app.js file looks somewhat like this:
var $$ = Dom7;
var app = new Framework7({
//.....
data: function () {
return {
user_profile : ''
}
},
on: {
tabShow( tab ) //-- when a bottom tab is clicked
{
if( $$(tab).attr('id') == 'view-settings' )
{
//.. do ajax call and get the response data in "resp"
app.data.user_profile = resp.response.profile; //setting the info to app's data
}
}
},
routes: routes
});
var settingsView = app.views.create('#view-settings', {
url: '/settings/'
});
And in routes.js:
routes = [
{
path: '/',
url: './index.html',
},
{
path: '/contacts/',
componentUrl: './pages/contacts.html',
},
{
path: '/settings/',
componentUrl: './pages/settings.html',
}
];
This Contacts page contains static content. For the Home page, am doing the AJAX API call during the deviceready state. Because am setting up some headers for authentication and stuff(for all the AJAX api calls) in there.
The problem am facing is, am unable to display the content in Settings page. It is always empty!
Am using this in that template page:
<div class="item-title item-label">Full Name - {{$root.user_profile.full_name}}</div>
I want to compile that template only when clicking the respective tab button.
Maybe that's the problem.
Any suggestions?
After going through the documentations again and again, I got another way to do this.
So, during the tabShow event, I check whether the user is accessing the Settings/Profile tab. If so, I check whether an object in app.data (eg: app.data.user_profile is empty or not(am storing the profile details there). If empty, I would do an AJAX API call to get the profile details. When the profile details is obtained, I would use app.form.fillFromData() method to fill the form. Documentation here: https://framework7.io/docs/form.html#form-data-app-methods
Make sure to name the form as well as the input elements in that form, and the same name should be use in the object(key name) when calling the fillFromData() function.
And one more thing, for the routes, /settings/ path, I used url instead of the componentUrl property to pass the url of the page.
This may not be the best solution, but am still learning. And it seems to have solved by current problem.
Thank you

Simple MEAN Stack issue

I have a express route;
router.get('/uni/:queryst', function (req, res) {
var choice = req.params.queryst);}
Where basically I use the queryst value from the URL to use it to query something in my database. On the front end, I need to have an input field where the user types the choice and hits submit and then it translates to something like /uni/valuehere on the browser and sending GET to express where it gets caught by the router I wrote above. I don't know how can I make the input field of HTML turn into a URL of the format /uni/valuehere .
As for testing the code initially I was directly typing the URL manually with the value I intended to verify to check if the data passed. Well, users are not going to type URL's directly to search, they will need a form and that is what I can't get my head around to how to turn the input field into a URL in a format /uni/WhateverTheUserHasTypedInTheFieldShouldAppearHere
Thanks in advance for the help. I am self learning the MEAN stack and have come a long way. I need support of experienced developers as I am currently stuck here.
While sending the request write (in controller):
$http.get('/uni/'+queryst).success(function(response){
// Write code using response
});
While on the server side write:
$http.get('/uni/:queryst',function(req,res){
var choice= req.params.queryst
// Code ....
});

Jade block extend for each item in database?

What am I doing wrong?
test.jade
doctype !!! 5
html(lang="en-us")
head
title test
h1 Information;
body
block testcontent
form(method="post")
input(type="button", value="refresh")
testcontent.jade
extends test
append testcontent
br
p test #{title}
routing handler;
app.get('/search', function(req, res) {
res.render('test');
});
app.post('/search', function(req, res) {
table.find().sort({created:1}).toArray(function(err, items) {
if(err) {
console.log(err);
}
else {
res.render('testcontent', {title:items._id});
}
});
});
Trying to make it write 'test' and the title for each item in my database. I click 'refresh' in the page, nothing happens.
When the user sends a POST using the refresh button, it's meant to get every entry's _id in my database and stick it in an array with table.find().sort({created:1}).toArray()
Then I wanted it to render testcontent for each entry, res.render('testcontent', {title:items._id}); which should push out several entries of "test exampleTitle" on new lines.
Then that goes into the testcontent block in the first file, which can now be 'updated' every time the user clicks refresh.
Right?
But it doesn't work, no errors thrown, it just doesn't add the "test title" text to the /search route like I want it to.
I'm using Node.js/express and MongoDB.
Did you check if your form is submitted? Change the type of the button to submit from button and that will submit the form. And in your route handler, I see you are passing only one title to your template, so you will see only one line in your page. If you want one line per entry, you should pass the whole array to your template and iterate using the 'each' construct in your jade template

Should Backbone.js grab GET parameters from URL?

I am trying to implement a search function for my website. When the user types a search term foobar into a input box and submits it, he is redirected to http://mydomain.com/search?query=foobar.
Problem:: How should I grab the GET parameters query from the URL, and send it to the backend and get a array of results back as a JSON response? Should I even do it this way?
My current attempt below does not even cause the search function to be triggered.
Router
var AppRouter = Backbone.Router.extend({
routes: {
'search?query=:query': 'search'
// ... and some other routes
},
search: function(query) {
this.photoList = new SearchCollection();
var self = this;
this.photoList.fetch({
data: {query: query},
success: function() {
self.photoListView = new PhotoListView({ collection: self.photoList });
self.photoListView.render();
}
});
}
});
var app = new AppRouter();
Backbone.history.start({
pushState: true,
root: '/'
});
There have been several issues filed against Backbone for this very issue. There is an existing plugin that works well for this:
https://github.com/jhudson8/backbone-query-parameters
Alternatively, I'm currently using query string parameters in a mock API that matches Backbone's route matching. Looks something like this
Route
"/api/v2/application/:query"
Query
application: function(query) {
var params = $.deparam(query.slice(1));
// params.something...
}
As to your actual issue at hand how are you redirecting to index.html to support pushState?
I hit this same issue and contemplated using backbone-query-parameters, but that should be considered generally an incorrect approach.
The url query string is not meant for the front end. They get sent to the server and force a refresh when navigating from page.html to page.html?something=something.
You should be using hash fragments instead. i.e. http://www.example.com/ajax.html#key1=value1&key2=value2 then just get those values the normal backbone way and build your request params from that.
See https://github.com/jashkenas/backbone/issues/891, https://developers.google.com/webmasters/ajax-crawling/docs/specification, https://www.rfc-editor.org/rfc/rfc3986#section-3.5
You can always read the URL via jQuery URL plugin. It works well.
https://github.com/allmarkedup/jQuery-URL-Parser
There are very few cases when you need to read the URL and extract the GET params. I think that you are doing things wrong and here are my options:
1) if you are having just one page in your app (single app page) you can display results as they type in your input field or after they hit submit
2) if you are redirecting the user to a different page that means you can bootstrap data so that after the page is loaded backbone will just have to render your results and only make other requests if you change your search word
3) you can have a javascript variable which is initialized on page load directly from the server where working with GET params is probably easier

Categories