I have a visualForce page and an apex controller. I need to get a date value from my visualForce page into my Apex Controller (this is done already) and then pass this value into my SOQL query. My SOQL query runs fine when I put an actual date value in, but when I use the variable created before it says can't find variable named x.
I'm certain this is a general programming mistake, this is only my second week using code not clicks!
Let me know if you want to see the VF page too.
**Apex Controller***
global with sharing class GoogleChartsController2 {
Opportunity o = new Opportunity();
public Opportunity getProxyObject() {
return o;
}
/**
Loads all Opportunities and then filters
*/
#RemoteAction
global static Opportunity[] loadOpps() {
return [select Id, Name, ExpectedRevenue, LeadSource, DaysToStartDate__c, Probability, CloseDate, Amount from Opportunity WHERE CloseDate = :o.CloseDate ];
}
}
Section from VisualForce Page *
<apex:page controller="GoogleChartsController2" sidebar="false">
<!-- Google API inclusion -->
<apex:includescript id="a" value="https://www.google.com/jsapi">
<apex:sectionheader title="Google Charts + Javascript Remoting" subtitle="Demoing - Opportunities by Exepected Revenue">
<!-- Google Charts will be drawn in this DIV -->
<div id="chartBlock" style="width: 900px; height: 500px;">>
<script type="text/javascript">
// Load the Visualization API and the piechart package.
google.load('visualization', '1.0', {'packages':['corechart']});
// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(initCharts);
function initCharts() {
// Following the usual Remoting syntax
// [<namespace>.]<controller>.<method>([params...,] <callbackFunction>(result, event) {...}
// controller : GoogleChartsController
// method : loadOpps
GoogleChartsController2.loadOpps(
function(result, event){
// load Column chart
var visualization = new google.visualization.BubbleChart(document.getElementById('chartBlock'));
// Prepare table model for chart with columns
var data = new google.visualization.DataTable();
data.addColumn('string', 'Opportunity');
data.addColumn('number', 'Days To Contract Start');
data.addColumn('number', 'Probability (%)');
data.addColumn('string', 'Lead Source');
data.addColumn('number', 'Amount');
// add rows from the remoting results
for(var i =0; i<result.length;i++){
var r = result[i];
data.addRow([r.Name, r.DaysToStartDate__c, r.Probability, r.LeadSource, r.Amount]);
}
// all done, lets draw the chart with some options to make it look nice.
visualization.draw(data, {
legend : {position: 'right', textStyle: {color: 'black', fontSize: 10}},
width:window.innerWidth,
length:window.innerLength,
vAxis:{title: 'Probability', textStyle:{fontSize: 10}, minValue: 0, maxValue: 100},
hAxis:{title: 'Days Until Close' , textStyle:{fontSize: 10},
chartArea: {width: '50%', height: '75%'},
sizeAxis: {minSize: 150, minValue: 150},
showTextEvery:1,slantedText:false}})
},
{escape:true});
}
</script>
</div></apex:sectionheader></apex:includescript>
<apex:form >
<apex:outputlabel value="Enter your name here"/>
<apex:inputField value="{!proxyObject.closeDate}"/>
<apex:actionsupport event="onclick" rerender="display" />
<apex:outputpanel id="display">
<apex:outputtext value="The date entered is {!proxyObject.closeDate}"/>
</apex:outputpanel>
</apex:form>
</apex:page>
The need for remoting makes this a bit more interesting. Because remoting methods must be declared as static (as you've already done), you won't be able to reference any member variables that you're using in the controller. You can, however, pass along their value from the page in your remoting call.
First off, you'll want to modify your remoting method to accept a parameter from the Javascript that's calling it. Then you'll just parse that date string in your SOQL query. You can leave the proxyObject in the controller in order to leverage the out-of-the-box date picker that Salesforce provides, but you won't be referencing that variable directly in the loadOpps method.
global with sharing class GoogleChartsController2 {
public Opportunity proxyObject {public get; public set;}
public GoogleChartsController2() {
proxyObject = new Opportunity();
// just giving this a value for testing purposes
proxyObject.CloseDate = System.today();
}
/** Loads all Opportunities and then filters */
#RemoteAction
global static Opportunity[] loadOpps(String closedate) {
return [select Id, Name, ExpectedRevenue, LeadSource, Probability, CloseDate, Amount from Opportunity WHERE CloseDate = :Date.parse(closedate)];
}
}
Next, you're going to have to reference the field on your VF page when making the call to loadOpps. You can do this a couple of different ways, the easiest of which for these circumstances is probably to use the VF $Component merge field markup. This will require you to specify IDs for your nested VF tags:
<apex:form id="testForm" >
<apex:inputField id="closedate" value="{!proxyObject.closeDate}"/>
And these can then be referenced like:
var dt = document.getElementById("{!$Component.testForm.closedate}").value;
So your final VF page becomes something like:
<apex:page controller="GoogleChartsController2" sidebar="false">
<apex:sectionheader title="Google Charts + Javascript Remoting" subtitle="Demoing - Opportunities by Expected Revenue" />
<apex:form id="testForm" >
<apex:outputlabel value="Enter a date here"/>
<apex:inputField id="closedate" value="{!proxyObject.closeDate}" >
<apex:actionsupport event="onchange" rerender="display" />
</apex:inputField>
<apex:outputpanel id="display">
<apex:outputtext value="The date entered is {!proxyObject.closeDate}"/>
</apex:outputpanel>
</apex:form>
<!-- Google API inclusion -->
<apex:includescript id="a" value="https://www.google.com/jsapi">
<!-- Google Charts will be drawn in this DIV -->
<div id="chartBlock" style="width: 900px; height: 500px;">>
<script type="text/javascript">
// Load the Visualization API and the piechart package.
google.load('visualization', '1.0', {'packages':['corechart']});
// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(initCharts);
function initCharts() {
// Following the usual Remoting syntax
// [<namespace>.]<controller>.<method>([params...,] <callbackFunction>(result, event) {...}
// controller : GoogleChartsController
// method : loadOpps
var dt = document.getElementById("{!$Component.testForm.closedate}").value;
GoogleChartsController2.loadOpps( dt,
function(result, event){
// load Column chart
var visualization = new google.visualization.BubbleChart(document.getElementById('chartBlock'));
// Prepare table model for chart with columns
var data = new google.visualization.DataTable();
data.addColumn('string', 'Opportunity');
data.addColumn('number', 'Days To Contract Start');
data.addColumn('number', 'Probability (%)');
data.addColumn('string', 'Lead Source');
data.addColumn('number', 'Amount');
// add rows from the remoting results
for(var i =0; i<result.length;i++){
var r = result[i];
data.addRow([r.Name, r.DaysToStartDate__c, r.Probability, r.LeadSource, r.Amount]);
}
// all done, lets draw the chart with some options to make it look nice.
visualization.draw(data, {
legend : {position: 'right', textStyle: {color: 'black', fontSize: 10}},
width:window.innerWidth,
length:window.innerLength,
vAxis:{title: 'Probability', textStyle:{fontSize: 10}, minValue: 0, maxValue: 100},
hAxis:{title: 'Days Until Close' , textStyle:{fontSize: 10},
chartArea: {width: '50%', height: '75%'},
sizeAxis: {minSize: 150, minValue: 150},
showTextEvery:1,slantedText:false}})
},
{escape:true});
}
</script>
</div></apex:includescript>
</apex:page>
This seems to work for the most part for me, although the charting API is complaining about Column 2 being a string...but that's something you should be able to sort out by changing your columns around. Keep in mind that this currently won't re-render the chart whenever you change the date, but that shouldn't be difficult if you make a few changes to your apex:actionSupport tag.
Hope this gets you on the right track!
Related
In my ASP.net MVC web project, I am using ANgularJS ui-grid for better data display option.
In the other razo view pages I have used partial view load
<partial name="TestPartialView" model="data.TestPartialData" />
Now in the ui-grid, in a column I also want to use the partial view.
```$scope.columns = [
{
name: 'TestColumn',
field: '..xxx',
width: '30%',
cellTemplate: 'Templates/TestPartialView.cshtml'
},] ```
But I really dont know how to do this. Also in my TestPartialView.cshtml, I need to inject a model class.
As a current alternative, I have defined the template again in the javascript file.But I want reuse the template.
Any idea?
Update:
So the above screenshot is my scenario. I will have a grid column statistics. And in the cell I will show bar chart based on some row specific model data.
Yes I have done this (as seen on the picture) with column definition.
Now the similar bar chart I have used a .cshtml file where a model class is passed and the view is loaded partially.
I want to reuse the partial view her in the grid also.
I think you can create a Controller to return your target View. And then you call this endpoint to obtain the view as the response and add it into your cellTemplate.
$http.get("https://localhost:44391/Home/GetTemplateFromPartial")
.then(function(response) {
$scope.columns = [
{
name: 'TestColumn',
field: '..xxx',
width: '30%',
cellTemplate: $sce.trustAsHtml(response.data)
}];
console.log($scope.columns);
});
The Controller will return IActionResult
public IActionResult GetTemplateFromPartial()
{
LocalMallUser user = new LocalMallUser
{
id = 1,
age = 18,
user_name = "test_user"
};
return PartialView("_SayHello", user);
}
_SayHello.cshtml:
#model WebApplication1.Models.LocalMallUser
<h1>#Model.user_name</h1>
<h2>#Model.age</h2>
I am updating a Line chart using HTTP get through ngResource and a rest API.
My technique is to get the JSON dataset and create a new chart every time a user is clicking on a button.
It works great, but at one time, it causes the browser crash. I have tested on Chrome, Firefox on both Windows and Linux.
In my controller :
$scope.labels = $scope.dataFromREST;
$scope.series = ['Series A'];
$scope.data = [$scope.dataFromREST2];
$scope.onClick = function (points, evt) {
console.log(points, evt);
};
$scope.datasetOverride = [{ yAxisID: 'y-axis-1' }];
$scope.options = {
scales: {
yAxes: [
{
id: 'y-axis-1',
type: 'linear',
display: true,
position: 'left'
}
],
xAxes: [{
responsive: true,
ticks: {
autoSkip: true,
maxTicksLimit: 20
}
}]
}
};
In my index.html :
<canvas id="line" class="chart chart-line" chart-data="data"
chart-labels="labels" chart-series="series" chart-options="options"
chart-dataset-override="datasetOverride" chart-click="onClick">
</canvas>
Is there a way to just update or refresh the Line Chart with the $scope.dataFromREST data received and not create a new Chart object every time? (Because I think, the crash come from creating a new chart every time) I see the ".update()" function, but I can't seem to get it to work.
I have also tried the ".destroy()" and I am still getting the browser wind up to crash.
How can I get rid of that crash? Please help!
Yes, there is a way to simply update the underlying chart.js chart data without having to re-instantiate the chart each and every time. You just need to use the update(duration, lazy) function from the API.
Here is an example that I use in one of my apps (modified for your specific case). Note, chart is my chart.js object (what was returned from new Chart()...:
assembledData = {};
assembledData.data = // data from api call in an acceptable data format that chart.js understands
assembledData.colors = // new color definition for your data so it will update correctly
assembledData.labels = // new label definition for your data so it will update correctly
chart.data.datasets[0].data = assembledData.data;
chart.data.datasets[0].backgroundColor = assembledData.colors;
chart.data.labels = assembledData.labels;
chart.update();
Depending on how your chart behaves you may not have to re-define colors and labels on each update.
For this page: https://big.four51ordercloud.com/demo/product/customimageselector
I am trying to make one of several links that when clicked, will set the "Color" drop down to a certain value, such as "Style 1 - White" all while the "Color" drop down is hidden.
Example: Click HERE for White.
I can do this with jQuery and vanilla JavaScript just fine, but it doesn't work with AngularJS at all. The first drop down "Style" will dictate which hyperlinks are shown. That part I can do.
With that said, I am new to AngularJS.
Any advice? :-(
You can do as follows.
HTML
<button ng-click="chooseWhite()"></button>
<select ng-options="option in options" ng-model="selectedOption"></select>
Controller
$scope.options = [{ color: 'red', id: 1 }, { color: 'white', id: 2 }];
$scope.selectedOption = $scope.options[0];
$scope.chooseWhite = function () {
$scope.selectedOption = $scope.options[1];
};
Bind an object the select element and change the selected value while you click on links or buttons.
I have an order line grid where I need to be able to open the popup editor form programmatically with the edit form fields pre-populated (using AngularJs).
In the HTML, I have a lineGrid and an addButton, which calls addRow() on the ticketEntryController:
<div id="wrapper" class="container-fluid" ng-controller="ticketEntryController">
<div ng-controller="ticketLineController">
<div kendo-grid="ticketLineGrid" k-options="getTicketLineGridOptions()"></div>
</div>
<button id="addButton" ng-click="addRow()" class="btn btn-primary btn-sm">Add Row</button>
</div>
Here is the ticketEntryController:
(function () {
'use strict';
angular.module('app').controller('ticketEntryController', ticketEntryController);
function ticketEntryController($scope) {
$scope.lineGrid = {};
$scope.addRow = function () {
var item = { itemNo: 'TEST123', itemDescr: 'Some description' };
$scope.$broadcast('AddRow', item);
}
}
})();
Here is part of the ticketLineController:
function ticketLineController($scope) {
$scope.$on('AddRow', function(event, item) {
console.log("ticketLineController, AddRow: " + item.itemNo);
$scope.itemNo = item.itemNo;
$scope.itemDescr = item.itemDescr;
$scope.ticketLineGrid.addRow();
});
Plunker: http://plnkr.co/edit/VG39UlTpyjeTThpTi4Gf?p=preview
When the Add Row button is clicked, the editor popup form opens up, but all fields are empty. How can I populate the fields (like they are when you click the Edit button for an existing row)?
I figured out how to get a row to be pre-populated for you, although I'm not sure if this is necessarily the best way to do it, but it does accomplish the job - I'm more familiar with AngularJs, not so much with Kendo UI.
The only place that the Kendo API allows you to change/set the new item that you are adding is in the edit event but I couldn't see a way to send your own object along to the event when you call addRow so you need to have a reference to a shared object in your controller with I called itemForAdd. Before calling addRow() in your controller, you need to set the itemForAdd object with the actual object that you want to pre-populate the form with.
var itemForAdd = {};
$scope.$on('AddRow', function(event, item) {
// save reference to item to use for pre-population
itemForAdd = item;
$scope.ticketLineGrid.addRow();
});
Now in the edit event that the Kendo API sends out, you can populate the items from your selected item in the model item. It's not really required, but I also like to clear out objects that I use like this so in the save and cancel events, I clear out the shared itemForAdd object.
edit: function (e) {
if (e.model.isNew()) {
e.model.set("itemNo", itemForAdd.itemNo);
e.model.set("itemDescr", itemForAdd.itemDescr);
}
var popupWindow = e.container.getKendoWindow();
e.container.find(".k-edit-form-container").width("auto");
popupWindow.setOptions({
width: 640
});
},
save: function(e) {
if (e.model.isNew()) {
// clear out the shared object
itemForAdd = {};
}
},
cancel: function(e) {
if (e.model.isNew()) {
// clear out the shared object
itemForAdd = {};
}
}
With the previous changes, the functionality that you want is mostly working but the data in the table in the edit popup doesn't show the updated values. This is because the Kendo data bindings apparently didn't know they had to update. I couldn't figure out how to make that work, so I just used the AngularJs style bindings for that table (where you had +=itemNo=+), so that the values in the table would update based on the changes in the model object:
<tbody>
<tr>
<td>{{dataItem.itemNo}}</td>
<td>{{dataItem.itemDescr}}</td>
<td>{{dataItem.cat}}</td>
<td>{{dataItem.mfg}}</td>
<td>{{dataItem.mfgPartNo}}</td>
</tr>
</tbody>
But there was one more issue at this point, only the itemNo was being updated, not the itemDescr and that was because itemDescr was set as editable: false in your grid configuration, so I had to changed it to editable: true
fields: {
id: { type: "string", editable: false },
itemDescr: { type: "string", editable: true },
...
},
And finally, here is an updated plunker with my changes: http://plnkr.co/edit/rWavvMh4dRFAsJjuygQX?p=preview
I have added custom text field in dhtmlschedular.js but
I am unable to trace the path. how can my custom field value get on controller.
or complete flow of dhtmlx for saving the data to db.
I done so far :-
lightbox: {
sections: [
{name: "description", height: 200, map_to: "text", type: "textarea", focus: true },
{ name:"prabhu", height:200, map_to:"txt1", type:"dhiraj"},
{name: "time", height: 72, type: "time", map_to: "auto"}]
dhiraj: {
render:function(sns){
return "<div class='dhx_cal_ltext' style='height:60px;'>Text <input id='txt1' name='txt1' type='text'><br/>Details <input id='calendarDetailId' name='txt2' type='text'></div>";
},
set_value:function(node,value,ev){
node.childNodes[1].value=value||"";
node.childNodes[4].value=ev.details||"";
},
get_value:function(node,ev){
ev.location = node.childNodes[4].value;
return node.childNodes[1].value;
},
focus:function(node){
var a=node.childNodes[1]; a.select(); a.focus();
}
}
};
You can add custom field without modifying sources of the component (i'd recomend avoid modifying them whenever it's possible)
If you need to add another control into the details form, you can overwrite scheduler.configuration.lightbox.sections object (in your page/script file, not in the sources)
The 'map_to' property defines property of the data object(event) to be mapped to the input.
If control should be mapped to several fields, you can use map_to:"auto" and retreive needed fields from event object manually.
The code on your page may look like following:
//defining a control
scheduler.form_blocks["dhiraj"]={
render:function(sns){
return "<div class='dhx_cal_ltext' style='height:60px;'>" +
"Text <input id='txt1' name='txt1' type='text'><br/>" +
"Details <input id='calendarDetailId' name='txt2' type='text'>" +
"</div>";
},
set_value:function(node,value,ev){
//get values from event manually
node.childNodes[1].value = ev.txt1 || "";
node.childNodes[4].value = ev.txt2 || "";
},
get_value:function(node,ev){
//assign values to event object
ev.txt1 = node.childNodes[1].value;
ev.txt2 = node.childNodes[4].value;
},
focus:function(node){
var a=node.childNodes[1]; a.select(); a.focus();
}
};
//add control to the details form
scheduler.locale.labels.section_prabhu = "";//labels for inputs are defined as 'seciton_'+inputName
scheduler.config.lightbox.sections = [
{name:"description", height:200, map_to:"text", type:"textarea" , focus:true},
{name:"prabhu", height:200, map_to:"auto", type:"dhiraj"},//here it is
{name:"time", height:72, type:"time", map_to:"auto"}
];
//init scheduler after control is defined
scheduler.init('scheduler_here',new Date(),"week");
New input will show value of 'txt1' and 'txt2' properties, as can be seen in the definition. When changes are saved, component will send all event values to the server. Custom values also will be sent.
Depending on how you handle update on the server side, you should either add custom fields to configuration of dhtmlxConnector, or retreive them from GET/POST parameters.
http://docs.dhtmlx.com/scheduler/lightbox_editors.html
http://docs.dhtmlx.com/scheduler/custom_lightbox_editor.html