I am using Thymeleaf and Spring MVC for a project and I am displaying a list in Thymeleaf and I want to get the selected items and pass them when I press submit
This is my form with the list:
<form th:action="#{/trip-info}" th:object="${trip}" method="post">
....
<a class="list-group-item list-group-item-action" th:each="currentPerson : ${trip.employees}" th:text="${currentPerson.getFirstName() + ' ' + currentPerson.getLastName()}" onclick="addPerson(this)"></a>
</form>
I have the employees list displayed in my page, now I am not sure if I can pass to a controller the selected values or if I must use javascript and construct a body. Does anybody have any ideas ?
Thanks!
Transferring the selected form item is outside the role of 'Thymeleaf'.
'Thymeleaf' is a server side template engine that simply creates code in html.
The event (item select) changed by the client must be processed by JavaScript.
For a simple example,
Before submitting from the form, check the selected item in javascript and create a function that performs the submit.
If you need an example, show me your source code in more detail.
Additional comments -
There are two ways.
1. How does 'a tag' directly tell you about your event?
2. How to externally liseten events of 'a tag'
I wanted the '2' method, and you chose the '1' method.
Your source will be terminated with the href moved before executing the 'onclick' event.
Take a look at this example.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
some method<br/>
some method2<br/>
some method3<br/>
<script>
function someEvent(event){
console.log(event);
}
</script>
</body>
</html>
'href' works but is invalidated by void (0). Or moved to another window tab by target. And then onclick happens sequentially.
I can not speak English and the explanation is immature. I'm sorry.
Related
We have our own system which we need to integrate with MS Dynamics 365.For Example : In Accounts section we need to add an extra tab that loads IFrame or something that retrieves some extra information from our system.
The following are the things that I reached :
Inserting IFrame within a new Dashboard: (but it will not fetch specific account information, it will only pass the currently logged in user along with the organization name)
Unified Service Desk (USD): (we may add customization but this is a desktop app and we need it to be on web)
Microsoft flow: this would only work in the background when you create or edit an account (not sure if it has another functionality)
Extensions: Not sure how to use it to achieve the same functionality, I believe the solution may be here but I just need from where to start.
Has anybody done a similar thing before?
Thank you
You can definitely do it,
Here is how I just tried on one of my Trail Instance.
I added new Tab as you need, I called it "HTML Page"
On this Tab I added Webresource, you can add Iframe as well and call your external WebPage.
For my simple use case I created a simple HTML page as webresource in CRM and configured it to Webresource tab as below
Sample code for HTML. Dont worry about long html file. Mostly it is bla bla. What is of our importance is <body onload="myFunction()"> and then in
<script>
function myFunction() {
debugger;
alert("Account Id when from fromcontext is ");
alert(parent.Xrm.getformContext().data.entity.getId());
}
</script>
complete HTML code below
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>My first styled page</title>
</head>
<body onload="myFunction()">
<!-- Site navigation menu -->
<ul class="navbar">
<li>Home page
<li>Musings
<li>My town
<li>Links
</ul>
<!-- Main content -->
<h1>My first styled page</h1>
<p>Welcome to my styled page!
<p>It lacks images, but at least it has style.
And it has links, even if they don't go
anywhere…
<p>There should be more here, but I don't know
what yet.
<!-- Sign and date the page, it's only polite! -->
<address>Made 5 April 2004<br>
by myself.</address>
<script>
function myFunction() {
debugger;
alert("Account Id when from fromcontext is ", parent.Xrm.getformContext().data.entity.getId());
}
</script>
</body>
</html>
Also on Form Load of account I added additional Javascript. This javascript will create global variable which could be called from your webresource.
Article Link for additional Javascript
Sample code used for Javascript below
formContext=null;
function onload(executionContext){
debugger;
var formContext = executionContext.getFormContext();
Xrm.getformContext = function (){
return formContext;
};
Xrm.getParentAttribute = function (attrName) {
debugger;
return formContext.getAttribute(attrName);
};
Xrm.getParentControl = function (attrName) {
debugger;
return formContext.getControl(attrName);
};
}
Final Result will be something like below
Summary:
Create Wberesource/Iframe
Create Additiona Js on Load
Use global variable in your webresource.
I'm trying to use ajax with thymeleaf. I designed a simple html page with two input field. I would like to use addEventHandler for the value of first input text, then I want to send it to controller and make calculation, after that I need to write it in same html form in the second field which returns from controller.
For example:
first input text value -> controller (make calculation) -> (write value) in second input text.
My html page is
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link rel='stylesheet prefetch' href='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css'>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<input type="text" name="Thing" value=""/>
<script th:inline="javascript">
window.onload = function () {
/* event listener */
document.getElementsByName("Thing")[0].addEventListener('change', doThing);
/* function */
function doThing() {
var url = '#{/testurl}';
$("#fill").load(url);
alert('Horray! Someone wrote "' + this.value + '"!');
}
}
</script>
<!-- Results block -->
<div id="fill">
<p th:text="${responseMsg}"/></div>
</div>
</body>
</html>
My controller
#RequestMapping(value = "/testurl", method = RequestMethod.GET)
public String test(Model model) {
model.addAttribute("responseMsg","calcualted value")
return "test";
}
However I cannot call controller from ajax. Could you help me?
There are a few issues with your code. First of all, it looks like you're using the same template for both the initial loading of the application, and returning the calculated result.
You should split these two into different calls if you're using AJAX, since one of the goals of AJAX is that you don't need to reload an entire page for one change.
If you need to return a simple value, you should use a separate request method like this:
#GetMapping("/calculation")
#ResponseBody
public int multiply(#RequestParam int input) {
return input * 2; // The calculation
}
What's important to notice here is that I'm using #ResponseBody and that I'm sending the input to this method as a #RequestParam.
Since you will be returning the calculated value directly, you don't need the Model, nor the responseMsg. So you can remove that from your original request mapping.
You can also remove it from your <div id="fill">, since the goal of your code is to use AJAX to fill this element and not to use Thymeleaf. So you can just have an empty element:
<div id="fill">
</div>
Now, there are also a few issues with your Thymeleaf page. As far as I know, '#{/testurl}' is not the valid syntax for providing URLs. The proper syntax would be to use square brackets:
var url = [[#{/calculation}]];
You also have to make sure you change the url to point to the new request mapping. Additionally, this doesn't look as beautiful since it isn't valid JavaScript, the alternative way to write this is:
var url = /*[[ #{/calculation} ]]*/ null;
Now, your script has also a few issues. Since you're using $().load() you must make sure that you have jQuery loaded somewhere (this looks like jQuery syntax so I'm assuming you want to use jQuery).
You also have to send your input parameter somehow. To do that, you can use the event object that will be passed to the doThing() function, for example:
function doThing(evt) {
var url = [[#{/calculation}]];
$("#fill").load(url + '?input=' + evt.target.value);
alert('Horray! Someone wrote "' + this.value + '"!');
}
As you can see, I'm also adding the ?input=, which will allow you to send the passed value to the AJAX call.
Finally, using $().load() isn't the best way to work with AJAX calls unless you try to load partial HTML templates asynchronously. If you just want to load a value, you could use the following code in stead:
$.get({
url: /*[[ #{/calculation} ]]*/ null,
data: { input: evt.target.value }
}).then(function(result) {
$('#fill').text(result);
});
Be aware that $.get() can be cached by browsers (the same applies to $().load() though). So if the same input parameter can lead to different results, you want to use different HTTP methods (POST for example).
I'm trying to write a docpad plugin that will allow me to insert meta tags unique to each page, for example og:title or og:description. I've been able to accomplish this globally with the populateCollections event for global values, but have not been able to do this per page.
I'd like for this to work without the need for a template function so that the meta tag is inserted automatically based on the document's meta. One way might be to grab the contentRendered value in the writeBefore event and do string manipulation that way, but that seems hacky.
Any ideas?
This worked for what I needed. Basically, I'm getting the rendered content right before the file is written using the writeBefore event, and doing a very simple string replace which adds the meta tags and their unique values, which is pulled from the model in the collection.
writeBefore: (opts) ->
docPad = #docPad
templateData = docpad.getTemplateData()
siteUrl = templateData.site.url
for model in opts.collection.models
if model.get('outExtension') == 'html'
url = #getTag('og:url', siteUrl+model.get('url'))
title = #getTag('og:title', model.get('title'))
content = model.get('contentRendered')
if content
content = content.replace(/<\/title>/, '</title>'+url+title+description)
model.set('contentRendered', content)
# Helper
getTag: (ogName, data) ->
return "\n <meta property=\"#{ogName}\" content=\"#{data}\" />"
Great answer David, leaving this one if someone faced the same issue I did.
Check if meta tag is broken, if it is - don't render:
renderBefore: (opts) ->
for model in opts.collection.models
if model.get('date').toLocaleDateString()=='Invalid Date'
model.set('write', false)
docpad.log model.get('title')+' has broken date format!\n\n\n\n\n'
false
I am using partials in with collections. Adding what is needed in the document like this:
```
title: Meetings and Events
layout: page
description: "This is my custom description."
tags: ['resources']
pageOrder: 3
pageclass: rc-events
```
I needed a custom CSS class by page. Then you can call it in your default template like this.
<div id="main" class="container <%= #document.pageclass %>">
Should be the same for meta
<meta name="description" content="<%= #document.description) %>" />
or check your docpad.coffee file and put together helper function for prepared content based off of a default site value combined with a #document value. Then you can just call something like the default:
<meta name="description" content="<%= #getPreparedDescription() %>" />
Which is built by this helper function:
# Get the prepared site/document description
getPreparedDescription: ->
# if we have a document description, then we should use that, otherwise use the site's description
#document.description or #site.description
I am building an application that uses the Bootstrap Collapse component to render a sequence of panels, all of which will initially be in the collapsed state.
Since the page may contain many such panels and each of them may contain a large amount of content, it seems appropriate to populate these panels on demand, by executing an AJAX call when the user expands any panel.
The dynamic content of the page (including the markup for the panels) is rendered using AngularJS, and I assume it's possible to configure Angular to bind to an event on the panel elements, that results in their content being lazy loaded when they expand.
Unfortunately, after looking at the AngularJS docs and the available tutorials, I can't see how best to tackle this. Can anyone throw any light on it?
Thanks in advance,
Tim
This is way old, but the question might still come up now and then. I now find this to be the most suitable solution without polluting your controllers:
(myDirective loading its content via AJAX right after its creation.)
<accordion>
<accordion-group
heading=""
ng-repeat="foo in bar"
ng-init="status = {load: false}"
ng-click="status.load = true">
<myDirective ng-if="status.load"></myDirective>
</accordion-group>
</accordion>
each element created by ng-repeat gets its own $scope, so clicking ab accordion-group will result in only the respective directive being loaded.
edit:
depending on latency and the size of the data that's to be lazy loaded, you might consider using ng-mouseover instead of ng-click. That way loading starts some 100ms before the user opens the accordion which can reduce 'sluggishness' of your UI. Obviously there's the downside of occasionally loading content of groups that are never actually clicked.
#Tim Coulter, I've created something following the idea of #Stewie.
It can definitely be improved, but I guess it's a good starting point.
I've created a small directive to bind the click event of the accordion's panel. When the click event is fired, I passed the panel template via the panel-template= attribute and it updates the main-template which is used inside the panel.
It makes reference to 2 html files (panel1.html and panel2.html) that contains the content of the each panel.
I would recommend to create a service to fetch these files via AJAX - just the way you wanted.
On the code below I created a service called dataService for this purpose and you should bind it to the click event - so files are loaded on demand when the user clicks on it.
Note the the mainTemplate is a common panel to all accordions, so when it changes the all the accordions will have the same content, BUT I am assuming you want to display only one panel at time, right ?!
Anyway as I said before the logic can be improved to fix these little 'gotchas', but I believe the core functionality is there to start with. :)
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
<script>
var myApp = angular.module('myApp', []);
myApp.controller('AccordionDemoCtrl', ['$scope', 'dataService', function ($scope, dataService) {
$scope.oneAtATime = true;
$scope.mainTemplate = '';
$scope.groups = [
{
id: "001",
title: "Dynamic Group Header - 1",
content: "Dynamic Group Body - 1",
template: "panel1.html"
},
{
id: "002",
title: "Dynamic Group Header - 2",
content: "Dynamic Group Body - 2",
template: "panel2.html"
}
];
}]);
myApp.factory('dataService', [ '$http', function($http){
return {
getData: function() {
return // you AJAX content data here;
}
}
}]);
myApp.directive('accordionToggle', [function () {
return {
restrict: 'C',
scope: {
mainTemplate: '=',
panelTemplate: '#'
},
link: function (scope, element, iAttrs) {
element.bind('click', function(e){
scope.mainTemplate = scope.panelTemplate;
scope.$apply();
});
}
};
}]);
</script>
</head>
<body ng-controller="AccordionDemoCtrl">
<div class="accordion" id="accordionParent">
<div class="accordion-group" ng-repeat="group in groups" >
<div class="accordion-heading">
<a class="accordion-toggle" main-template="$parent.mainTemplate" panel-template="{{ group.template }}" data-toggle="collapse" data-parent="#accordionParent" href="#collapse{{ $parent.group.id }}">
Collapsible Group Item {{ $parent.group.id }}
</a>
</div>
<div id="collapse{{ group.id }}" class="accordion-body collapse">
<div class="accordion-inner">
<div class="include-example" ng-include="mainTemplate"></div>
</div>
</div>
</div>
</div>
</body>
</html>
I am new to MVC 3 and have come accross the following scenario:
First let me explain how I have setup my application:
All post backs to the server use jquery ajax which which return a view from the controller that either appended or prepended or replace a targeted div.
The Scenario:
I have come to a point where I would like to upload images but unfortunately because of the jquery ajax posting I cannot get the values for an html in C# Request.Files. I know there are plugins out there to help out with this but I would like to do this myself so I have created an <iframe> which i then use a bit of javascript and post the form targeted to the iframe (old classic way of doing things):
function UploadImg(SACTION) {
alert(SACTION);
validatorform.action = SACTION;
validatorform.target = "fraImage";
validatorform.method = "POST";
validatorform.submit();
}
the SACTION parameter looks like this #Url.Action("UploadFile", "Response"). This all works well as it hits the controllers action method and I can then save the image:
[HttpPost]
public ActionResult UploadFile(string ArticleID)
{
ViewBag.PreviewImage = cFileUploads.UploadFile(ArticleID, Request.Files[0]);
return View("ImagePreview");
}
I would now like to return a view to the iframe (simply to preview the image and then do a couple of other things but this is besides the point)
The View for previewing the Image:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form action="">
<img alt="" src="#ViewBag.PreviewImage" />
</form>
</body>
</html>
The Problem:
Unfortunately when I return the View (ImagePreview.cshtml) in C# the whole page is refreshed, all I want is for the iFrame to be refreshed. How should I return the view from the controller?
Fiqured out the problem, I had a bit of javascript that was replacing the contents of a div that the iframe was sitting in ... (slap on my forehead). All working perfectly now :)
Will leave this question here just incase ..