Vapor 3 Rendering leaf template with script inline - vapor

I am trying to render a template in Vapor 3 with Leaf. Most of my HTML is in my base.leaf. In the login.leaf template, I need to add a JS script. Trouble is when it gets to the function it breaks and renders the function. Can anyone tell me how to add these properly? Thanks for your help in advance. Here's what is giving me problems:
#set("content") {
<h1>#(title)</h1>
<div id="logo"><img src="images/global/journey_trax_logo.png" alt=""/></div>
<div id="sheet1" class="form_sheet">
<input type="text" class="text_input" id="name_field" placeholder="NAME">
<input type="password" class="text_input" id="password_field" placeholder="PASSWORD">
<div id="continue_btn1" class="text_btn" onClick="logIn();">Continue</div>
<p> </p>
</div>
<script>
var user_name = readCookie("user_name");
var user_password = readCookie("user_password");
document.getElementById("user_name").value = user_name;
document.getElementById("user_password").value = user_password;
function logIn() {
var baseURL = window.top.location.href;
if (baseURL.indexOf("#") != -1) {
baseURL = window.location.href.split("#")[0];
}
var url = baseURL + "login";
console.log("[logIn] post to URL: "+url);
console.log("[logIn] name: "+user_name+", password: "+user_password);
$.post( url,
{
name: user_name,
password: user_password
},
function(data) {
// Do something with the return data?
console.log("[logIn] callback data: "+data);
console.log("[logIn] handle succes or error");
//
parent.loadHTML('screens/home.html');
}
);
}
</script>
<style>
.text_input, select {
width: 100%;
margin-bottom: 30px;
}
.text_btn {
width: 100%;
margin-top: 20px;
cursor: pointer;
}
</style>
}
#embed("base")

Your fundamental problem is that the } characters in the Javascript are being captured by Leaf because it is inside the #set tag. You have two choices:
Leave it where it is escape all the instances of } inside your <script> tag to \}. As far as I can tell, it doesn't need { escaping in the same way; presumably, this is because there isn't a Leaf tag immediately preceding. This works reliably and you can use Leaf to generate your javascript server-side before sending it to the client.
Move the <script> and contents above (i.e. outside) the #set tag. If you do embed any Leaf tags inside the javascript, you'll need to start escaping any } characters belonging to the Javascript as option 1.

Related

Why does the iOS content go up when a element is added?

I'm using Ionic to build a chat app, but I'm facing a problem, when I run my app on the iOS environment, every time a new message is added to the content the content goes up without any reason.
I've tried everything but I still didn't find a way to fix it, note that this bug only appears with the iOS content but works perfectly on Android.
I've published a video to show this bug
here is the code that I'm using to update the messages
ngOnInit() {
this.chatID = this.route.snapshot.paramMap.get('id');
this.afs
.collection(`chat/${this.chatID}/messages`,(ref) => ref.orderBy('createdAt'))
.valueChanges({ idField: 'id' })
.subscribe((event) => {
this.data = event;
console.log(this.data);
});
}
This is the function to push the new message
addChatMessage() {
const { newMsg } = this;
this.chatID = this.route.snapshot.paramMap.get('id');
this.afs
.collection(`chat/${this.chatID}/messages`)
.add({
msg: newMsg,
profilePic: this.profilePic,
username: this.username,
from: this.user.getUID(),
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
})
this.newMsg = '';
}
And here is the HTML code
<div *ngFor="let message of data">
<div style="display: flex; justify-content: flex-end">
<div style=" display: flex;
flex-direction: column;
align-items: flex-end;
">
<ion-card class="my-message"
*ngIf="message.username == username">
<ion-label>
{{ message.msg }}
<br />
</ion-label>
</ion-card>
</div>
</div>

Accessing Databindings by braces instead of dot (.) operator

Have a template repeater that I am running, and showing data by the dot operator
<template as="dom-repeat" items="{{list}}" as="row" index-as="rowIndex"
<div>{{row.foo}}</div>
<div>{{row.bar}}</div>
</template
I was trying to get the item by braces
<div>row["foo"]</div>
<div>row['bar']</div>
but those dont work, but is there a way to make it work? I am trying to make this list dynamic based on some object metadata, generalizing it. That way i could run something like:
<div>{{row[getKey(rowIndex)]}}</div>
Edit:
I saw this: https://github.com/Polymer/polymer/issues/1504 which states that this is now allowed in Polymer 0.5, but there is an experimental branch in 1.0 called: Polymer Expressions at https://github.com/Polymer/polymer-expressions/tree/2.0-preview
which shows that it can be done.
The issue is that this is experimental therefore I dont want to add this to my production DartPolymer1.0 codebase.
I guess withh that said, are there workarounds for it? Im thinking I might try to do something with conversion of Objects to arrays for accessing and then just do a direct access.
Will post a follow-on answer if i can figure it out.
This is the answer I had come up with:
<style>
.table{
display:table;
}
.row{
display:table-row;
}
.cell{
display:table-cell;
}
.th{
display:table-cell;
font-weight: bold;
}
.table, .row, .cell, .th {
border: solid 1px black;
padding: 5px;
}
</style>
<div class="table">
<div id="headerRow" class="row">
<template is="dom-repeat" items="{{columnHeaders}}">
<template is="dom-repeat" items="{{convertMap(item)}}" as="prop">
<div class="th">{{prop.value}}</div>
</template>
</template>
</div>
<template id="myRepeat" is="dom-repeat" items="{{data}}" as="row">
<div class="row">
<template is="dom-repeat" items="{{getListByFilter(row)}}" as="prop">
<div class="cell">{{prop.value}}</div>
</template>
</div>
</template>
</div>
with the Dart code of:
#property
List<Map> columnHeaders = [];
#property
List<Map> data = [];
#reflectable
List<Map> convertMap(Map map, [__]){
List<Map> lst = map.keys.map((key)=> { "name": key, "value": map[key]}).toList();
return lst;
}
#reflectable
List<Map> getListByFilter(Map row) {
List<Map> headers = new List<Map>();
columnHeaders.forEach((Map ele) => headers.add(convertMap(ele)[0]));
List<Map> rowList = new List<Map>();
for (Map header in headers) {
print("in for loop");
Map node = new Map();
node["name"] = header["name"];
node["value"] = row[header["name"]];
rowList.add(node);
}
List keysToIgnore = rowList.map((Map element) => element["name"]).toList();
for (String key in row.keys) {
if (!keysToIgnore.contains(key)) {
Map node = new Map();
node["name"] = key;
node["value"] = row[key];
rowList.add(node);
}
}
return rowList.sublist(0, columnHeaders.length);
}
therefore when defining this element:
<myDataGrid data="{{testrows}}" columnheaders="{{headers}}"></myDataGrid>
where the dart objects are formed in the idea of:
List<Map> headers = [{"columnKey":"ColumnTextString"},....];
List<Map> testRows = [{"columnKey": data, "anotherColumnKey": dataB}];
where the columnKey has to be in the Row Objects.... and headers keys are a subset of the Row Objects'. Therefore, it will set an order of columns and use that as a referencing factor for the Row Data.
It will maintain that each row still has access to the full set of data for other means, but will only display what is currently set up in the headers.
This gets around the bracket operator fairly well.

Modal box "hidden" behind greyed out area

Im following along with http://multiplethreads.wordpress.com/tag/pagedown/
The example in the link above makes use of twitter bootstrap whereas i make use of zurb foundation.
Basically im trying to get a custom dialog box to insert images from my pagedown editior (sort of like how stackoverflow does it).
Im abale to pull up the modal box (foundation reveal) but it seems to be "behind" something and i cant seem to interact with it. Any ideas?
My code:
js file:
PH.ui.markdown = {
initialize: function () {
var markdownTextArea = $('textarea[data-markdown=true]');
if (markdownTextArea.length == 1) {
markdownTextArea.addClass('wmd-input')
.wrap("<div class='wmd-panel' />")
.before("<div id='wmd-button-bar'></div>")
.after("<div id='wmd-preview' class='wmd-preview'></div>");
var converter = Markdown.getSanitizingConverter();
var editor = new Markdown.Editor(converter);
editor.hooks.set("insertImageDialog", function (callback) {
//setTimeout(function () {
$('#myModal').foundation('reveal', 'open');
// }, 5000);
return true; // tell the editor that we'll take care of getting the image url
});
editor.run();
}
}
};
html:
<div id="myModal" class="reveal-modal">
<h2>Upload Image</h2>
<%= f.file_field :image %>
<button class="btn" id="insert_image_post">Insert Image</button>
<!-- <a class="close-reveal-modal">×</a> -->
</div>
I figured it out.
The wmd-prompt-background had a z-index of 1000:
<div class="wmd-prompt-background" style="position: absolute; top: 0px; z-index: 1000; opacity: 0.5; height: 1085px; left: 0px; width: 100%;"></div>
So i just added:
<style type="text/css">
#myModal {
z-index: 1500;
}
</style>
to my page and it worked.
One way to do it is to position the div of your modal directly under the <body>. This way you make sure that the modal is not part of elements with special z-index

angular directive compile order

I was trying to write a simple directive to generate a (potentially) more complex dom element. I am quite confused about what is going on here but I think the directive I use inside my directive get linked first? Anyway the element I am generating is not visible where it should.
Sorry for all that confusion, here is the plunkr:
http://plnkr.co/edit/vWxTmA1tQ2rz6Z9dJyU9?p=preview
I think the directive I use inside my directive get linked first?
Yes. A child directive's link function will execute before the parent's link function.
Here is a fiddle that shows two nested directives,
<div d1>
<div d2></div>
</div>
and it logs when the directives' controller and link functions are called.
There are a few issues with your Plunker:
Since you are using # for your isolate scopes, you need to use {{}}s in your attribute values:
<visible value='{{visible}}'>plop</visible>
<invisible value='{{visible}}'>plop</invisible>
Since $scope.visible is defined in your controller, I assume you meant to use that value, and not test.
In the invisible directive, you need to use isolate scope property value in your link function. Property visible is available to the transcluded scope (which is in affect if you use a template in your directive like #Langdon has) but not the isolate scope, which is what the link function sees.
var template = "<span ng-show='value'>{{value}}</span>";
Plunker.
If you want a simple directive, you're better off letting Angular do most of the work through ngTransclude, and $watch.
http://plnkr.co/edit/xYTNIUKYuHWhTrK80qKJ?p=preview
HTML:
<!doctype html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>trying to compile stuff</title>
<script src="http://code.angularjs.org/1.1.1/angular.js"></script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller="AppCtrl">
<input type="checkbox" ng-model="test" id="test" /><label for="test">Visibility (currently {{test}})</label>
<br />
<br />
<visible value='test'>visible tag</visible>
<invisible value='test'>invisible tag</invisible>
</div>
</body>
</html>
JavaScript:
angular
.module('app', [])
.controller('AppCtrl', function($scope) {
$scope.test = false;
})
.directive('visible', function() {
return {
restrict: 'E',
transclude: true,
template: '<span ng-transclude></span>',
replace: true,
scope: {
value: '='
},
link: function(scope, element, attrs) {
console.log(attrs);
scope.$watch('value', function (value) {
element.css('display', value ? '' : 'none');
});
console.log(attrs.value);
}
};
})
.directive('invisible', function() {
return {
restrict: 'E',
transclude: true,
template: '<span ng-transclude></span>',
replace: true,
scope: {
value: '='
},
link: function(scope, element, attrs) {
scope.$watch('value', function (value) {
element.css('display', value ? 'none' : '');
});
}
};
});

Notify panel similar to stackoverflow's

Remember the little div that shows up at the top of the page to notify us of things (like new badges)?
I would like to implement something like that as well and am looking for some best practices or patterns.
My site is an ASP.NET MVC app as well. Ideally the answers would include specifics like "put this in the master page" and "do this in the controllers".
Just to save you from having to look yourself, this is the code I see from the welcome message you get when not logged in at stackoverflow.
<div class="notify" style="">
<span>
First time at Stack Overflow? Check out the
FAQ!
</span>
<a class="close-notify" onclick="notify.close(true)" title="dismiss this notification">×</a>
</div>
<script type="text/javascript">
$().ready(function() {
notify.show();
});
</script>
I'd like to add that I understand this perfectly and also understand the jquery involvement. I'm just interested in who puts the code into the markup and when ("who" as in which entities within an ASP.NET MVC app).
Thanks!
This answer has a complete solution.
Copy-pasting:
This is the markup, initially hidden so we can fade it in:
<div id='message' style="display: none;">
<span>Hey, This is my Message.</span>
X
</div>
Here are the styles applied:
#message {
font-family:Arial,Helvetica,sans-serif;
position:fixed;
top:0px;
left:0px;
width:100%;
z-index:105;
text-align:center;
font-weight:bold;
font-size:100%;
color:white;
padding:10px 0px 10px 0px;
background-color:#8E1609;
}
#message span {
text-align: center;
width: 95%;
float:left;
}
.close-notify {
white-space: nowrap;
float:right;
margin-right:10px;
color:#fff;
text-decoration:none;
border:2px #fff solid;
padding-left:3px;
padding-right:3px
}
.close-notify a {
color: #fff;
}
And this is javascript (using jQuery):
$(document).ready(function() {
$("#message").fadeIn("slow");
$("#message a.close-notify").click(function() {
$("#message").fadeOut("slow");
return false;
});
});
And voila. Depending on your page setup you might also want to edit the body margin-top on display.
Here is a demo of it in action.
After snooping around the code a bit, here's a guess:
The following notification container is always in the view markup:
<div id="notify-container"> </div>
That notification container is hidden by default, and is populated by javascript given certain circumstances. It can contain any number of messages.
If the user is not logged in
Persistence: Cookies are used to keep track of whether a message is shown or not.
Server side generated code in the view:
I think stackoverflow only shows one message if you aren't logged in. The following code is injected into the view:
<script type="text/javascript">
$(function() { notify.showFirstTime(); });
</script>
The showFirstTime() javascript method just determines whether to show the "Is this your first time here?" message based on whether a cookie has been set or not. If there is no cookie, the message is shown. If the user takes action, the cookie is set, and the message won't be show in the future. The nofity.showFirstTime() function handles checking for the cookie.
If the user is logged in
Persistence: The database is used to keep track of whether a message has been shown or not.
Server side generated code in the view:
When a page is requested, the server side code checks the database to see what messages need to be displayed. The server side code then injects messages in json format into the view and puts a javascript call to showMessages().
For example, if I am logged into a view, I see the following in the markup at SO:
<script type="text/javascript">
1
2 var msgArray = [{"id":49611,"messageTypeId":8,"text":"Welcome to Super User! Visit your \u003ca href=\"/users/00000?tab=accounts\"\u003eaccounts tab\u003c/a\u003e to associate with our other websites!","userId":00000,"showProfile":false}];
3 $(function() { notify.showMessages(msgArray); });
4
</script>
So the server side code either injects code to call the "showFirstTime" method if the user is not logged in or it injects messages and calls "showMessages" for a logged in user.
More about the client side code
The other key component is the "notify" JavaScript module Picflight has de-minified (you can do the same using yslow for firebug). The notify module handles the populating of the notification div based on the server side generated javascript.
Not logged in, client side
If the user is not logged in, then the module handles events when the user X's out the notification or goes to the FAQ by creating a cookie. It also determines whether to display the first time message by checking for a cookie.
Logged in, client side
If the user is logged in, the notify module adds all the messages generated by the server into the notification div. It also most likely uses ajax to update the database when a user dismisses a message.
Though these are by no means official, the common practices that I follow would result in something like this:
Create the element that will act as the notification container in the markup, but hide it by default (this can be done numerous ways - JavaScript, external CSS, or inline styles).
Keep the scripts responsible for the behavior of the notification outside of the markup. In the example above, you can see there is an onclick as well as another function that fires on page load contained in the markup. Though it works, I see this as mixing presentation and behavior.
Keep the notification message's presentation contained in an external stylesheet.
Again, these are just my common practices stated in the context of your question. The thing with web development, as the nature of your question already shows, is that there are so many ways to do the same thing with the same results.
I see the following jQuery function? I beleive that injects the html into the div with id notify-container.
I don't understand how this JS is used and called based on certain events, perhaps someone can explain.
var notify = function() {
var d = false;
var e = 0;
var c = -1;
var f = "m";
var a = function(h) {
if (!d) {
$("#notify-container").append('<table id="notify-table"></table>');
d = true
}
var g = "<tr" + (h.messageTypeId ? ' id="notify-' + h.messageTypeId + '"' : "");
g += ' class="notify" style="display:none"><td class="notify">' + h.text;
if (h.showProfile) {
var i = escape("/users/" + h.userId);
g += ' See your profile.'
}
g += '</td><td class="notify-close"><a title="dismiss this notification" onclick="notify.close(';
g += (h.messageTypeId ? h.messageTypeId : "") + ')">×</a></td></tr>';
$("#notify-table").append(g)
};
var b = function() {
$.cookie("m", "-1", {
expires: 90,
path: "/"
})
};
return {
showFirstTime: function() {
if ($.cookie("new")) {
$.cookie("new", "0", {
expires: -1,
path: "/"
});
b()
}
if ($.cookie("m")) {
return
}
$("body").css("margin-top", "2.5em");
a({
messageTypeId: c,
text: 'First time here? Check out the <a onclick="notify.closeFirstTime()">FAQ</a>!'
});
$(".notify").fadeIn("slow")
},
showMessages: function(g) {
for (var h = 0; h < g.length; h++) {
a(g[h])
}
$(".notify").fadeIn("slow");
e = g.length
},
show: function(g) {
$("body").css("margin-top", "2.5em");
a({
text: g
});
$(".notify").fadeIn("slow")
},
close: function(g) {
var i;
var h = 0;
if (g && g != c) {
$.post("/messages/mark-as-read", {
messagetypeid: g
});
i = $("#notify-" + g);
if (e > 1) {
h = parseInt($("body").css("margin-top").match(/\d+/));
h = h - (h / e)
}
} else {
if (g && g == c) {
b()
}
i = $(".notify")
}
i.children("td").css("border-bottom", "none").end().fadeOut("fast", function() {
$("body").css("margin-top", h + "px");
i.remove()
})
},
closeFirstTime: function() {
b();
document.location = "/faq"
}
}
} ();
StackOverflow uses jQuery - the JS code you posted from SO is a jQuery call. It'll do exactly what you want with almost no code. Highly recommended.
I wrote this piece of Javascript that does just that including stacking, staying with you as you scroll like Stack Overflow's does and pushing the whole page down whenever a new bar is added. The bars also expire. The bars also slide into existence.
// Show a message bar at the top of the screen to tell the user that something is going on.
// hideAfterMS - Optional argument. When supplied it hides the bar after a set number of milliseconds.
function AdvancedMessageBar(hideAfterMS) {
// Add an element to the top of the page to hold all of these bars.
if ($('#barNotificationContainer').length == 0)
{
var barContainer = $('<div id="barNotificationContainer" style="width: 100%; margin: 0px; padding: 0px;"></div>');
barContainer.prependTo('body');
var barContainerFixed = $('<div id="barNotificationContainerFixed" style="width: 100%; position: fixed; top: 0; left: 0;"></div>');
barContainerFixed.prependTo('body');
}
this.barTopOfPage = $('<div style="margin: 0px; background: orange; width: 100%; text-align: center; display: none; font-size: 15px; font-weight: bold; border-bottom-style: solid; border-bottom-color: darkorange;"><table style="width: 100%; padding: 5px;" cellpadding="0" cellspacing="0"><tr><td style="width: 20%; font-size: 10px; font-weight: normal;" class="leftMessage" ></td><td style="width: 60%; text-align: center;" class="messageCell"></td><td class="rightMessage" style="width: 20%; font-size: 10px; font-weight: normal;"></td></tr></table></div>');
this.barTopOfScreen = this.barTopOfPage.clone();
this.barTopOfPage.css("background", "transparent");
this.barTopOfPage.css("border-bottom-color", "transparent");
this.barTopOfPage.css("color", "transparent");
this.barTopOfPage.prependTo('#barNotificationContainer');
this.barTopOfScreen.appendTo('#barNotificationContainerFixed');
this.setBarColor = function (backgroundColor, borderColor) {
this.barTopOfScreen.css("background", backgroundColor);
this.barTopOfScreen.css("border-bottom-color", borderColor);
};
// Sets the message in the center of the screen.
// leftMesage - optional
// rightMessage - optional
this.setMessage = function (message, leftMessage, rightMessage) {
this.barTopOfPage.find('.messageCell').html(message);
this.barTopOfPage.find('.leftMessage').html(leftMessage);
this.barTopOfPage.find('.rightMessage').html(rightMessage);
this.barTopOfScreen.find('.messageCell').html(message);
this.barTopOfScreen.find('.leftMessage').html(leftMessage);
this.barTopOfScreen.find('.rightMessage').html(rightMessage);
};
this.show = function() {
this.barTopOfPage.slideDown(1000);
this.barTopOfScreen.slideDown(1000);
};
this.hide = function () {
this.barTopOfPage.slideUp(1000);
this.barTopOfScreen.slideUp(1000);
};
var self = this;
if (hideAfterMS != undefined) {
setTimeout(function () { self.hide(); }, hideAfterMS);
}
}
To use it you must use jQuery and ensure there are no margins or padding on the body of your page.
The parameter that the AdvancedMessageBar takes is optional. If provided it will cause the bar to disappear after a certain amount of time in milliseconds.
var mBar = new AdvancedMessageBar(10000);
mBar.setMessage('This is my message', 'Left Message', 'Right Message');
mBar.show();
If you want to stack these then just create more AdvancedMessageBar objects and they'll automatically stack.

Resources