Pass parameters to all views - variables

I have a play application (Play 1.2.4). In each page in my application I want to have access to the logged in user (using groovy). So I can perform permission check etc. I tried to put this in the main template just before the doLayout
#{set user:user/}
I can have access to the user in the main page of my application, but when going through links to other pages I can no longer have access to the user variable.

I think you had misunderstanding about using #{set} and #{get} tag on Play!Framework. Look at the documentation on #{set} and #{get} tag.
I give you example, you have three templates like follows:
main.html
<html>
<head>
<title>#{get 'title' /}</title>
</head>
<body>
#{doLayout /}
</body>
</html>
template1.html
#{extends 'main.html' /}
#{set title:header /}
<p>This is template1.html page</p>
template2.html
#{extends 'main.html' /}
#{set title:header /}
<p>This is template2.html page</p>
And the methods on controllers like these:
public static void template1() {
String header = "Template 1 Title"
render(header);
}
public static void template2() {
render();
}
This is the explanation, when you render template1.html, you may notice that we are set the page title (by using header variable) in template1.html. The title that we set, is retrieved on main.html by using #{get} on main.html.
Then look when you render template2.html, you may notice we are set the page title too. But the difference is in the controllers (on template2() method), you have not passed header variable value to the template. So, although you have set the title, the title have null value and the implication the title was not set when you render template2.html.
So, for your problem, I think when you insist to place #{set user:user /} on template and want to access user variable, you must pass the user variable on all controller methods before rendering the template. Ugh.. It's so inefficient!
Suggestion
I suggest you that you may use session to store the user data like userid, because it is accessible in all your application templates! Please look at the documentation here.
Hope this can make you understand the mechanism and will so

Related

htmx:afterSettle not working with hx-trigger

This code does not trigger a swap event, even though I can see that the afterSettle event is firing in the console.
<div id="product-gallery" hx-trigger="htmx:afterSettle" hx-get="{% url 'products' %}" hx-swap="outerHTML">
This works, but loops forever of course, with:
<div id="product-gallery" hx-trigger="load" hx-get="{% url 'products' %}" hx-swap="outerHTML">
I can see from htmx.logAll() that the htmx:afterSettle even is firing, it's just not triggering the above element. Have also tried htmx:afterSwap, which is also logged by logAll()
I'm trying to reload the gallery after a form has been swapped out (the form is inside this parent product-gallery div). Which I was hoping I could achieve by adding a from constraint:
<div id="product-gallery" hx-get="{% url 'products' %}" hx-swap="outerHTML" hx-trigger="afterSettle from:.product-form">
Structure is:
<div id="product-gallery">
<div id="product-form-1">
<form>
...
</form>
</div>
...
</div>
Update - it works! Followed solution 3 from https://htmx.org/examples/update-other-content/:
I added a header to my response in the form update view:
if form.is_valid():
form.save()
context = dict()
context['form'] = form
response = render(self.request, 'form_product.html', context)
response['HX-Trigger'] = 'productUpdate'
return response
Then I listen for this event in the gallery div:
<div id="product-gallery" hx-get="{% url 'products' %}" hx-swap="outerHTML" hx-trigger="productUpdate from:body">
The one bit of js I retain is for closing forms when they are valid:
htmx.on("htmx:afterSwap", function(evt) {
const eventIdTarget = evt['target'].id;
if (eventIdTarget === 'product-gallery') {
if ($("[id^=product-form] .alert-warning").length === 0) {
$.magnificPopup.close();
}
}
})
If you have troubles with http redirects, then this might help you:
If you want a response which was triggered via htmx to do a full page reload, then you should not return a http redirect response (302, aka as HttpResponseRedirect in Django).
You need to set the hx-redirect response header: https://htmx.org/reference/#response_headers
If you set hx-redirect and set the http response code to 302, then htmx will do a redirect on ajax-level (not on the full screen).
Next thing which might confuse new users: if you are used to the old post/redirect/get pattern, then there are good news: This is not needed any more.
If the client sends a http-post, and all data validates, you should return a http 2xx response containing the new HTML. There is no need for the outdated redirect/get dance.
If you think the htmx docs could get improved, then you might want to create a pull request to improve the docs.
AFAIK you can't use "afterSettle" like this: hx-trigger="htmx:afterSettle".
If you want to update a second part of the page, then you can use OOB (out-off-band):
The hx-swap-oob attribute allows you to specify that some content in a response should be swapped into the DOM somewhere other than the target, that is "Out of Band". This allows you to piggy back updates to other element updates on a response.
https://htmx.org/attributes/hx-swap-oob/
More about Update other content

How to integrate my code into a sitefinity project

We have a sitefinity Customer Portal. Now we need to add MVC pages to it. I understand how to add a page, and how to drag e.g. a list to the page's content. But I don't understand how I can create a controller and other c# code to populate the list and do other custom things. We cannot open the project in Visual Studio, and we have no access to the existing code.
First of all, you must sure your project run success on your local. You can check it by login to back end page.
Then you can create the MVC component like this: (you should create all of this in root/MVC folder)
Create controller first:
[ControllerToolboxItem(Name = "ImportCSV", Title = "ImportCSV", SectionName = "ImportCSV")]
public class ImportCSVController : Controller
{
// GET: ImportCSV
public ActionResult Index()
{
return View();
}
}
SectionName is title of content group for you custom
Title is the title of component
Name is used for code behind
Then you can create the views to show in page: (you have to create the views in MVC/Views/ImportCSV, sitefinity will recognize folder name to map in BE)
<h2>Upload File</h2>
<div class="form-group">
<input type="file" id="dataFile" name="upload" />
</div>
<div class="form-group">
<a onclick="upload()" class="button" id="btnupload">Upload</a>
</div>
You need to get access to the code then, controllers\models need to be compiled. You can get away with a lot directly in a cshtml file though which DOESN'T need to be compiled.
Could you download a new blank SF project that's on your version and start from scratch pointed at your DB? Copy over /App_Data and /ResourcePackages to the new project and just run it. Should work fine, but any page that has a custom widget on it that uses custom code would tank. Sorry I'm just not sure why you don't have the code. Could use JustDecompile to retrieve the actual code for custom widgets too I suppose.

Apply vue-katex to content loaded from static folder

I'm trying to make a blog using Vue as laid out in the excellent demo here. I'd like to include some mathematical formulas and equations in my blog, so I thought I'd try to use vue-katex. vue-katex formats my mathematical notation perfectly when I put all my KaTeX HTML directly into my Vue templates, but to create a useable blog I need to keep my content separate from my templates (as shown in the demo).
I can't get vue-katex to format HTML content in the static folder. That's what I'd like help with.
Setup
I cloned the github repo for the demo.
I added vue-katex to package.json:
"vue-katex": "^0.1.2",
I added the KaTeX CSS to index.html:
<!-- KaTeX styles -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-alpha2/katex.min.css"
integrity="sha384-exe4Ak6B0EoJI0ogGxjJ8rn+RN3ftPnEQrGwX59KTCl5ybGzvHGKjhPKk/KC3abb"
crossorigin="anonymous"
>
I added the import statement to src/App.vue:
import Vue from 'vue'
import VueKatex from 'vue-katex'
Vue.use(VueKatex)
and I added a simple line of HTML with KaTeX to the BlogPost template:
<p>Here's an equation in the actual Vue template: <div class="equation" v-katex="'X \\sim N(\\mu, \\sigma^2)'"></div></p>
As I said, this works - I see formatted mathematical notation in my blog post (URL http://localhost:8080/read/neque-libero-convallis-eget):
However, I need different equations for every blog post, of course.
So I tried adding KaTeX HTML to the "content" field in the JSON for the first blog post: static/api/post/neque-libero-convallis-eget.json. I changed the "content" line to:
"content": "Here's an equation in the static folder: <div class=\"equation\" v-katex=\"'X \\sim N(\\mu, \\sigma^2)'\"></div>",
This content appears on the page, but the equation doesn't render. I see this: (the text appears but no equation is shown)
When I use Developer Tools to inspect the HTML on the page, I see this:
You can see that vue-katex has been applied to the equation I put in the template directly: it has parsed the HTML I typed into lots of spans with all the mathematical symbols, which are showing perfectly.
However the KaTeX HTML I've added to the "content" in the static folder has simply been placed on the page exactly as I typed it, and is therefore not showing up as an equation on the page. I really need to keep my blog post content in this static folder - I don't want to have to create a different .vue file for each blog post, that defeats the point!
My question is: is there a way to manually "apply" vue-katex to the HTML I place in the static folder, when it loads? Perhaps there is something I can add to the plugins/resource/index.js file, since this contains the function that loads the data from the static folder?
Many thanks in advance for any help.
*Disclaimer: I'm definitely no expert / authority on what I'm about to explain!
One thing to remember is that Vue reads the templates you write, and then replaces them as reactive components. This means that although you often write Vue attributes like v-for, v-html or in this case v-katex these attributes are only useful up until the app or component is mounted.
With this in mind, if you have a Vue app that ajax loads some html, its not going to be able to rerender itself with those Vue bindings in place.
I have somewhat ignored your current set up and set about solving the issue in another way.
Step 1: Reformat your data from the server side
I've put the posts into an array, and each post contains the template (just a string of html) and the equations separately as an array. I've used [e1] in the post as a placeholder for where the katex will go.
var postsFromServer = [{
content : `<div>
<h2>Crazy equation</h2>
<p>Look here!</p>
[e1]
</div>`,
equations : [
{
key : 'e1',
value : "c = \\pm\\sqrt{a^2 + b^2}"
}
]
}];
Step 2: When the post is rendered, do some work on it
Rather than just use v-html="post.content", I've wrapped the html output in a method
<div id="app">
<div v-for="post in posts" v-html="parsePostContent(post)">
</div>
</div>
Step 3: Create a method that renders all the katex, and then replaces the placeholders in the post
methods : {
parsePostContent(post){
// Loop through every equation that we have in our post from the server
for(var e = 0; e < post.equations.length; e++){
// Get the raw katex text
var equation = post.equations[e].value;
// Get the placeholder i.e. e1
var position = post.equations[e].key;
// Replace [e1] in the post content with the rendered katex
post.content = post.content.replace("[" + position + "]", katex.renderToString(equation));
}
// Return
return post.content;
}
}
Here is the whole set up, which renders Katex:
https://codepen.io/EightArmsHQ/pen/qxzEQP?editors=1010

JavaScript .innerHTMLworking only when called manually

I've got a very simple function, of replacing the innerHTML of a element. I've been trying to debug this for hours but simply can't, and it's infuriating.
When called from a button press the JavaScript (as follows) works well, but when called from another function it doesn't work. I am totally lost as to why this might be, and its a fairly core part of my app
// This loaded function in my actual code is a document listener
// checking for when Cordova is loaded which then calls the loaded function
loaded();
function loaded() {
alert("loaded");
changeText();
}
function changeText() {
alert("started");
document.getElementById('boldStuff').innerHTML = 'Fred Flinstone';
}
Button press and HTML to replace
<div id="main">
<input type='button' onclick='changeText()' value='Change Text'/>
<p>Change this text >> <b id='boldStuff'> THIS TEXT</b> </p>
</div>
It is also here in full on JSFiddle
You are already changed the innerHTML by calling the function loaded(); on onLoad.
Put this in an empty file and same as .html and open with browser and try. I have commented the function loaded();. Now it will be changed in onclick.
<div id="main">
<input type='button' onclick='changeText();' value='Change Text'/>
<p>Change this text >> <b id='boldStuff'> THIS TEXT</b> </p>
</div>
<script>
//loaded();
function loaded() {
alert("loaded");
changeText();
}
function changeText() {
alert("started");
document.getElementById('boldStuff').innerHTML = 'Fred Flinstone';
}
</script>
The problem here is, that the element you're trying to manipulate is not yet existing when you are calling the changeText() function.
To ensure that the code is only executed after the page has finished loading (and all elements are in place) you can use the onload handler on the body element like this:
<body onload="loaded();">
Additionally you should know, that it's very bad practice to manipulate values by using the innerHTML property. The correct way is to use DOM Manipulations, maybe this can help you.
You script loads before the element (boldStuff) is loaded,
Test Link - 1 - Put the js in a seperate file
Test Link - 2 - put the js at the very end, before closing the <body>

How to extend dijit.form.button

I am trying to extend dijit.form.Button with an extra attribute but this is not working.Code is given below
In file1.js
dojo.require('dijit.form.Button');
dojo.extend(dijit.form.Button,{xyz: ''});
In file2.jsp
<script type="text/javascript" src="file1.js"></script>
<div dojoType="dijit.form.Button" xyz="abc"></div>
However when I look at the HTML of the created button (In chrome seen by right click and then selecting 'inspect element' option), it doesn't show xyz attribute.
You need to keep in mind that there's a distinction between the widget object and its HTML representation. When you extend dijit.form.Button, the xyz attribute is added to the widget class, but not automatically to the HTML that the widget will render. So in your case, if you do
console.debug(dijit.byId("yourWidgetId").get("xyz"));
.. you'll see that the button object does have the xyz member, but the HTML (like you point out) does not.
If you also want it do be visible in the HTML, you have to manually add it to the HTML rendering of the button. One way to do that is to subclass dijit.form.Button and override the buildRendering method.
dojo.declare("my.Button", dijit.form.Button, {
xyz: '',
buildRendering: function() {
this.inherited(arguments);
this.domNode.setAttribute("xyz", this.xyz);
}
});
If you add an instance of your new Button class in the HTML, like so:
<div dojoType="my.Button" xyz="foobar" id="mybtn"></div>
.. then the HTML representation (after Dojo has parsed it and made it into a nice looking widget) will contain the xyz attribute. Probably something like this:
<span class="..." xyz="foobar" dir="ltr" widgetid="mybtn">
<span class="..." dojoattachevent="ondijitclick:_onButtonClick">
<input class="dijitOffScreen" type="button" dojoattachpoint="valueNode" ...>
</span>