How to properly return an associative array back to a nunjucks template - gulp-nunjucks-render

Within my gulp nunjucks area, I created a custom filter that generates date & time information for the week. I'm stuck in knowing how to return an associative array back into my nunjucks template and how to reference any of the array values. Below is my setup.
const manageEnvironment = function (environment) {
environment.addFilter('ServiceDate', function (njDay) {
//...lots of logic here....
//...my initial attempt in prepping a return for my data....
var domDate = nSunday[1]["mdate"];
var domMonth = nSunday[1]["mmonth"];
var domHour = nSunday[1]["mhour"];
var domMin = nSunday[1]["mmin"];
var domDay = nSunday[1]["mday"];
var domMer = nSunday[1]["mmeridiem"];
// my OP question - but am I wasting my time in defining the above variables because there is a way to simply return `nSunday[1]` back to my nunjucks template? and us it like this...
/*
{{"Sunday"|ServiceDate.mdate}}
{{"Sunday"|ServiceDate.mmonth}}
{{"Sunday"|ServiceDate.mhour}}
{{"Sunday"|ServiceDate.mmin}}
{{"Sunday"|ServiceDate.mday}}
{{"Sunday"|ServiceDate.mmeridiem}}
*/
return {
//..not sure how...
}
});
}
//now generate nunjucks...
function genNunJucks(cb) {
return src(['src/views/*.html'])
.pipe(nunjucksRender({
path: ['src/views/', 'src/views/parts'],
ext: '.html',
data: {},
inheritExtension: false,
envOptions: {
watch: true
},
manageEnv: manageEnvironment,
loaders: null
}))
.pipe(htmlbeautify({
indentSize: 2,
"eol": "\n",
"indent_level": 0,
"preserve_newlines": false
}))
.pipe(dest('pub'));
cb();
}
Thanks for any education in the process of learning how to properly return an associative array back to a nunjucks template and accessing any of its values.

My first mistake was determining if I needed to set a filter or a global function to process my logic. After reading the docs, it appears that addGlobal is a better fit for my case.
I then returned my associative array as an object back into my template, which from there I'm able to iterate through. The full code...
from my gulpfile.js file
//define helper function for nunjucks
const manageEnvironment = function (env) {
function npcc_serviceDate(gDay) {
//...lots of code here to generate the next (gDay) info...
return nextEventDate; //return my associative array as an nunjucks object
}
env.addGlobal('serviceDate', npcc_serviceDate);
}
//generate nunjucks template...
function genNunJucks(cb) {
return src(['src/views/*.html'])
.pipe(nunjucksRender({
path: ['src/views/', 'src/views/parts'],
ext: '.html',
data: {},
inheritExtension: false,
envOptions: {
watch: true
},
manageEnv: manageEnvironment,
loaders: null
}))
.pipe(htmlbeautify({
indentSize: 2,
"eol": "\n",
"indent_level": 0,
"preserve_newlines": false
}))
.pipe(dest('pub'));
cb();
}
my main.html nunjucks file
I can now access various values of my object and place them anywhere in my template.
<ul>
{% for info in serviceDate("Sunday") %}
<li>{{ info.mday }}</li> //"Sunday"
<li>{{ info.mdate }}</li> //"19"
<li>{{ info.mmonth }}</li> //"June"
<li>{{ info.mhour }}</li> //"11"
<li>{{ info.mmin }}</li> //"30"
<li>{{ info.mmeridiem }}</li> //"am"
{% endfor %}
</ul>

Related

List.JS - Filter with comma separated values

I'm trying to create a dropdown to filter based on dates, but I would like to be able to comma separate the dates instead of initilizing each field as a separate filter, which would be slow when you account for hundreds of dates, and each list item having 30+ dates each.
I thought maybe list.js supports comma separated fields, but I can't find any obvious solution online.
Here is a simple codepen with the code working with a single field:
https://codepen.io/mauricekindermann/pen/QWyqzQL
This: <span class="stat filter_dates">1999,2099,2199</span>
Instead of this: <span class="stat filter_dates">1999</span>
Is this possible? Or do I need to initiate each date as a separate filter?
I didn't get any answers and couldn't find an obvious soultion to this. So the final result uses this method (JS script inside a PHP file)
<script>
var options = {
valueNames: [
'id',
<?
while($item= mysqli_fetch_array($query))
{
?>
'filter_<?=$item['id']?>',
<?
}
?>
]
};
$(document).ready(function()
{
if($('#filter').length > 0)
{
$('#filter').change(function ()
{
var selection = this.value;
<?
$i=0;
mysqli_data_seek($query, 0);
while($item= mysqli_fetch_array($query))
{
if($i==0){
$type='if';
} else {
$type='else if';
}
?>
<?=$type?>(selection == '<?=$item['id']?>')
{
userList.filter(function (item) {
return (item.values().<?='filter_'.$item['id']?> == selection);
});
}
<?
$i++;
}
if($i > 0){
?>
else {
userList.filter();
}
<?
}
?>
});
};
});
</script>

How to prevent individual form elements from updating using Meteor + React?

I have a Meteor + React single-page-application with a basic form in it. Data is collected from MongoDB using the createContainer method and passed to a form component. The problem I am facing is this. A user starts completing the form but, if the data that originally populated the form changes (by another user somewhere else in the world saving the form), the createContainer method will re-compute, which in turn pushes a new set of props to the form component and therefore overwrites what the user is typing in.
For many reasons, I cannot use the shouldComponentUpdate lifecycle method within the form component. One reason is that the form contains a select element, whose list of items should still accept reactive updates.
I need a way of halting certain reactive updates, but allowing others, whilst the user is completing the form. Any suggestions?
export default FormContainer = createContainer(( params ) => {
const dataFormHandle = Meteor.subscribe('FormsPub');
const dataFormIsReady = dataFormHandle.ready();
const dataListHandle = Meteor.subscribe('ListItemsPub');
const dataListIsReady = dataListHandle.ready();
let name = "";
let listItems = [];
let listSelectedValue = null;
if(dataListIsReady) {
listItems = collections.ListItemsColl.find({_id: ListId}).fetch();
}
if(dataFormIsReady) {
let formData = collections.FormsColl.find({_id: formId}).fetch();
name = formData[0].name;
listSelectedValue = formData[0].listSelectedValue;
}
return {
name,
listItems,
listSelectedValue
};
}, Form);
...
export default class Form extends Component {
constructor(props) {
super(props);
this.state = {
name: (this.props.name) ? this.props.name : "",
listSelectedValue: (this.props.listSelectedValue) ? this.props.listSelectedValue : null
};
}
componentWillReceiveProps(nextProps) {
this.setState({name: (nextProps.name) ? nextProps.name : ""});
this.setState({listSelectedValue: (nextProps.listSelectedValue) ? nextProps.listSelectedValue : null});
}
updateFormState(){
var name = e.target.name;
var val = e.target.value;
if(name == "name"){this.setState({name: val});}
if(name == "list"){
if( typeof e.target[e.target.selectedIndex] != "undefined" ) {
this.setState({listSelectedValue: val});
}
}
}
render(){
return (
<div>
<input type="text" name="name" value={this.state.name} onChange={this.updateFormState.bind(this)} />
<Select2
value={this.state.listSelectedValue}
name="list"
onChange={this.updateFormState.bind(this)}
options={{
minimumResultsForSearch: Infinity
}}
data={this.props.listItems}
/>
</div>
);
}
}
For the data in the form that you wish to be non-reactive simply specify reactive: false in your .find(), for example:
let formData = collections.FormsColl.find({ _id: formId },{ reactive: false }).fetch();
This will prevent the data from reactively updating while the form is open.

Merged two collections with foreach data not displaying in meteor template

I am new to meteor and have two Mongo collections, user and patient. I have concatenated them, in console this is displaying data but not in the html template. I am able to show data in objects in the console but not on my html template as seen in the image below:
What I have tried so far:
Template.showpatient.helpers({
'patientName': function () {
var tmpCollection = Patients.find() // `enter code here`
tmpCollection.forEach(function (myDoc) {
var a = Patients.find({"UserID": myDoc.UserID}).fetch().concat(user.find({"UserID": myDoc.UserID}).fetch())
return a;
console.log(a);
});
}
})
and at template side I am showing the data like this
{{#each patientName}}
<tr>
<td>
{{FirstName}}
</td>
<td class="f20">
{{LastName}}
</td>
<td>{{Gender}}</td>
<td>{{age}}</td>
<td>{{Email}}</td>
<td>{{HomePhone}}</td>
<td>{{CellPhone}}</td>
<td>{{Fax}}</td>
<td>{{AddressLine1}}</td>
<td>{{AddressLine2}}</td>
<td>{{City}}</td>
<td>{{State}}</td>
<td>{{Zip}}</td>
<td>{{IPAddressCreatedFrom}}</td>
<td>{{MaritalStatus}}</td>
Use the collection's map() method to iterate over the cursor, access the patient documents and use the underscore method _.extend() to generate a merged list of the patients and users joined on the UserID field. The following demonstrates this:
Template.showpatient.helpers({
'patientName': function () {
var merged = Patients.find().map(function(patient){
var user = User.findOne({"UserID": patient.UserID}) // get the
return _.extend(patient, user);
});
console.log(merged);
return merged;
}
})

Form and Security in Symfony

Let's say I want a form so users can change/update their own comments. (comment that they leave for an article for exemple).
Let's say I get all comments linked to an article (comments of any users)... and I incorpore then into a form.
Controller
$comments = $em->getrepository('comment')->getAllComments_from_an_article($article); // retrun all comments related to an article whatever the author/users of the comments.
$comments = array('comments' => $comments);
$form = $this->createForm(new CommentsType(), $comments);
CommentsType
builder->add('comments','collection', array('type'=> new commentType() ));
CommentType
builder->add('comment_text','textarea');
Now I display only comment that belong to the current user:
{% for comment in form.comments %}
{% if app.user = comment.user %} {{ form.widget(comment.comment_text) }}
{% endfor %}
My question is, will it be secure like this or there is a risk that the current user might be able to edit comment of other users?
It's often best to start at the end of the security articles and work backwards. So what you want to achieve is this:
class CommmentController
{
public function editAction($request,$comment)
{
if (!$this->isGranted('EDIT',$comment)
{
throw new AccessDeniedException();
So you are tapping in to the existing security system and asking it if the current user is allowed to update the specific comment in question. Your CommentVoter is what actually decides.
Chopped down a bit, your voter might look like:
class CommentVoter implements VoterInterface
{
public function supportsAttribute($attribute)
{
return in_array($attribute, array('VIEW','EDIT');
}
// Only deal with comments for this voter
public function supportsClass($class)
{
// Actually better to inject the class name but oh well
$supportedClass = 'AppBundle\Entity\Comment';
return $supportedClass === $class || is_subclass_of($class, $supportedClass);
}
public function vote(TokenInterface $token, $comment, array $attributes)
{
// Only deal with comments
if (!$this->supportsClass(get_class($comment))) {
return VoterInterface::ACCESS_ABSTAIN;
}
$user = $token->getUser();
switch($attributes[0])
{
'EDIT':
// It all comes down to here
if ($user->getId() === $comment->getUser()->getId())
{
return VoterInterface::ACCESS_GRANTED;
}
break;
}
return VoterInterface::ACCESS_DENIED;
}
Wire this up with an entry in your services.yml file: http://symfony.com/doc/current/cookbook/security/voters_data_permission.html#declaring-the-voter-as-a-service
As you might imagine, your voter can be more sophisticated. For example, admin users might be able to edit all comments. Easy enough to add the code.
If you implement the VIEW permission then your template becomes:
{% for comment in form.comments %}
{% if is_granted('VIEW',comment.vars.value) %} {{ form.widget(comment.comment_text) }}
{% endfor %}
Hope this points you in the right direction.

How to create nested <ul> <li> tags with HtmlTags (FubuMVC)

I have no previous experience with FubuMVC HtmlTags library, and I simply got stuck when trying to accomplish a simple nested structure like this:
<ul>
<li>text</li>
<li>text
<ul>
<li>subtext</li>
<li>subtext</li>
</ul>
</li>
<li>text</li>
</ul>
Here's how I have it when building the string:
public static HtmlString ChildNodesRecursive(DocumentNode documentNode)
{
var tag="";
if (documentNode.Children.Count > 0)
{
tag = "<ul>";
foreach (var c in documentNode.Children)
{
tag += "<li>" + c.Name;
tag += ChildNodesRecursive(c);
tag += "</li>";
}
tag += "</ul>";
}
return new HtmlString(tag);
}
Works fine, but I like to use HtmlTags library (outside of FubuMvc, with the HtmlTags separate Nuget).
Edit : I got inspiration from both answers and came up with what I needed. So here's the code I ended up using.
public static HtmlTags.HtmlTag ChildNodesRecursiveHtmlTag(DocumentNode documentNode)
{
var ul = new HtmlTags.HtmlTag("ul");
foreach (var c in documentNode.Children)
{
var li = new HtmlTags.HtmlTag("li");
li.Add("a").Attr("href",c.ContextFullPath).Text(c.Name);
if (c.Children.Count > 0)
{
li.Children.Add(ChildNodesRecursiveHtmlTag(c));
}
ul.Children.Add(li);
}
return ul;
}
I can give you an example which may make things clearer to you:
var ul = new HtmlTag("span").AddClass("form_input");
ul.Modify(t =>
{
foreach (var value in choice)
{
t.Add("input")
.Attr("type", "radio")
.Attr("name", request.Accessor.Name)
.Attr("value", value)
.Add("span")
.AddClass("fixed-width")
.Text(value);
}
});
Gives you something like
<span class="form-input">
<input type="radio" name="bla" value="foo" />
<span class="fixed-width">foo</span>
...etc...
</span>
You can carry on nesting tags with modify and filling in the lambda. I think you will find that what you want to do is possible with the bits of syntax shown.
This code:
var root = new HtmlTags.HtmlTag("ul");
root.Add("li").Text("item1");
var child = root.Add("ul");
child.Add("li").Text("item2");
return root.ToPrettyString();
produces the following output:
<ul>
<li>item1</li><ul>
<li>item2</li>
</ul>
</ul>