SSRS and Interactive Sorting work in MVC4 - asp.net

I have implemented a reporting engine in mvc using html rendering. I had to create custom paging functionality based on the metadata returned via the render method. I am now looking at implementing interactive sorting in the same fashion which is looking like a more daunting task. It seems there is a setting in the HTML Device Information Settings header for html rendering called ActionScript.
ActionScript(*)-
Specifies the name of the JavaScript function to use when an action event occurs, such as a drillthrough or bookmark click. If this parameter is specified, an action event will trigger the named JavaScript function instead of a postback to the server.
My question is has anyone used this feature? Since I lose authentication after the report is rendered I will have to somehow make authenticated callbacks in the controller. It seems to me it may be easier just to add parameters for sorting in the report, however I would like to keep the interactive sorting in the report.
--Edit Solution1---
It turns out that the * next to script name above means the ActionScript parameter is being depreciated after ssrs 2012. Thus, I decided not to pursue it. If anyone else stumbles upon this then the best way I thought of to simulate interactive sorting without post backs is detailed below:
On the Report
1. Add a parameter to the report SortField1
2. Add a Sort condition to the Tablix or group you wish to sort. Tablix-Sort Expressions
3. Set the expression of the sort to Fields(Paremeters!SortField1.Value).Value
4. Set the default value of parameter SortField1 to the default sort field
5. Set Allow Nulls of the parameter to true.
6. Add a image or label for each column you would like to sort by
7. Create a action for the element created in step 7 with code similar to
="javascript:void(reportSortRequest('ColumsFieldName'))"
NOTE: Your view or view descendant will have need to have an identical function defined to accept the action
In the View
Implement the function reportSortRequest(fieldName). This should set
Model.SortField1 and invoke the post back to the controller to
re-render the report.
In the Controller (This is for Ajax post backs that sends a model that will have a field SortField1)
Call the render with report information including SortField1.
Updated with client script that works to access the report markup wrapper.
The report element generated by the ssrs markup can be accessed in your view by its tags. I have that found the following will work using the predetermined ssrs wrapper element #oReportCell":
Loaded in iframe :
var frameContent = $("#myIFrame").contents();
var ssrsContentElement = frameContent.find("#oReportCell");
Loaded in div:
var ssrsContentElement = $("#myDiv").find("#oReportCell");
Function to load html blob into either a frame or a div. Depending how the report was configured, the content is either a url of a temp file or the html markup.
function setReportContent(content, isUrl, renderInIFrame) {
if (isUrl) {
if (renderInIFrame) {
$("#reportContent").html("<iframe id='reportFrame' sandbox='allow-same-origin allow-scripts' width='100%' height='300' scrolling='yes' onload='onReportFrameLoad();'\></iframe>");
$('#reportFrame').attr('src', content);
}
else
$("#reportContent").load(content);
}
else {
if (renderInIFrame) {
$("#reportContent").html("<iframe id='reportFrame' sandbox='allow-same-origin allow-scripts' width='100%' height='300' scrolling='yes' onload='onReportFrameLoad();'\></iframe>");
$('#reportFrame').contents().find('html').html(content);
}
else
$("#reportContent").html(content);
}
if(!renderInIFrame)
showReportWaitIndicator(false);
$("#reportContent").show();
}
Notice that if you want report images with auto-resize property set to be rendered correctly you have to turn on allow-scripts so that the ssrs resize js functions can fire, be careful.

Related

How do I edit variables and update content of a view component using a button within the same component?

Summary: I have a view component with buttons which need to be able to change the details displayed in the rest of the component on click, but i can't figure out how to update the components content and data using an element within the component.
I am using asp.net razor pages to make a simple UI which displays various info grabbed from a JSON file, no database connection or POST functionality necessary.
I want the block of html that displays the data to be a reusable component, so i don't have to rewrite it every time i want to show a different entry in the file. From my research, there are a few options: HTML components, tag helpers, razor components, partial views, and view components. I chose to use a view component since the other options all seemed either outdated or not capable enough to achieve what i wanted. That is, with the exception of razor components, which require blazor to function fully which would also require me to learn a whole new framework and retrofit my program to work for it.
My view component functions to the point where i can invoke it from Index.cs and the component shows all the data from the first log in the list, as desired.
My intention: the default entryNo value is 0, so the component shows the title and date from the first log entry. When a user clicks the "Next" button (inside the component), entryNo becomes 1, and the component shows the title and date of the second entry in the log file.
ViewComponent.cshtml:
#model FullLog
#{ int entryIndLocal = 0; }
<div>
<div>#Model[entryNo].Title</div>
<div>#Model[entryNo].Date</div>
<button onclick={entryNo++}>Next</button>
</div>
What i've tried:
Setting the entryNo value in the view's .cs file and including a function to increment that value when called. I couldn't figure out how to call the function from the cshtml.
Initialising the entryNo variable from within the cshtml file, with the intention of the adding onclick={#entryNo++}. This didn't work either.

How Do I Measure a Single Page Website That Uses URL Fragments with Tealium/GA?

I was asked to capture the analytics on a website. The website is made up of 5 web pages, but I now realize that the domain is the same and the only thing that changes is the URL fragment, i.e. www.domain.com#a, www.domain.com#b. The only info that comes through to GA is the domain and it does not include the URL fragments. The tracking code I'm using is Tealium and the data is being sent back to Google Analytics. How can I set this up so that i can see the entire URL in GA including the URL fragments?
So, from Tealium's perspective we need to trigger a view event when a new fragment is loaded (if I am understanding this correctly).
If we assume that the fragment change occurs on a link click then we need to trigger the view event when the link click occurs.
From the GA perspective, we need to trigger a view that captures the new information. For a view this is likely to be location, path and title.
Therefore, we need Tealium to construct the new data points and then pass them in a view event to GA.
The simplest way to do this in Tealium (all things being equal) is via a jQuery onHandler Extension
The jQuery extension requires the following information:
jQuery selector (or selectors) to pay attention to
"Trigger On" event type (this will be Click in this example)
Tracking event type to run (View event in this case)
Variable & values to set
Tealium jQuery onHandler extension config
note it's always a good idea to set a condition on your jQuery extensions so that they only run when needed instead of all the time and everywhere
In this extension, I have set the following:
jQuery Selector: '#MyID_1, #MyID_2, #MyID_3' -- yes you can pass a list of selectors or nearly any other valid jQuery selector statement
Trigger On: 'click'
Tracking Event: 'view'
3 Variables:
a. 'page_name' : $(this).text(); //get the link text
b. 'my_url' : utag.data['dom.url']+$(this).attr('href') //building the full URL including the fragment
//utag.data['dom.url'] is a variable/datapoint that Tealium automatically generates
c. my_path : utag.data['dom.pathname']+$(this).attr('href'); //building the path
//utag.data['dom.pathname'] is a variable/datapoint that Tealium automatically generates
NOTE: make sure to set the type for each these to "JS Code" otherwise your JavaScript will be quoted out as a string.
Why these three variables? As I understand GA, these are the values it would expect for a new page view -- location/URL, path, and Title so we are constructing those values in the extension to pass them to GA on the view event.
Now, we just need to map these new variables to GA.
my_path gets mapped to page in the GA mapping toolbox
page_name gets mapped to title
location isn't a default option in the mapping toolbox so we need to add a custom destination variable called location and map my_url to it.
custom variable mapping for GA
That's how you do it from within Tealium and minimal coding. If for some reason you don't want to / can't do it inside of Tealium, this provides us with a very nice template for a custom function to add to our codebase:
`$(document.body).on('click', '#altID', function(){
utag.view({
"page_name": $(this).text(),
"my_url": utag.data['dom.url'] + $(this).attr('href'),
"my_path": utag.data['dom.pathname'] + $(this).attr('href')
})
})`
See both in action over here at CodePen.

ZF3 - adding a view fragment to ViewModel

I am curious to know if it is possible to add a fragment to a view model:
return new ViewModel([]);
Returns my view to https://example.com/view
However is it possible to set this dynamically to add a fragment?
return new ViewModel("#fragment")
To return to https://example.com/view#fragment
The use case, have a js login/register view that is either https://example.com/view#login or https://example.com/view#register and want to return to the correct view...
EDIT
Obviously, this can be done using a re-direct, however, in the case of returning form errors, ViewModel needs to be used...
Not sure if this is possible to apply param to router, but you can alwas return redirectToRegister = true, and then write and execute JavaScript in view which will change view if True.
A fragment is a same document reference i.e. if the URL specification is used correctly #register and #login will refer to different elements in the same document.
The view model therefore should be returning the same html in both cases, and the fragment will be handled by the browser. To distinguish register and login pages instead use different paths in the URL.

Filter page datasource based on record selection

I seem to be struggling with this. I am working on a project management app in Google App Maker. I am using SQL data models. The portion of the app I am having trouble with is several project detail pages linked with a project list page. I have no trouble going from the project list page to the first project detail page.
I have a table widget in the project list page, and the onClick code for a table row is:
app.datasources.Projects.selectKey(widget.datasource.item._key);
app.showPage(app.pages.ProjectPage);
This filters the first detail page with the primary key of the record in focus. No problem here. When I want to navigate the next detail page that has a different datasource (but I still need to filter using the same primary key) I am struggling.
If someone can guide me in making a button in the first detail page that when clicked gets the _key of the project loaded in that current page and filters another detail page for the same project in focus, (using another data model, let's call it ProjectBudgetPage for example) I would really appreciate the help.
This was one my latest attempts at navigation to other detail pages within a project, this was for an onClick event:
var widgets = widget.parent.descendants;
var projectID = widgets.HiddenProjectID.text;
var projectDataSource = app.datasources.ProjectBudgetView.item;
projectDataSource = projectID;
app.showPage(app.pages.ProjectInfoPage);
which results in: (TypeError) : Cannot read property '__gwt_instance' of undefined
at HeaderProjectTabs.HeaderContainer.InfoTabButton.onClick:5:5
I am not sure if I understood properly but I suggest that you start by checking this official App Maker template available here https://developers.google.com/appmaker/templates/partner-management/ which provides a ready-to-run app you can use to manage an internal list of partners, but you can also customize it with your own needs. I recently used most of the UI and logic of this template and I was able to integrate it with SQL data models and it works really great.
I noticed that for this template they are saving the (widget.datasource.item._key) to a Page custom property which you can access afterwards. Custom properties are page-level properties that store data for a single user session. Custom properties are useful when you need to bind properties of multiple widgets to a single value. More info available here https://developers.google.com/appmaker/ui/binding#custom_properties
In your scenario, you can create a new custom property (string) inside your ProjectInfoPage and then you can add a script to the button onClick event in your ProjectPage to save the _key or any other item to a page property. For example:
app.pages.ProjectInfoPage.properties.ProjectKey = widget.datasource.item._key;
app.showDialog(app.pages.ProjectInfoPage);
Then you can get the “ProjectKey” property by adding this to the onAttach event in your ProjectInfoPage:
var key = app.pages.ProjectInfoPage.properties.ProjectKey;
…
In the onAttach event you can filter the new model or pass the property values to a server script. I believe there are other approaches but I hope this helps!

How to implement SiteCatalyst inbuilt variable 'pageType' in DTM?

I am trying to implement SiteCatalyst's inbuilt variable 's.pageType' using DTM (which we use to capture the 404 error pages).
I can definitely write this piece of code in DTM's s.code:
s.pageType="errorPage"
But the problem is the condition which would identify if the page is 404 error page or not, can be identified only at page code level (as per developers, there are exceptions which is thrown if error page comes up, which can be used to identify this condition on the page), but this logic we cannot be used in DTM. Along with this, on the 404 error page, the pageName variable should not be populated.
How this can be done, since I am fetching pageName from a data element in DTM (inside "Pageviews & Content" section) which would always fire on every page.
How to implement this, please help me out. Am I missing something ?
Thanks,
Adi
It sounds like your setup is like this:
<dtm header tag>
// code that identifies 404 page
<dtm footer tag>
And it sounds like the issue is that since data elements are the first thing evaluated up in DTM top tag, your page name data element is being evaluated before it is known that it is a 404 page.
What I would do is in the code that identifies that it is a 404 page, make it output a global js var that flags the page as a 404 page, e.g. window.is404Page=true;.
From here, the overall goal now is to keep your existing data element and pageName assignment as-is, but then later override it with an empty string (and pop pageType instead). You didn't really give any details about where you are actually setting pageName, so here are some scenarios that should point you in the right direction:
Scenario 1: pageName is set in Pageviews & Content in the main tool config
1.a: In Library Management, if you have set AA to load at Page Bottom, then go to Customize Page Code section, make sure it's set to execute "after UI Sttings" (If you are already using this code box and it must be set to execute before UI settings, then skip this and go to 1.b). Click on Open Editor and add the following:
if (window.is404Page) {
s.pageName='';
s.pageType='errorPage';
}
1.b: In Library Management, if you have set AA to load at Page Top, then you will need to create a page load rule (or use an existing rule that will trigger on every page view) that evaluates at page bottom, on dom ready, or onload (basically anything but top of page - point here is to get it to eval after is404Page has been set). Within the rule, go to Adobe Analytics > Custom Page Code and add the code from 1.a there.
Scenario 2: pageName is set within a page load rule
If your page load rule is set to trigger at "Top of Page" then you will need to create a separate rule that triggers after that. See 1.b.
2.a: If you are setting pageName from within the DTM field using %dataElement% syntax, then add the code from 1.a to the Custom Page Code section.
2.b: If you are setting pageName from within the Custom Page Code section, using e.g. s.pageName=_satellite.getVar('dataElement'); then simply add the code from 1.a directly below it.
TL;DR: set a global js var to act as a flag in your on-page code that determines if 404 page and then look for that in your DTM code to overwrite pageName and write pageType in DTM custom code sections that get eval'd after the js flag var is set.

Resources