Current Context for Iron-Router in Templates - mongodb

So I've got iron-router that returns Posts.find({_id: id}) in it's data function.
Now I'm wondering how I should handle this in the respective template.
My Question: What is the data context for Iron-Router?
In my template I would be doing something like this
{{#each some_context}}
{{content}} <br>
{{/each}}
So what would be the context I should be going with?

In your data function in iron-router do something like this
var return_object = {
all_posts: Posts.find({_id: id})
}
return return_object;
In your html file you can do something like this
<template name="all_posts">
{{#each all_posts}}
{{title}}
{{/each}}
</template>
The router sets the data context to whatever you return in the data function. In this case you've returned return_object so you have access to all of it's fields. Structuring your return like this ensures that you can use the right data context when going through your each.

Related

View error in Flask when retrieving item in MongoDB

I am trying to populate a page built in Flask from items in MongoDB. My route looks like:
#app.route('/job/<posting>', methods=["GET"])
def job_post(posting):
posting = db.openings.find({'Id': posting})
title = db.openings.find_one({'Id': posting}, {'Title': 1, '_id':0})
return render_template('post_page.html', posting=posting, title=title)
My template page for post_page.html is:
{% extends "layout.html" %}
{% block content%}
<div class="container">
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<h3><a class="mr-2" href="#">{{ title }}</a></h3>
</div> -->
</article>
</div>
{% endblock content %}
When I attempt to access a page, I get an error saying "bson.errors.InvalidDocument: cannot encode object: <pymongo.cursor.Cursor object at 0x10dd402e8>, of type: <class 'pymongo.cursor.Cursor'>"
When I research that error, most of the issues have come from using find instead of find_one. I am using find_one here, and when I test it in a Python shell, the variable type for title is a dictionary, so I'm confused as to why it's passing a cursor object to the template.
You are passing posting in to the function and then immediately setting it in the first function posting = db.openings.find({'Id': posting}). I suspect you don't mean to do this.
find() return a cursor which you're then passing into the find_one() function which is causing the error you see.
Not sure what you are trying to achieve by calling find() then find_one() on the same collection. My suggestion is you remove the line posting = db.openings.find({'Id': posting})

Dynamic Vue Form from Input-Elements

I'm building a reusable vue-form-component. For more flexibility the basic idea is to NOT have to specify the form information in the vue-data-object beforehand, but to get the data-structure from the dom-input-elements itself.
On instance-creation the "v-model" attributes are read from the input-tags and applied to the instance via vue.set()
This works fairly well: https://jsfiddle.net/seltsam23/hrL3ec3z/9/
One detail is missing though: I need to only query the children-input-fields, and not the site-wide fields in case I'm using more than one form-component at the same time:
created() {
var inputs = document.querySelectorAll('input'); // works, but this returns ALL elements
var inputs = this.$el.querySelectorAll('input'); // Doesn't work because $el is only available after mounted().
...
}
mounted() {
var inputs = this.$el.querySelectorAll('input'); // works, but attribute "v-model" is removed from inputs at this point
...
}
I've tried to create data-path attribute in the created() phase to store the v-model value on the element itself, but after mount all those created attributes disappear.
Any ideas how to achieve this in an elegant way?
You should be aware that the only reason you see v-model attributes at all is that you're using inline-template. They are in the DOM during the created phase because the template has not yet been processed. What I'm saying is that you're trying to do something pretty hacky, and you probably shouldn't.
It's backward to the normal Vue approach of having the data model drive the DOM, but I know that in some cases it is useful to initialize things from the HTML.
How about this approach?
Vue.component('my-form', {
props: ['init'],
data() {
return {
form: {}
}
},
created() {
this.form = this.init;
}
})
new Vue({
el: '#vue',
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="vue">
<my-form inline-template :init="{foo: {bar: 'one', baz: 'two'}}">
<form>
<input type="text" v-model="form.foo.bar">
<span v-text="form.foo.bar"></span>
<hr>
<input type="text" v-model="form.foo.baz">
<span v-text="form.foo.baz"></span>
</form>
</my-form>
</div>

Binding an html form action to a controller method that takes some parameters

In my Find controller I have a method like:
public Result findLatest(String repoStr) {
............
}
Which is linked through a route:
GET /latest controllers.Find.findLatest(repo: String)
Then, I have a form in a view like:
<form action="#routes.Find.findLatest()" method="get">
....
<select name="repo">....</select>
</form>
But obviously that is failing, because it is expecting some parameters that I do not fulfill in the action. What is the correct way to do this without having to end up leaving the findLatest method taking no parameters in my controller?
You could change the routes to accept an empty string:
GET /latest/:repo controllers.Find.findLatest(repo: String = "")
Then configure your controller function to handle empty string.
That way,
<form action="#routes.Find.findLatest()" method="get">
....
<select name="repo">....</select>
will evaluate repo as an empty string at the controller level.
Edit: Support for this implementation was dropped in Play v 2.1
You may be interested in Play's Optional parameters e.g. play.libs.F.Option[String]
Example: How to handle optional query parameters in Play framework
GET /latest/:repo/:artifact controllers.Find.findLatestArtifact(repo: play.libs.F.Option[String], artifact: play.libs.F.Option[String])
This will allow you flexibility in which arguments need to be provided.
Not sure which language you're using but the link above contains an example for scala and the method declaration in java would look something like:
import play.libs.F.Option;
public static Result findLatestArtifact(Option<String> repo, Option<String> artifact){ ... }
and updated implementation 2.1
Routes with optional parameter - Play 2.1 Scala
EDIT: play 2.1+ Support : Props to #RobertUdah below
Initializing to null:
GET /latest/ controllers.Find.findLatest(repo: String = null)
GET /latest/:repo controllers.Find.findLatest(repo: String)
<form action="#routes.Find.findLatest()" method="get">
Normally all form data go in the body and you can retrieve them in your action method with bindFromRequest() (see docs).
If you really want to pass one form element as a part of the URL then you have to dynamically compose your URL in JavaScript and change your route.
Your route could look like:
GET /latest/:repo controllers.Find.findLatest(repo: String)
And the JavaScript part like (I didn't actually test the code):
<form name="myform" action="javascript:composeUrl();" method="get">
....
<select name="repo">....</select>
</form>
<script>
function submitform() {
var formElement = document.getElementsByName("myform");
var repo = formElement.options[e.selectedIndex].text;
formElement.action = "/lastest/" + repo;
formElement.submit();
}
</script>
Cavice suggested something close to what I consider the best solution for this (since F.Option are not supported anymore with the default binders in Play 2.1 ).
I ended up leaving the route like:
GET /latest controllers.Find.findLatest(repo=null)
and the view like:
<form action="#routes.Find.findLatest(null)" method="get">
<select name="repo"> .... </select>
....
</form>
and in the controller:
public Result findLatest(String repoStr) {
if(repoStr==null) {
repoStr=Form.form().bindFromRequest().get("repo");
.....
This allows me to have a second route like:
GET /latest/:repo controllers.Find.findLatest(repo: String)

How to use indexof on array derbyjs

I have an array passed to my template and I want to see if I have the value stored in it:
{{each _page.friends as #friend}}
{{if _page.user.friends.indexOf(#friend.id)<0}}
<button>Add</button>
{{else}}
Already Friends
{{/if}}
{{/each}}
Apparently indexOf isn't a function, but the array (_page.user.friends) does seem to exist I can use it its own {{each}}....
Is there a way to do this? Or perhaps a better way?
I do not see support for indexOf mentioned in Derby View documentation. However, you can always use a view function to determine whether someone is a friend or not.
// in the view
{{each _page.friends as #friend}}
{{if isFriend(_page.user.friends, #friend.id)}}
<button>Add</button>
{{else}}
Already Friends
{{/if}}
{{/each}}
// in controller
FriendListController.prototype.isFriend = function(friends, friendId) {
return friends.indexOf(friendId) > 0;
};

knockout.js select element binding loses value on subsequent form submit

I have a knockout viewmodel getting populated from a JSON call.
In a select element in a form, I have a set of options (also coming from viewmodel) and the value, part of observableArray.
The issue is only with the select element and not with input ones -- when submitting the form, only the values that have been assigned to in select contain proper values. So the ones that have been successfully loaded from JSON and presented in form, but left unchanged, will be sent back to server as the first value from the options array.
HTML Form:
<form>
<table >
<thead>
...
</thead>
<tbody data-bind='foreach: ScaledCostEntries'>
<tr>
<td><input data-bind='value: StartDateString' class="startdate" type="text"/></td>
<td><select data-bind='value: InvoiceType, options: $root.InvoiceTypes'></select></td>
<td><a href='#' data-bind='click: $root.removeCost'>Delete</a></td>
</tr>
</tbody>
</table>
<button data-bind='click: addCost'>Add New Row</button>
<button data-bind='click: save' >Update</button>
</form>
In this code above the problem is with InvoiceType, part of the viewmodels ScaledCostEntries observableArray. (Also, if I swap the order of value and options, that will not put a selected value in the select element).
and the JS:
<script type="text/javascript">
$(function () {
var scaledCostModel = function () {
var self = this;
self.ScaledCostEntries = ko.observableArray([]);
self.InvoiceTypes = ko.observableArray([]);
self.addCost = function () {
self.ScaledCostEntries.push({
StartDateString: ko.observable(),
InvoiceType: ko.observable()
});
};
self.removeCost = function (cost) {
cost.IsDeleted = true;
self.ScaledCostEntries.destroy(cost);
};
self.save = function (form) {
jQuery.ajax({
url: '#Request.Url.PathAndQuery',
type: "POST",
dataType: "json",
contentType: "application/json; charset=utf-8",
data: ko.toJSON(self.ScaledCostEntries)
});
};
};
jQuery.getJSON('#Request.Url.PathAndQuery', function (data) {
ko.mapping.fromJS(data, {}, viewModel);
});
var viewModel = new scaledCostModel();
ko.applyBindings(viewModel);
});
</script>
So, to summarize, the issue is with viewmodel's property bound to a select element. When the select is left unchanged (not reselected), the viewmodel will have it's value as the first item from the options (InvoiceTypes) array, when posting to server.
In the end, I might be forgetting something trivial and this is my first more serious knockout.js attempt.
Note: InvoiceType is part of the ScaledCostEntries, which is observableArray.
InvoiceTypes is observableArray.
Both InvoiceTypes and ScaledCostEntries, come from JSON and ScaledCostEntries is sent back.
My assumption is that this is due to how the ScaledCostEntries are being handled on the server on the form submission.
I've run into the problem before (across various server-side frameworks) when I have a main model with a list of dependent models that are being added and removed, and a form submission is done against the main model to update everything.
The problem is that during form submission, when the values in the request are being mapped to the server-side model, blank values are taken to mean "no change" as opposed to "delete this". This works well with properties directly on a model, but doesn't work for lists of dependent models.
There are a couple of ways I've found of dealing with this: use Ajax to delete the underlying model and update the relationship with the main model when the 'remove' or 'delete' button is pressed in the view; or explicitly send the whole list of models each time and explicitly delete and rebuild the list on the server for each form submission. Each are applicable in different situations, and there are probably other approaches that may work well, too.