Flutter Web: Reading and writing query string values - flutter

Is it possible to read/write to the query string directly, as opposed to pushing named routes to the root Navigator?
We're developing a Desktop style app, that uses an inner content area. We would like to support deep-linking on Web, but do not rely on our root navigator to change pages so the normal approach doesn't fit. (We use an IndexedStack + 'TabMenu' instead)
All we really need is few simple API's:
Window.setRoute("/foo/bar")
String route = Window.getRoute()
Window.onRouteChanged = _handleRouteChanged;
From there we could easily handle everything in code. Is this possible?

From https://github.com/flutter/flutter/issues/35261#issuecomment-507610298
you can get the query parameters by accessing window.location.href with the dart:js package:
import 'dart:js' as js;
...
var uri = Uri.tryParse(js.context['location']['href']);
if (uri != null) return uri.queryParameters;
And directly change window.location.href

Related

storing data so it is globally available in flutter

In my project I am calling the rest API which result gives a list of Map.
my App has 4 bottomtab options, in Dashboard I am hitting the API, and I am doing some operations on the data, but I need the result of that API should be available to all the tabs, else I have to hit again and store in that dart file,
Is there any way whrere I can Hit once and store it into a map and I can use that all over my project?
thanks!
Create a static List variable of your Data by Creating a new Class named Global
class Global
{
static List<MYDataClass> MyDataList = List();
}
to Add Data pass Object of MYDataClass
MyDataList.add(MYDataClass())
to Access it
Global.MyDataList[index].property;
Hope this Help...

load only components scripts that are in the current page

What I'm trying to achieve is that if i have 2 components nodes :
component1
clientlib
component1.js
component2
clientlib
component2.js
and i drag them into page1, then when page1 is generated, only component1.js and component2.js will be loaded when navigating to page1 .
One approach i saw is to use custom Tag Library as described here : http://www.icidigital.com/blog/best-approaches-clientlibs-aem-part-3/
I have two questions :
1) is there an existing feature in AEM to do this ?
2) if not, what is the easiest way to create such custom Tag Library ?
EDIT:
Assume that there is no ability to just include all component clientLibs, rather load only those that are added to the page.
There is no built in feature to do this. Although I've heard that the clientlib infrastructure is being looked at for a re-write so I'm optimistic that something like this will be added in the future.
We have, and I know other company have, created a "deferred script tag." Ours is a very simple tag that take a chunk of html like a clientlib include, add it to a unique list and then on an out call at the footer, spits it all out one after another.
Here's the core of a simple tag implementation that extends BodyTagSupport. Then in your footer grab the attribute and write it out.
public int doEndTag() throws JspException {
SlingHttpServletRequest request = (SlingHttpServletRequest)pageContext.getAttribute("slingRequest");
Set<String> delayed = (Set<String>)request.getAttribute(DELAYED_INCLUDE);
if(delayed == null){
delayed = new HashSet<String>();
}
if(StringUtils.isNotBlank(this.bodyContent.getString())){
delayed.add(this.bodyContent.getString().trim());
}
request.setAttribute(DELAYED_INCLUDE, delayed);
return EVAL_PAGE;
}
Theoretically the possible way of doing is to write script in your page component/abstract page component that does something like this -
Step1 : String path = currentPage.getPath()
Step2 : Query this path for components (one way is to have a master list do a contains clause on sling:resourceType)
Step 3: User resource resolver to resolve the resourceType in Step 3, this will give you resource under your apps.
Step 4: From the above resource get the sub-resource with primary type as cq:ClientLibraryFolder
Step 5: from the client libs resource in Step 4 get the categories and include the JS from them
you could actually write a model to adapt a component resource to a clientLibrary to actually clean the code.
Let me know if you need actual code, I can write that in my free time.

Fiori -- Cross application navigation; Handling startup parameters?

This question is related to:
Fiori - Cross Application Navigation
http://help.sap.com/saphelp_uiaddon10/helpdata/en/07/9561b716bb4f2f8ae4e47bacbdb86d/content.htm
Remove URL params on routing
My use case is like this:
I have multiple applications that should link to others (deep).
Since documentation of cross navigation mention to avoid deep links I decided to use a startup parameter.
For example:
Application A has a list of some items in the detail view of one item there is a reference to another application B that contains some other details.
Assume A shows article details and B shows some details of a producer of an article.
Application A would now use some navigation like this:
sap.ushell.Container.getService("CrossApplicationNavigation").hrefForExternal({
target : { semanticObject : "ApplicationB", action : "display" },
params : { "someID" : "102343333"}
})
Now in application B I use code like this inside the Component.js at the end of the init method.
var oRouter = that.getRouter().initialize();
var oComponentData = this.getComponentData();
if (oComponentData.startupParameters) {
oRouter.navTo("SomeView", {
someId : oComponentData.startupParameters.someID[0],
}, false);
}
First question: Is this the right place for handling the startup parameters?
Second question: If I using the navigation the startup parameter will still be in the code, I would prefer to remove it, but how?
Update
In the target application (B) it would lead to the following URL:
https://server/sap/bc/ui5_ui5/ui2/ushell/shells/abap/FioriLaunchpad.html?sap-client=100&sap-language=EN#SemObject-display?someID=102343333&/SomeView(102343333)/
Anyhow I would prefere to have something like this:
https://server/sap/bc/ui5_ui5/ui2/ushell/shells/abap/FioriLaunchpad.html?sap-client=100&sap-language=EN#SemObject-display?/SomeView(102343333)/
The parameter must be retrieved as
var oComponentData = this.getComponentData();
if (oComponentData.startupParameters) {
oRouter.navTo("SomeView", {
someId : oComponentData.startupParameters.someID[0],
}, false);
as you write. In Fiori applications, the startup parameters injected into the Component data of your constructor may have been renamed, enriched by further default values etc.. Thus they may be distinct from the parameter one observes in the url. Applications are advised to refrain from trying to inspect the URL directly.
If one supplies a very long set of url parameters, one will observer that the FLP replaces some of them with sap-intent-param=AS123424 ("compacted URL") to work around url length restrictions on some platforms and in bookmarks, in the
getComponentData().startupParameters one will receive the full set of parameters).
As to the second question.
No, there is currently no way to "cleanse" the URL and avoid the redundancy between and inner app route.
SemObject-display?someID=102343333&/SomeView(102343333)/
which after navigation may look like
SemObject-display?someID=102343333&/SomeView(102343999)/
App was started with 102343333, but then user navigated within the app to another item (102343999).
Any change in the "Shell-part" of the has (SemObject-display?someID102343333) will lead to a cross-app-navigation (reinstantiation of your component) with a different startupParameter.
(There are cases where this is desired in the flow, e.g. a cross navigation from a OrgUnit factsheet to the parent OrgUnit factsheet via a link).
There were ideas within SAP to fuse the inner-app routes and the intent parameters, but they were not carried out, as it's mostly url aesthetics.
Note: To support boomarking, one has to respect both startup parameters and
inner app route during component instantiation,
assuming the user created a bookmark on
SemObject-display?someID=102343333&/SomeView(102343999)/
(While he was looking at 9999(!)).
When reinstantiating the app, the inner app route should take higher precedence than startup-parameters.
So amend the code to:
var oComponentData = this.getComponentData();
if (oComponentData.startupParameters) {
if (sap.ui.core.getHashChanger().getHash()=== "") {
// if no inner app route present, navigate
oRouter.navTo("SomeView", {
someId : oComponentData.startupParameters.someID[0],
}, false);
}
}
https://sapui5.netweaver.ondemand.com/#docs/api/symbols/sap.ushell.services.CrossApplicationNavigation.html
SAP Fiori Launchpad for Developers, Navigation Concept
http://www.sdn.sap.com/irj/scn/go/portal/prtroot/docs/library/uuid/907ae317-cb47-3210-9bba-e1b5e70e5c79?QuickLink=index&overridelayout=true&59575491523067
I was having issues navigating from a Fiori elements app in to a deep page in a freestyle UI5 app and then answer from #user6649841 provided most the solution for my requirement.
In my instance, navigating from the elements list report (app "A") in to the target freestyle app (app "B") I didn't want the worklist/initial page in app B to display at all and instead go straight to the detail page without a flickering of the initial app screen.
The below worked for me, note though it doesn't solve the ugly URL issues. In my case I'm not fussed about it as my nav back will nav back to the elements list report (App A) and never show the worklist page in App B so the user will never make another search on top of this URL which would lead with inconsistent inner and outer keys
Component.js (at end of init function after all the standard sap code, but before router initialization):
var oComponentData = this.getComponentData();
var startupParams = oComponentData.startupParameters;
if (startupParams && startupParams.myQueryStringParamName && startupParams.myQueryStringParamName[0]) {
//In my case using hash changer as I dont want the original landing page (default route) to be
//in the history, so the detail page loads straight away and nav back will cause to nav back to App A
var hashChanger = sap.ui.core.routing.HashChanger.getInstance();
hashChanger.replaceHash("detailPage/" + startupParams.myQueryStringParamName[0]);
}
//initialise after the above so the new hash is set and it doesnt initially load the
//page assigned to the default route causing a flickering and nav slide effect
this.getRouter().initialize();
Looking at the UI5 SDK in UI5 1.48 and above in the initialize method of router you can pass in a boolean to tell it to ignore the initial hash so possibly can do a simpler implementation in newer releases of UI5
Is Component.js right place for handling the startup parameters?
Depends,if you have multiple views and you want to dynamically route based on the incoming parameters. Else you can handle in specific view also.
Your second question was not quite clear to me.
Nevertheless, if you want to only specific cases of startup parameters, then from Source App, set some flag to understand where is the request coming from and handle accordingly. So this way, your normal navigation won't be tampered.

Using Google Maps v3 DrawingManager from GWT

I am working on a project that uses GWT 2.4 and gwt-maps.jar to create a MapWidget and place it in a panel with various controls. This all works fine.
I would like to give my users the ability to draw a polyline on the map and use the getLength method in Polyline to determine the length of the drawn polyline. I have done this before in ActionScript and it was a bit of a pain in the neck (the rubber-banding between the last clicked point and the mouse in particular) and I was hoping not to have to do that again.
The drawing manager looks like it might be a good fit (for the drawing part at least) but it is in v3 of the API and the gwt-maps.jar code only is at v2. So I thought I might write some JavaScript and call out from GWT using JSNI, something along the lines of (in wibble.html - my top-level HTML file):
var dM = new google.maps.drawing.DrawingManager( ...
function showDM(map) {
dM.setMap(map);
dM.setOptions({
drawingControl: true
});
And then (in Wobble.java):
private MapWidget map = new MapWidget( ...
private native void showIt(final MapWidget map) /*-{
$wnd.showDM(map);
}-*/;
I have tried passing the MapWidget and its peer but in both cases I get an invalid value error when calling setMap.
Has anybody tried (and succeeded) in doing this or am I barkig up the wrong tree?
Thanks,
SO
First of all, I only have experience with GWT and GWT-JS communication. Not Google APIs.
Now:
Looks like you are passing a GWT object (compiled javscript object) to DrawingManager. The problem is the DrawingManager API receives "nice javascript objects" (not objects with obfuscated methods).
If you want to pass an HTML Element is ok (but then, you must pass widget.getElement() that really is a <div> object (by example).
Solution
Indeed GMaps API docs says you must pass a Map object from the GMap API. You create that map with an element that will be the canvas.
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
If you want to use your MapWidget as the map canvas then you can use its HTML Element.
In GWT:
private native void showIt(final MapWidget map) /*-{
$wnd.showDM(map.getElement()); // use mapwidget's element as canvas
}-*/;
In javascript:
function showDM(canvasToUse) {
// TODO: define myOptions :)
var map = new google.maps.Map(canvasToUse, myOptions);
dM.setMap(map);
dM.setOptions({
drawingControl: true
});
Disclaimer
It's only based on my experience with GWT and JSNI. I didn't try it nor have experience with GMaps or DrawingManager. You should check what I say and tell me if I had luck :)
Hope it helps!

MVC 2.0 - different view based on URL with shared controls

I have 2 master pages. One is intended to be shown in a normal standalone website. The other is to be used in external sites as an Iframe.
I want to be able to show the normal page at http://example.com/home/index and the iframed version at http://example.com/framed/home/index
I want to have controls that will postback to one controller so I don't have to duplicate logic, so they must be available in both the normal and iframed versions.
My problem is that when I try and use areas, I just can't get them to work right with the default url. Also, I have the added complication of structuremap. When I try and hit /area/controller/action, I get
The IControllerFactory
'MySite.Web.Code.IoC.StructureMapControllerFactory'
did not return a controller for the
name 'MyArea'.
Does anyone know how to make this kind of setup work? Really all I'm doing is trying to show one set of views if it has /Framed/controller/action and another set if it does not have /framed. I thought areas were the way to go, but maybe not.
All of our controllers implement the same base class, and we use the following override to do what you're describing:
protected override ViewResult View(string viewName, string masterName, object model)
{
if (masterName == null)
{
var options = PortalRequestManager.CurrentPortalRouteOptions;
masterName = options.MvcMasterPath;
}
return base.View(viewName, masterName, model);
}
All of our AreaRegistrations use the following method to register their areas:
public static void RegisterMvcAreaRoutes(AreaRegistrationContext context, string name, string url,
object defaults)
{
context.MapRoute(name + "Portal",
"P/Channel/" + url,
defaults);
context.MapRoute(name + "FramePortal",
"F/Channel/" + url,
defaults);
}
And then the PortalRequestManager that you saw in the first code block parses the URL to see if it uses "/P" or "/F" to determine which MvcMasterPath to use.
We use Ninject's controller factory, which has no problem with this setup, so I can't really speak to your problems with StructureMap.