ArcGIS JS API - Conditional logic within popup - javascript

I have created a custom popup with the popupTemplate function with ArcGIS JS API 4.x
It would be great to introduce some conditional logic into my popups. For example, I am currently displaying a hyperlink to a website driven by an attribute within my layer. Not all features have associated websites, so ideally the conditional logic would only display text if the URL attribute was populated. The code below shows i) my existing popup and ii) how I am guessing I would insert a conditional value.
var template = {
content: [
{
type: "text", // TextContentElement
text: "<h1><font color='#c94600'>Click <a href={URL} target='_blank'><b>here</b></a> for video </font></h1>"
},
{
type: "text", // TextContentElement
text: conditionalFunction
},
]};
where conditionalFunction = something to the effect of...
if URL.attribute not null:
> return URL
else:
> return null
Can this be done easily with simple/basic Javascript?? (i'm very much a beginner)? If I can get this working, the next step would be to show a conditional table :)
Any help much appreciated.

You have a couple of ways to do this, you can use functions or promises. Basically instead of defining the content in a "static" manner, you do it "dynamic". In this way each time the popup display the content will be generated using the feature data.
Take a look at this example of ArcGIS. It uses function to set the content of the template, and it dinamically define the arrow style to display (up or down and color).
You can do something similar, like this,
var template = {
content: function(feature) {
var div = document.createElement("div");
if (feature.graphic.attributes.URL) {
div.innerHTML =
"<h1><font color='#c94600'>Click <a href=" +
feature.graphic.attributes.URL +
" target='_blank'><b>here</b></a> for video </font></h1>";
}
return div;
},
outFields: ["URL"]
};
I am assuming that URL is the attribute that contains the url.

Related

Multiple tables with individual search inputs in serverSide mode using DataTables

Application is using:
DataTables 1.10.18
jquery 3.2.1
PHP back-end
lodash 4.17.4
The application contains a web page which consists of multiple DataTables. Each of these uses serverSide: true (server-side mode) to obtain the data via an ajax endpoint which returns JSON data.
The tables are initialised as follows:
On page load several <table>'s are rendered. I'm using a jquery .each() to initialise the DataTable for each one:
$.each($('table'), function () {
$(this).DataTable({
processing: true,
serverSide: true,
searching: false,
ajax: {
data: {
table_id: $(this).attr('id')
},
url: '/get-data.json',
},
...
});
Each <table> has an ID. This is passed via ajax in the data: attribute. The endpoint /get-data.json returns data based on the table ID. In other words it knows "which table" the data should be obtained for based on this ID.
I want to be able to do searching on tables, but it must be done server-side. For this reason my initialisation code in (1) sets searching: false because this effectively disables the client-side search facility that DataTables provides (which we can't use in this instance as searching must be done server-side).
The problem I'm facing is how to create search inputs for each table, make an ajax call and update the appropriate table. I want the search to work in realtime after >=3 characters have been entered. Critical to this question is that 1 search input is responsible for searching 1 DataTable - it's not a search feature where the input can update "any/every table on the page" which is a commonly described pattern in other questions. 1 input : searching 1 table in this case.
My plan has been as follows - each table referenced in point (2) has an ID. I need to create unique inputs. So if I have tables with ID's #table1, #table2, #table3 I can easily create:
<input type="text" name="table1_search" id="table1_search">
<input type="text" name="table2_search" id="table2_search">
<input type="text" name="table3_search" id="table3_search">
I then detect if any changes have occurred on inputs:
$('input[type="text"]').bind("keyup change input",
function (e) {
// Ignore tab key for keyup event otherwise it'll fire an ajax request that does nothing useful.
if (e.which !== 9) {
processSearch.call(this);
} else {
e.preventDefault();
}
});
var prev_value = {};
function processSearch() {
var obj = $(this),
search_id = obj.attr('id'), // ID of input
search_value = obj.val(); // Value of input
// There's been no change to the field, ignore.
if (prev_value[search_id] === search_value) {
return;
}
prev_value[search_id] = search_value;
/* Wait until at least 3 characters have been entered, or user has cleared the input */
if (search_value.length >= 3 || (!search_value)) {
debouncedDraw({search_id: search_id, search_value: search_value});
}
}
The above code does what I need in terms of waiting for >=3 characters to be entered. I'm then executing a function called debouncedDraw which passes an object containing search_id and search_value. These refer to the input ID and value respectively, e.g. if I type "foo" into #table1_search then the object is:
{search_id: 'table1_search', search_value: 'foo'}
The debouncedDraw function looks like this. This is using lodash to limit the rate at which the function can fire. The point here is to stop it making needless ajax requests based on a question I asked a few years ago here: DataTables - kill ajax requests when a new one has started:
var debouncedDraw = _.debounce(function (opts) {
console.log(opts);
}, 500);
At the moment this will just console.log the object given above.
I'm unsure of the best way to proceed at this point. I need to re-run /get-data.json via ajax and then update the appropriate table.
I could access the request data and split the search_id based on the underscore to work out which table ID the data is for (e.g. table1_search targets #table1). I then need to write this data back to the appropriate table (#table1 in this case).
I can't help but think I'm going about this in a convoluted way and wondered if DataTables itself has any better ways of supporting this? It seems quite a basic requirement (multiple searchable tables in serverSide mode). But I can't find any posts which refer how to do this specifically.
All the "gotchas" I've experienced over the years is encapsulated in the snippet below. This is the basic template I always use when creating a new datatable. You can create as many datatables on a page as you need using this pattern.
Personally I would use a different ajax url path/route for each table so that table logic is in separate files in the backend... but it is possible to have all the data logic in a single backend file. I modified my usual template to suit that.
<script> //I usually put the script section in the head tag
var table_1; //declare your table var here and initialize as a datatable inside document ready below.
$(document).ready(function() {
table_1 = $('#table_1').DataTable( {
dom: "Bfrtip",
ajax: {
url: "/get-data.json?table=table_1", //add query string var for backend routing
type: "POST" //use POST to not have to deal with url encoding various characters
},
serverSide: true,
searchDelay: 2000, // use this instead of custom debounce
processing: true, // optional visual indicator that a search has been sent to backend
lengthMenu: [ 10, 25, 50, 75, 100 ], // define per page limits. first value will be the default
buttons: [
"pageLength" // per page drop down button. i usually override/extend the default button
],
columns: [ // column definitions of json data fields
{ data: "col_1", title: "ID", width: "1%" }, // width: 1% makes col width as small as possible
{ data: "col_2", title: "Label 2", visible:false }, //visible: false allows you access to field data without displaying to user
{ data: "col_3", title: "Label 3", render: function ( data, type, row ) { //render allows combining of fields into single column
return data + ' <small>('+row.col_2+')</small>'; // data will be col_3 value. row.col_2 is how you reference col_2 value
} },
{ data: "col_4", title: "Label 4", searchable:false }, //searchable: false set this field to not be used in search
],
rowId: 'col_1' //sets the tr row id to the value in this column. useful for DOM and other manipulation later
} );
}
</script>
<table id="table_1" class="table table-striped table-bordered table-sm" style="width:100%"></table>
<!-- If you define title attributes in col definitions above you don't need to create html table headers/footers. Just an empty table tag will do. -->
With this pattern you can utilize the built-in search input that comes with datatables for your use case with server-side processing on all tables.
There's a method behind my madness which I tried to document in the script comments on each line. Let me know if you have a question on something. I'm thinking this is bounty worthy.
For reference, when developing a new app using datatables I basically live on this page https://datatables.net/reference/option/
Edit 1
Inside your existing debounced drawTable function you could do something like this:
function drawTable(id) {
$('#'+id).DataTable().ajax.url( 'get-data.json?table_id='+id+'&foo=bar' ); //update ajax url of existing dt - if necessary
$('#'+id).DataTable().search(search_input_val).draw(); // fire ajax request with value from your custom search input
}
I'm fairly certain you will need to set "searching" to true though for this method to work.
Edit 2
Another way I just thought of, without using dt search. Pass all your data through modified url and load/reload.
$('#'+id).DataTable().ajax.url( 'get-data.json?table_id='+id+'&search=foo' ).load();
You could then get rid of all the debounce stuff if you use a button click listener or an onblur listener on the input field and fire the same command above.
Have you seen this? https://datatables.net/reference/api/%24.fn.dataTable.util.throttle()
I've never used it before, but it looks like a debounce. The example on the page shows it being used for .search()
I've implemented the following but would prefer a better solution as I don't think this is efficient and it definitely isn't elegant!
Taking the code from the question I modified the debounce function as follows:
var debouncedDraw = _.debounce(function (opts) {
// Destroy the existing DataTable.
$('#' + opts.search_region).DataTable().destroy();
// Re-run the drawTable method to get the new DataTable with the search results
drawTable(opts.search_region);
}, 500);
I introduced a function called drawTable which takes the ID of a <table> and runs the DataTables initialisation code. The ajax object was also modified to take into account anything entered into the search keywords input for the given table ID:
function drawTable(id) {
$id = $('#'+id); // Convert string ID to jquery identifier
$id.DataTable({
// DataTable initialisation code as per question
ajax: {
data: {
table_id: id,
keywords: $('input[name="keywords_' + id + '"]').val() // search keywords for table_id
},
url: '/get-data.json',
},
// ... code as per question
});
}
The $.each() was modified so that it detects the ID of each <table> on page load and calls drawTable:
$.each($('table'), function () {
drawTable($(this).attr('id'));
});
This "works" in that it creates each of the required DataTable's on page load, and also handles the search. The search input names were modified to the format: keywords_ plus the ID of the table, e.g. keywords_table1.
I don't think this is efficient because I'm having to call destroy on my DataTable. As per the docs:
This has a very significant performance hit on the page, since a lot of calculations and DOM manipulation is involved, so if you can avoid this, and use the API, that is very strongly encouraged!
However the reason I'm doing this is also as given in the same docs:
DataTables does not allow initialisation options to be altered at any time other than at initialisation time. Any manipulation of the table after initialisation must be done through the API
Well, I'm not using the client-side search feature as I'm having to do searching server-side. So I'm unsure whether manipulating the table via the API would actually help in this instance anyway.
Are there better ways of achieving this?

How can i display content atributes using .ftl? Alfresco

I want to get attributes of a content using an ftl. Does anyone have an example or idea of ​​how to do it. thank you
Content Props
Workflow Form
You are asking to show a content property on a workflow form. The workflow does not have direct access to the content properties. And, there could be many pieces of content in the workflow, so which content are you talking about?
So, the way you have to do this is write some JavaScript in your workflow process that iterates over the documents in your workflow package to gather up the values you want to display on the workflow form, then set those values on one or more process variables.
If you have configured the workflow form in share to display those variables, the values will display on the workflow form.
Get name (or other properties) of content using javascript - Alfresco
var title;
var unitPrice;
Alfresco.util.Ajax.jsonGet(
{
url: Alfresco.constants.PROXY_URI_RELATIVE + "api/metadata?nodeRef=" + nodeRefContext + "&shortQNames=true" ,
successCallback:
{
fn: function(response)
{
if (response.json)
{
title=response.json.properties["cm:title"];
alert(title);
unitPrice=response.json.properties["art:unitPrice"];
alert(unitPrice);
}
},
scope: this
},
failureCallback:
{
fn: function(response)
{
Alfresco.util.PopupManager.displayPrompt(
{
failureMessage: this.msg("message.failure")
});
},
scope: this
}
});
reference link Get filename of workflow with javascript

Materialize Forms Accepting links

I use materialize often and ran into a little issue within one of my apps. To make it simple I want to basically include a link within a form input (materialize) that will send into a chat feature I made. When generating the tag, the form cannot read the text (because it is rejecting the html generated link format presumably). Does anyone knowledgable of forms with materialize know if forms can accept links and if so what kind of format or special script I would need to include to make the form accept text in the form of a link. It could be another issue as well. Any input would be appreciated!
Code in question:
// function for pre-filling the information in the modal on the chat button press
function handleChatForm() {
$.get("/api/user_data", {}, function(data) {
$('#sender-name').val(data.firstName);
$('.sender-name-label').addClass('active');
});
var currentChatNote = $(this).closest('.addedNoteRow').data("note");
console.log(currentChatNote);
console.log(currentChatNote.title);
var noteObjectForChat = {
title: currentChatNote.title,
body: currentChatNote.body
};
var noteLink = $('<a>');
noteLink.data('noteObject', noteObjectForChat);
noteLink.attr("href", "/cms");
noteLink.append(currentChatNote.title);
$('#new-message-body').val(noteLink)
$('.new-message-label').addClass('active');
}
/** Listener for send button to push data into firebase **/
sendButton.on('click', function() {
var msgName = nameInput.val().trim();
var msgText = textInput.val().trim();
myFirebase.push({ Name: msgName, Text: msgText });
textInput.val("");
});

Windows 8/ Metro UI data-binding javascript

I am creating an application using javascript/HTML for windows 8 which basically displays text which is pulled from an html file.
I am using the data.js file to organise groups and items. One of the properties is the 'url' which stores the url of the html page which contains the main content for the application.
I came up with this code to retrieve the html code from the html page which contains the content to be displayed:
WinJS.UI.Fragments.renderCopy(url)
.done(function (fragment) {
return fragment;
});
How do I run this code for each item in the array in data.js and bind the data so that the content is from the HTML page, and the headings/titles are from the data.js file?
I apologise if I'm causing any confusion. I would appreciate any help.
Thanks.
Assuming you want to stick with the layout of data.js and not create your own data classes, I would use a custom renderer for the listview.
Something like this...
var customRender = WinJS.Utilities.markSupportedForProcessing(function (itemPromise) {
var currentItem = {};
return itemPromise.then(function (item) {
currentItem = item;
var myDiv = document.createElement("div");
return WinJS.UI.Fragments.renderCopy("/html/1_BasicFragmentLoad_Fragment.html", myDiv)
})
.then(function (fragment) {
var itemTemplate = WinJS.Utilities.query('.itemtemplate')[0];
currentItem.data.title = fragment.innerHTML;
return itemTemplate.winControl.render(currentItem.data);
});
}
);
In this example, I am binding the content of a html fragment to the title of a given item from data.js. You will need to update the itemtemplate and bind the title element to innerHTML instead of textContent.
<h4 class="item-title" data-win-bind="innerHTML: title"></h4>
You will also need to assign the custom renderer to the listview. You can do this in the HTML markup or just change the template js in groupItems.js to this...
listView.itemTemplate = customRender;
If you were to create your own data classes, you may want to put the promise chain from the customer renderer into the class constructor, eliminating the need for a customer renderer.

How should I handle Asp.net MVC URLs in javascript calls?

I am attempting to write a javascript heavy portion of my Asp.net MVC Web App (this portion of the website is a RIA using Extjs). However, I have come up to a standstill at the correct way to handle URLs in the javascript.
For example, right now I have an Ajax call to the List action in the ObjectsController, which resides in the Reading area. The List action takes a parameter of documentId (int). As of right now, this maps to /Reading/Objects/List since I have no changed routing yet (the site is too young at the moment to finalize routes). Normally in a view, to put this URL in a string I would do #Html.Action("List", "Objects", new { area = "Reading", documentId = 3).
However, this doesn't work when dealing with javascript, since javascript isn't parsed by a viewengine.
To get around this, I have a very small view that returns javascript constants, such as URLs, that is loaded prior to my main application's js files. The issue is that I can't call Html.Action for this action because at constant creation time I (obviously) do not know what documentId the ajax calls are going to be, and if you exclude documentId from the Html.Action call an exception occurs. The documentId could change during the normal workflow of the application.
How do I handle this? I don't want to hardcode the URL to /Reading/Objects/List because if I change my routing for this (for a more user friendly json API), or this web app isn't hosted on the root of the domain, the URL will no longer be valid.
How does everyone else handle MVC URLs in their javascript calls?
Here's a safe technique that I've been using. Even if your route changes, your JavaScript will automatically conform to the new route:
<script>
var url = '#Url.Action("List", "Objects", new { area = "Reading", documentId = "_documentId_")';
var id = 100;
var finalUrl = url.replace('_documentId_', id);
</script>
"_documentId_" is essentially a dummy placeholder. Then inside my JavaScript, I replace "_documentId_" with the proper id value once I know what it is. This way, regardless of how your route is configured, your URL will conform.
Update: Dec 20
I just saw this interesting blog post. The author built a library that allows you to build routes inside of your JavaScript file with intellisense support in VisualStudio.
http://weblogs.asp.net/zowens/archive/2010/12/20/asp-net-mvc-javascript-routing.aspx
Personally I use unobtrusive javascript and avoid mixing markup with javascript. AJAX calls are normally triggered by clicking on some buttons or links:
#Html.ActionLink("click me", "List", "Objects",
new { area = "Reading", documentId = 3 }, new { id = "foo" })
and then in a separate js file I would attach and handle the onclick event (example with jquery):
$(function() {
$('#foo').click(function() {
$('#resultDiv').load(this.href);
return false;
});
});
As you can I didn't need to use any hardcoded URL in my javascript file. URLs should always be handled by the routing engine and generated with html helpers.
If it was a <form> instead of a link I would simply handle the onsubmit event (the same way) and use the form's action attribute to get the URL.
UPDATE:
After pointing out in the comments section that the documentId is known only at client-side you could do this:
#Html.ActionLink("click me", "List", "Objects",
new { area = "Reading" }, new { id = "foo" })
And then:
$(function() {
$('#foo').click(function() {
$('#resultDiv').load(this.href, { documentId: '123' });
return false;
});
});
Turns out, this was all solved by using Url.Action() instead of Html.Action(). Url.Action() is (so far) allowing me to generate URLS without all of the parameters. I am assuming that this only works when the route does not specify the parameters in the target URL itself.

Categories

Resources