I have a function:
function images_styles(Isel){
if (Isel==1){
var marker_pic="/sites/all/themes/shakhty/images/map/icons/1.png";
}
else{
var marker_pic="http://www.openlayers.org/dev/img/marker.png";
}
var styleImage = new OpenLayers.Style({
graphicWidth: 43,
graphicHeight: 65,
graphicYOffset: -28,
label: "${label}",
externalGraphic: marker_pic,
fontSize: "1em",
strokeOpacity:5
});
var vectorImage = new OpenLayers.Layer.Vector("Images", {
styleMap: new OpenLayers.StyleMap({
"default": styleImage
})
});
map.addLayer(vectorImage);
}
I want to change images (marker_pic) when user chooses variant in form. But when the variant has choosen for the first time, the image doesn't change, until i reload page.
What should i do?
How to refresh images every time user choose another variant?
To update your vector layer with a new external graphic, try the following:
vectorImage.refresh({force:true});
Alternatively, the following should work as well:
vectorImage.redraw();
Related
First, please pardon the novel below, but this is fairly hard to describe.
I have a fairly complex application that I'm working on at the moment using gviz through HtmlService of a GAS application.
This is more of a client side gviz API question though.
I'm loading 2 data tables through the query method and combining them through data.join. This resulting dataTable is the source for a data view.
The view is then used to generate a dashboard with multiple category filters and a single tableChart. I have a button which toggles between two filter sets for both the view rows and columns displayed. The concept is to load all records and toggle between pending and completed showing the relevant columns to the user.
This part is actually working perfectly well. The issue that I have is a data update issue. Every time the visualisation draws, I iterate through the resulting table and attach actions to various columns.
One such action opens a jQueryUI dialog with the rows values and allows the user to make modifications and submit to the server. When it submits, rather than re-querying my data, my approach is to update the dataTable on AJAX success which is much much faster.
Prior to implementing the join, this was based on a single dataTable and when I updated the dataTable, the dataView based on it would update as well and calling draw() on the chartWrapper would update the UI just as expected.
Trouble is that this isn't working on the joined dataTable. I have confirmed in developer tools on the client side that the dataTable is in fact updating the values as is the dataView, but this never propagates to the tableChart.
And here's the really strange part. If use the .getDataTable method of the chartWrapper in dev tools, I can confirm that the record in question is updated! Though the value in the generated visualisation is not.
I've tried redrawing the chartWrapper in my AJAX success to no avail. It does redraw (chartWrapper) as the scroll level changes, but data does not change. I've tried manually redrawing both the chartWrapper and dashboard in dev tools even passing the view as the dataTable. Both do in fact redraw, but neither updates the data.
I've tried redrawing the getDataTable table on the chartWrapper, but that basically disassociates from my dashboard and styles and draws the entire dataset which is not what I need.
Changing the category filters doesn't update the tableChart after filtering back. Even applying the different filters to the dataView and then back doesn't update it. The only way I've found that works is to re-query the entire thing which takes a good 5-10 seconds. An unacceptable delay for every single change. This is fine when first loading, but ideally that should only happen once per user session.
I've spent hours debugging and scouring the (terrible) documentation and everything I've seen seems to indicate that it should work.
I'm starting to wonder if maybe this is some sort of cache issue or if I'm missing some trick that's not explained in the documentation.
The client side code for this particular page is 1100 lines, so hard to post in full. Below is an extremely simplified version of what I'm doing with the key parts included. Would appreciate any advice or suggestions.
Note, I've omitted quite a few parts related to generating the dialog or buttons for the actions, but they are mostly irrelevant.
var loaded = false;
var ready = {pro: false, sec: false};
var data = {};
// Ajax load gviz api
$.ajax({
url: 'https://www.google.com/jsapi?callback',
cache: true,
dataType: 'script',
success: function(){
google.load('visualization', '1', {packages:['controls'], 'callback' : sendQuery
});
return true;
}
});
function sendQuery() {
console.log('query setting');
var opts = {sendMethod: 'auto'};
var urlPro = 'Google Spreadsheet source 1';
queryPro = new google.visualization.Query(urlPro, opts);
queryPro.setQuery('select A,B,C,D,E where(G = \'No\')');//...15k+ rows 30+ cols in reality
var urlSec = 'Google Spreadsheet source 2';
querySec = new google.visualization.Query(urlSec, opts);
querySec.setQuery('select A,B,C,D');//...~200+ rows 30+ cols in reality, rows created with app
queryPro.send(function(response){
console.log('query Pro returned');
if (response.isError()) {
alert('Error in query Pro: ' + response.getMessage() + ' ' + response.getDetailedMessage());
return;
}
ready.pro = true;
data.pro = response.getDataTable();
if (ready.pro && ready.sec) {
drawDashboard(false);
}
});
querySec.send(function(response){
if (response.isError()) {
alert('Error in query Sec: ' + response.getMessage() + ' ' + response.getDetailedMessage());
return;
}
console.log('query Sec returned');
ready.sec = true;
data.sec = response.getDataTable();
if (ready.pro && ready.sec) {
drawDashboard(false);
}
});
}
function drawDashboard(complete) {
var pendingCols = [0,2,3,4];
var completeCols = [1,2,3,4,5,7,8];
if (!loaded) {
console.log('first load of data');
var joinProCols=[1,2,3,4];
var joinSecCols=[1,2,3];
joined = new google.visualization.data.join(data.pro, data.sec, 'left', [[0, 0]], joinProCols, joinSecCols);
viewActive = new google.visualization.DataView(joined);
var numCols = joined.getNumberOfColumns();
for (var i=0; i<numCols; i++){
ogColName = joined.getColumnLabel(i).replace(/\W/g, '');
if (empTable.hasOwnProperty(ogColName)){ //passed from server; ommitted def in this example
joined.setColumnLabel(i, empTable[ogColName][lang]);
}
joined.setColumnProperty(i, 'ident', ogColName);
}
}
var pendingRows = joined.getFilteredRows([{column: 4, value: null}]);
var completeRows = joined.getFilteredRows([{column: 4, minValue: ''}]);
if (complete) {
viewActive.setColumns(completeCols);
viewActive.setRows(completeRows);
} else {
viewActive.setColumns(pendingCols);
viewActive.setRows(pendingRows);
}
dashboard = new google.visualization.Dashboard(document.getElementById('dashboard-div'));
// options for displayed table
var tableOpts = {
width: '1500px',
height: '100%',
page: 'enable',
pageSize: 40,
cssClassNames: {
headerCell: 'gviz header',
headerRow: 'gviz header',
oddTableRow: 'gviz odd',
tableRow: 'gviz even',
selectedTableRow: 'gviz selected',
hoverTableRow: 'gviz hover',
rowNumberCell: 'gviz rowNum'
}
};
tableChart = new google.visualization.ChartWrapper({
'chartType': 'Table',
'containerId': 'table-div',
'options': tableOpts
});
var picker1 = new google.visualization.ControlWrapper({
'controlType': 'CategoryFilter',
'containerId': 'sel-pick1',
'options': {
'filterColumnLabel': 'Picker1',
'ui': {
'labelStacking': 'vertical',
'selectedValuesLayout': 'belowWrapping',
'caption': 'Picker1'
}
}
});
var picker2 = new google.visualization.ControlWrapper({
'controlType': 'CategoryFilter',
'containerId': 'sel-pick2',
'options': {
'filterColumnLabel': 'Picker2',
'ui': {
'labelStacking': 'vertical',
'selectedValuesLayout': 'belowWrapping',
'caption': 'Picker2'
}
}
});
//...quite a few more pickers in real code
if (complete) {
var picker3 = new google.visualization.ControlWrapper({
'controlType': 'CategoryFilter',
'containerId': 'sel-pick3',
'options': {
'filterColumnLabel': 'picker3',
'ui': {
'labelStacking': 'vertical',
'selectedValuesLayout': 'belowWrapping',
'caption': 'picker3'
}
}
});
//...A couple more pickers in real app here too
}
// Set up dependencies between controls and charts
dashboard.bind(picker1, picker2);
//...bindings for other pickers
if (complete) {
dashboard.bind(picker2, picker3);
dashboard.bind([picker1, picker2, picker3], tableChart);
} else {
dashboard.bind([picker1, picker2], tableChart);
}
// Draw all visualization components of the dashboard and add listeners
google.visualization.events.addListener(tableChart, 'ready', function(){
google.visualization.events.addListener(tableChart.getChart(), 'page', addFields);
google.visualization.events.addListener(tableChart.getChart(), 'sort', addFields);
google.visualization.events.addListener(tableChart.getChart(), 'select', function(e){
//cancel selection here as it won't be useful
tableChart.getChart().setSelection('');
});
//grab the table and inject our custom actions into it
if (complete) {
addFields(true);
} else {
addFields();
}
// show containers
$('.processing.page').hide();
$('#dashboard-div').show();
loaded = true;
});
dashboard.draw(viewActive);
}
//........................................
$('#dataForm').submit(function( event ) {
if ($('#dataForm').valid()){
$('#dataForm').hide();
$('.processing.data.form').show();
google.script.run.withSuccessHandler(onSuccessUpdate).recordData(this);
}
event.preventDefault();
});
//...................................................
function onSuccessUpdate(response){
var numCols = joined.getNumberOfColumns();
for (var i=0; i<numCols; i++){
for (var key in response.form){
if (!response.form.hasOwnProperty(key)) continue;
if (joined.getColumnProperty(i, 'ident') == key){
joined.setValue(response.row, i, response.form[key]);
}
}
}
tableChart.draw();//This should be updating the values!
$('.message.success').html(response.message).show();
$('#dataForm').show();
$('.processing.data.form').hide();
$('#userProfileDialog').dialog('close');
}
Thanks in advance for any insights. This one is driving me crazy.
So it turns out that everything is working exactly as it should and the issue was in fact that I missed a critical facet of how this works.
When updating the joined dataTable values in the loop, I was was updating the 'value' using the setValue() method.
However, the 'value' that the rendered chart is actually reading is the 'formatted value' accessible through the setFormattedValue() method. When updating the value programatically, there doesn't seem to be an automatic update to the formattedValue. I may have incorrectly assumed that or, more likely, have just been completely ignorant to that portion of it since I didn't use it.
Originally, prior to using the join when I only used a single data source and all was working, I was passing the 'options no_format' parameter to the query. In that context, it seems that the API would use the value instead of the formattedValue for the actual table chart.
Once I realised my error, my first attempt at a fix was to add the 'options no_format' parameter to both of my data queries. This didn't seem to work. It looks like the join method automatically generates formatted values even if it's source dataTables don't have any.
Therefore, the correction to the my original code is to use the setFormattedValue method in addition to the setValues method as follows:
function onSuccessUpdate(response){
var numCols = joined.getNumberOfColumns();
for (var i=0; i<numCols; i++){
for (var key in response.form){
if (!response.form.hasOwnProperty(key)) continue;
if (joined.getColumnProperty(i, 'ident') == key){
joined.setValue(response.row, i, response.form[key]);
//Need to also set formatted value
joined.setFormattedValue(response.row, i, response.form[key]);
}
}
}
tableChart.draw();//This should be updating the values!
$('.message.success').html(response.message).show();
$('#dataForm').show();
$('.processing.data.form').hide();
$('#userProfileDialog').dialog('close');
Once both the value and formatted value are updated, the chart updates as expected on draw.
So in the end, this was simply a mistake on my part, but I hope there might be some value to someone with this example code and this particular behaviour of the dataTable generated by the join method.
i had already done adding a click handler to each Segment of my doughnut chart with adding the following Code :
$("#myChart").click(
function(evt){
var activePoints = myNewChart.getSegmentsAtEvent(evt);
var chartelementid = activePoints[0].label;
alert(chartelementid);
//$('.details div').css("display", "none");
//$('#' + chartelementid).show();
}
);
This works fine, when finished it should display an additional Div with Details for this segment.
Unfortunality my labels are more then just Single Words, so i'm struggeling to create div ID's with the same name...
My Idea is to add to every Segment an additional Data like value,label, etc. so it could be an ID. but if i just add the ID information to the Segment it will not exist as variable.
Add DataType:
var dataImprove = [
{
value: 30,
color:"#001155",
highlight: "#1c2f7c",
label: "KnowHow Erhalt / Transfer & Aufbau",
id:"test"
}
]
where can i add in chart.js an additional dataType like shown above my ID to be accessible in the DOM?
kind regards Marco
As an alternative pass a JSON string as your label, then intercept to render. For example:
var canvas = document.getElementById(id);
var d = canvas.getContext("2d");
var chart = new Chart(d).Pie(json, {
segmentStrokeWidth: 1,
tooltipTemplate: "<%=label%>", //default the label
customTooltips: function (tooltip) {
// Hide if no tooltip
if (!tooltip) {
return;
}
var tooltipObj = JSON.parse(tooltip.text);
// etc
already found : between line 999 and 1023 in chart.js before drawing - i've added the line
id: ChartElements[0].id,
so the Data with the name ID is in the DOM avaiable.
Issue:
my famo.us app allows a user to add many "card" views on a work area. These cards are draggable, added directly to a ContainerSurface, and not in any particular layout.
The cards have a delete button, which when pressed deletes the card.
Currently I "delete" the view using
theCardView.render = function(){ return null; }
This works in removing the view and the surfaces on it from the work area;however, it has a strange side-effect. When I add another card the delete button surface is under the header surface that it normally sits on.
If I add a 2nd card, then the delete button on this 2nd card is displayed properly.
When I look at the DOM after the delete I see that divs for the surfaces on my view are still there, but empty
Is there a better/cleaner way to "delete" a view? Or is there something else I need to do to ensure the DOM is cleaned up?
I can get around this issue by setting the z-index of the button to 1; however this causes a problem when you drag the cards around over each other - The button is always on top of all the other cards.
Code of how I add the CardViews
this._eventInput.on('add-cards', function() {
for (var i=0; i < that.cardPicker.selected.length ;i++){
var x = new CardView({data:that.cardPicker.selected[i]});
that.subscribe(x);
that.cards.push(x);
that.contentContainer.add(x);
}
});
Code of how I delete the view
this._eventInput.on('delete-card',function(e){
// e is the CardView object that has the button that was clicked
removeFromArray(that.cards,e);
e.render = function(){ return null; }
});
Code for the view constructor
function CardView(data) {
View.apply(this, arguments);
that = this;
this.dbdata = data.data;
this.layout = new HeaderFooterLayout({
headerSize: 43,
footerSize: 0
});
// Header
var oname = this.dbdata.dbname+"."+this.dbdata.table.name;
this.headerSurface = new Surface({size: [275, 43],
content: oname
,properties: {
backgroundColor:'rgb(51, 51, 51)'
,color:'white'
,paddingTop:'10px'
,paddingLeft:'10px'
}
});
this.draggable = new Draggable();
this.headerSurface.pipe(this.draggable);
this.draggable.activate();
this.layout.header.add(this.headerSurface);
// Header delete button
this.deletebtn = new Surface({size: [55, 40],
content: '<button type="button" class="btn btn-default">del</button>'
,properties:{
}
});
this.mod = new Modifier({
transform: Transform.translate(215, 0, 0)
});
this.layout.header.add(this.mod).add(this.deletebtn);
this.deletebtn.pp = this;
this.deletebtn.on('click', function() {
that._eventOutput.emit('delete-card',this.pp);
});
this.layout.content.add(new Surface({size: [275, 300],
properties: {
backgroundColor:'rgb(236, 236, 236)'
}
}));
// a modifier that centers the surface
this.centerModifier = new Modifier({
origin : [0.5, 0.5],
align: [0.5, 0.5]
});
this.add(this.draggable).add(this.centerModifier).add(this.layout);
}
So a couple of things:
Deleting surfaces from the context
When calling context.add() a RenderNode is created and returned, all subsquent views/modifiers/etc added to this RenderNode create additional nodes. The render node is responsible for reporting to the context what elements it contains (each surface/modifier/etc is responsible for reporting what it represents).
To clear all of the elements from a render node do something like the following
var myView = new View();
var context = Engine.createContext();
myView.node = context.add(myView); // Now the context knows about your view
myView.node.set({}); // Now the context doesn't
Now the problem remains that the parent node/context will maintain a reference to the original node even though it doesn't contain anything. This is something that is known and are working towards a solution.
Dealing with z-index
If you wanted to set the z-index of the button so that it is above the surface itself, you can also modify the z-index of the focused view. This can be accomplished by maintaining a reference to the modifier of the specific view and increasing the z-index. You will have to ensure the the change in z-index flows down into each element.
I would go for the first solution and delete the elements from the render node.
I want to refresh a dojo grid in my web page. I tried .refresh which is given in dojotoolkit.org without success. is there any other convenient way to do refreshing? Thanks in advance.
Maybe this helps. This is the way i refresh my Grid:
if(!registry.byId("GraphGrid")){
var grid = new EnhancedGrid({
id: 'GraphGrid',
store: GraphicStore,
query: { ident: "*" },
structure: layout,
rowSelector: '20px',
plugins: {
indirectSelection: {
headerSelector:true,
width:"40px",
styles:"text-align: center;"
}}
},"GridGraphicInMap");
/*Call startup() to render the grid*/
grid.startup();
dojo.connect(grid, "onRowClick", grid, function(evt){
var idx = evt.rowIndex,
item = this.getItem(idx);
// get a value out of the item
var value = this.store.getValue(item, "geom");
highlightGeometry(value,true);
// do something with the value.
});
}
else {
registry.byId("GraphGrid").setStore(GraphicStore);
}
When i first call my function the grid is generated. Evrytime i call the function later only the store is refreshed.
Regards, Miriam
Here I have a problem with my tableView I want to insert text into each row of the table
can you help me please
here is the code
// this sets the background color of the master UIView (when there are no windows/tab groups on it)
Titanium.UI.setBackgroundColor('white');
// create tab group
var tabGroup = Titanium.UI.createTabGroup({
});
// create base UI tab and root window
var win1= Titanium.UI.createWindow({
title:'',
tabBarHidden: true,
barColor:'black',
backgroundColor:'white'
});
var tab= Ti.UI.createTab({
title:'Connexion ',
window:win1
});
//
// This is a test that is meant to verify that a row object can have a header
// and the table view has no table view header - the header should be displayed
var win = Titanium.UI.currentWindow;
var inputData = [
{title:'Pseudo/email :', header:'Connexion ......'},
{title:'Password :'},
{title:'Créer votre compte',hasChild:true, header:'not yet registered ?'},
];
var tableView = Titanium.UI.createTableView({
data:inputData,
style:Titanium.UI.iPhone.TableViewStyle.GROUPED
});
win1.add(tableView);
tabGroup.addTab(tab);
// open tab group-----------------------------------------
tabGroup.open();
win1.open();
that's what I did right now but I have a problem with the title of my table and then I add another table.
there are also some ouci with the cursor which moves in the mid-line titles
here is the code
var win1= Titanium.UI.createWindow({
title:'',
tabBarHidden: true,
barColor:'black',
backgroundColor:'white'
});
var view = Titanium.UI.createView({
backgroundColor: "#FFFEEE"
});
var row1 = Ti.UI.createTableViewRow({
height:'auto',
selectionStyle:Ti.UI.iPhone.TableViewCellSelectionStyle.NONE
});
var label1 = Titanium.UI.createLabel({
text:'Pseudo/e-mail :',
left: 10
});
var usernametf = Ti.UI.createTextField({
left: 100,
right:10,
//hintText: 'Pseudo/email :',
//textAlign:"right",
borderStyle: Ti.UI.INPUT_BORDERSTYLE_NONE
});
var row2 = Ti.UI.createTableViewRow({
height:'auto',
selectionStyle:Ti.UI.iPhone.TableViewCellSelectionStyle.NONE
});
var label2 = Titanium.UI.createLabel({
text:'Mot de passe :',
left: 10
});
var passwordtf = Ti.UI.createTextField({
left: 100,
//textAlign:"right",
//hintText: 'password',
right:30,
passwordMask:true,
borderStyle: Ti.UI.INPUT_BORDERSTYLE_NONE
});
row1.add(label1);
row1.add(usernametf);
row2.add(label2);
row2.add(passwordtf);
var data = [row1,row2];
var table = Ti.UI.createTableView
({
data:data,
style: Ti.UI.iPhone.TableViewStyle.GROUPED
});
view.add(table);
win1.add(view);
win1.open();
I let you know that I really began with Appcelerator
Is there any reason why you wouldn't just use textfields? You could then just grab the values on button press and insert.
Kitchen Sink example link
https://github.com/appcelerator/titanium_mobile/blob/master/demos/KitchenSink/Resources/examples/textfield_events.js
Another example of a screen like this would be the Tweetanium App https://github.com/appcelerator/tweetanium
Whether you use textfields or tableviews there are several ways to work with the Ti Database object.
Two common ways would be:
1) You can loop through your inputData object and insert. Below are a few examples.
https://github.com/appcelerator/titanium_mobile/blob/master/demos/KitchenSink/Resources/examples/database_2.js
https://github.com/appcelerator/titanium_mobile/blob/master/demos/KitchenSink/Resources/examples/database_3.js
2) You can use the tableview object itself or a wrapper similar to this example https://github.com/kwhinnery/Persistence
I would recommend using textfields if possible.