Debugging of the UI5 Expression Bindings - data-binding

Is there any way to debug UI5 Expression Bindings (without Formatters), embedded into XML with DevTools/IDE, but not in SAP WebIDE?

Debug "parse" method of ExpressionParser class. You can test that by creating a very simple app using the following code.
View
<App id="app">
<pages>
<Page id="page" title="{i18n>title}">
<content>
<Text text="{= ${/a} + ${/b} }" />
</content>
</Page>
</pages>
</App>
Controller
onInit: function () {
var oModel = new JSONModel({
a: 2,
b: 3
});
this.getView().setModel(oModel);
}
In DevTools, you can use Ctrl+P to open a file by searching for its name — ExpressionParser. If not, you can find this file in the following path (see image below): /resources/sap/ui/base/ExpressionParser-dbg.js

Related

How do I reference an embedded resource in a <script/> tag?

I have a ASPNET CORE website that references a separate class library.
In that class library I have a javascript file as an embedded resource.
IOW, I have this in MyLibrary.csproj:
<Project Sdk="Microsoft.NET.Sdk">
[...]
<ItemGroup>
<EmbeddedResource Include="JavaScript/MyFile.js" />
</ItemGroup>
</Project>
In the website project my _Layout.cshtml contains this:
<body>
[...]
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="//cdn.datatables.net/1.10.13/js/jquery.dataTables.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
#await RenderSectionAsync("Scripts", required: false)
</body>
I would like to add a tag, either to the end of the element in _Layout.cshtml, or to the "Scripts" section within any of the .cshtml files that use _Layout.cshtml.
How do I do this?
OK, I have something mostly working.
First, some context. I have two projects, building two different assemblies. It can be difficult, when reading various "explanations" of how to make this work, to understand which assembly they were referring to, so I'm going to be pedantically explicit.
There are a number of steps:
Add the file to the library project.
Make it accessible as an embedded resource.
Map an endpoint in the web site that serves up the embedded resource file.
Add a <script> tag to the html that links to that endpoint.
MyLibrary is a class library project containing a number of classes, and a JavaScript file that I want to make available to any website that references those classes.
In it I have a directory "JavaScript", and in that the file "MyJsFile.cs" that I want to make available to the client.
I've installed the Microsoft.Extensions.FileProviders.Embedded NuGet package in MyLibrary, and I've set GenerateEmbeddedFilesManifest to true.
And I've added the file I want to be available in an EmbeddedResource ItemGroup.
So MyLibrary.csproj looks like:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<ItemGroup>
[...]
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="5.0.8" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="JavaScript/MyJsFile.js" />
</ItemGroup>
</Project>
At this point, I compiled MyLibrary, and checked to confirm that MyJsFile.js was listed as an embedded resource in MyLibrary.dll. On Windows, you can examine an assembly using ildasm.exe. If you're running on Linux, and don't want to bother with finding, downloading, and maybe having to build one of the various competing versions of ildasm that seem to be out there, you can just stick this into a project that's referencing MyLibrary, and step past it in the debugger:
var resourceNames = typeof(MyLibrary).Assembly.GetManifestResourceNames();
You should see the name of the file, in resourceNames. You can extract the whole file with:
using (var resourceStream = typeof(MyLibrary).Assembly.GetManifestResourceStream("MyLibrary.JavaScript.MyJsFile.js"))
{
var rdr = new StreamReader(resourceStream);
var contents = rdr.ReadToEnd();
}
MyWebsite is a ASP.NET Core Web App project that references MyLibrary, and it's this that I need to be able serve up the file.
First we need to reference the library project. MyWebsite.csproj:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
[...]
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../MyLibrary/MyLibrary.csproj" />
</ItemGroup>
</Project>
Next, we need to map an endpoint in Startup.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
[...]
app.Map("/mylibrary", builder =>
{
var provider = new ManifestEmbeddedFileProvider(
assembly: Assembly.GetAssembly(typeof(MyLibrary)), "JavaScript");
builder.UseStaticFiles(new StaticFileOptions
{
FileProvider = provider
});
});
}
What this is doing is setting up a routing rule such that any reference to a file beginning with /mylibrary is mapped to an embedded resource of the same name in the JavaScript directory of MyLibrary.dll
Which means we can reference it in a <script> tag:
#section Scripts {
<script src="~/mylibrary/MyJsFile.js"></script>
[...]
}
And this is working. The objects defined in MyJsFile.js are visible in the browser.
All that's left is to move the App.Map("/mylibrary", [...]) configuration into an extension method in the library, so that MyWebsite doesn't need to know the details of how to configure it.
To do that, I need to add a couple of NuGet packages to MyLibrary:
Microsoft.AspNetCore.Mvc.Core
Microsoft.AspNetCore.StaticFiles
And then I needed to create an extension method in MyLibrary:
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder MapMyLibrary(this IApplicationBuilder app, string localPath)
{
app.Map(localPath, builder =>
{
var provider = new ManifestEmbeddedFileProvider(
assembly: Assembly.GetExecutingAssembly(), "JavaScript");
builder.UseStaticFiles(new StaticFileOptions
{
FileProvider = provider
});
});
return app;
}
}
And then, finally, change Startup.cs, in MyWebsite, to call it:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
[...]
app.MapMyLibrary("/mylibrary");
}

How to intercept WebView Navigating event in ViewModel

My app has a WebView for displaying some contact information. It has a link to a website that I want to load externally using Device.OpenUri(). I'm using FreshMvvm and I want to intercept the Navigating event from the WebView in the ViewModel and cancel the default action which would load the external page into the WebView.
I've tried using the Corcav.Behaviors plugin which does call my ViewModel command:
<WebView
HorizontalOptions="Fill"
VerticalOptions="FillAndExpand"
Source="{Binding WebViewSource}">
<b:Interaction.Behaviors>
<b:BehaviorCollection>
<b:EventToCommand
EventName="Navigating"
Command="{Binding NavigatingCommand}"
CommandParameter="{Binding}"/> <!-- what goes here -->
</b:BehaviorCollection>
</b:Interaction.Behaviors>
</WebView>
But I'm not sure what the CommandParameter should be - I need the URI of the link that was tapped, and I don't know how to then prevent the default behaviour from occurring.
Is this the best approach or should I be looking at an alternative?
Having revisited this recently for another project I stumbled across the answer. The updated XAML is:
<WebView
x:Name="webView"
HorizontalOptions="Fill"
VerticalOptions="FillAndExpand"
Source="{Binding WebViewSource}">
<behaviors:Interaction.Behaviors>
<behaviors:BehaviorCollection>
<behaviors:EventToCommand
EventName="Navigating"
Command="{Binding NavigatingCommand}"
PassEventArgument="True" />
</behaviors:BehaviorCollection>
</behaviors:Interaction.Behaviors>
</WebView>
The code in the ViewModel, that matches the tapped url against a list of valid options before opening the link in the device's browser, is:
public Command<WebNavigatingEventArgs> NavigatingCommand
{
get
{
return navigatingCommand ?? (navigatingCommand = new Command<WebNavigatingEventArgs>(
(param) =>
{
if (param != null && -1 < Array.IndexOf(_uris, param.Url))
{
Device.OpenUri(new Uri(param.Url));
param.Cancel = true;
}
},
(param) => true
));
}
}
You can´t navigate with a WebView, you must use a custom render (hybridwebview).
Here is an explanation:
https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom-renderer/hybridwebview/

With Glimpse How To Turn It Off Without Disabling?

I love Glimpse but only when I' interested in what it has to tell me. I have a glimpse role that I can turn on and off to make glimpse go away (see code below) but what I really want is to be able to turn it on and off while it is enabled in my global.asax. I've tried going to site.com/glimpse.axd and set "turn glimpse off" but then on the next page refresh it is back.
What am I missing?
public class GlimpseSecurityPolicy : IRuntimePolicy
{
public RuntimePolicy Execute(IRuntimePolicyContext policyContext)
{
var httpContext = policyContext.GetHttpContext();
if (!httpContext.User.IsInRole("GlimpseUser"))
{
return RuntimePolicy.Off;
}
return RuntimePolicy.On;
}
public RuntimeEvent ExecuteOn
{
get { return RuntimeEvent.EndRequest; }
}
}
In My Web.Config:
<glimpse defaultRuntimePolicy="On" endpointBaseUri="~/Glimpse.axd">
<runtimePolicies>
<ignoredTypes>
<add type="Glimpse.AspNet.Policy.LocalPolicy, Glimpse.AspNet" />
<add type="Glimpse.Core.Policy.ControlCookiePolicy, Glimpse.Core" />
</ignoredTypes>
</runtimePolicies>
Ok, the reason why clicking on the "Turn Glimpse Off" button has no effect is because the ControlCookiePolicy is disabled in the config, hence clicking that button will have no effect.
So you need to remove that entry from the config to make that work again:
<add type="Glimpse.Core.Policy.ControlCookiePolicy, Glimpse.Core" />
when you are saying that
The other code public class GlimpseSecurityPolicy" is in my global.asax
you mean that the GlimpseSecurityPolicy is basically defined as an inner class of the Mvc Application class?
Either way if you would enable logging for Glimpse in the config
<glimpse defaultRuntimePolicy="On" endpointBaseUri="~/Glimpse.axd">
<logging level="Trace" />
<runtimePolicies>
<ignoredTypes>
<add type="Glimpse.AspNet.Policy.LocalPolicy, Glimpse.AspNet" />
</ignoredTypes>
</runtimePolicies>
</glimpse>
then you should see a glimpse.log file appear in the root of your web application, and once the application is started, you should see an entry like this:
2014-06-13 09:48:25.8498 | DEBUG | Discovered IRuntimePolicy of type 'SOME NAMESPACE+GlimpseSecurityPolicy' and added it to collection. |
If that is the case then the policy is actually discovered.
You can put a breakpoint inside the Execute method to check whether a call is actually made and what the outcome is.

ASP.NET .cshtml razor file transform on Build

I'm trying to find a way to transform my index.cshtml file when building my project.
For instance make this:
<script src="app-dev.js"></script>
become this when Build mode is Release:
<script src="app-prod.js></script>
Ok, this will work.
Add an appsetting for it:
<add key="Environment" value="dev"/>
Then in your view add this:
<script src="app-#(System.Web.Configuration.WebConfigurationManager.AppSettings["Environment"]).js></script>
In your other environments simply use transforms to replace it i.e.
<appSettings>
<add key="Environment" value="prod" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
</appSettings>
An alternative and I feel a better approach would be to abstract out the WebConfiguration manager as an interface and have the app setting set in a model instead.
If it is in a common layout and set everytime maybe create a base model and have it set in an OnActionExecuted in a base controller instead.
In our case the decision has to be made based on whether the build is debug build or release build. We created a mvc helper extension method as
public static MvcHtmlString CustomScriptTag(this HtmlHelper helper)
{
var scriptString = string.Format("<script type='text/javascript' src='{0}'></script>", VirtualPathUtility.ToAbsolute("~/app-prod.js"));
#if DEBUG
scriptString = string.Format("<script type='text/javascript' src='{0}'></script>", VirtualPathUtility.ToAbsolute("~/app-dev.js"));
#endif
return new MvcHtmlString(scriptString);
}
From your cshtml file you can call it like
#Html.CustomScriptTag()

Custom AddForm template with Dexterity, on Plone 4

I'm having problems in getting my Dexterity content type to show a custom Add Form. I have already done this in a previous product, but, amazingly, I cannot accomplish this using Plone 4.1 and plone.app.dexterity 1.0.3
My CrmContact content type, living in package.name.types.contact.py, has its schema defined in this way:
from five import grok
from zope import schema
from zope.interface import implements
from plone.directives import form, dexterity
class ICrmContact(form.Schema):
"""A contact item for the CRM"""
title = schema.TextLine(
title=_(u"Company name"),
)
...
class CrmContact(dexterity.Container):
implements(ICrmContact)
class Add(dexterity.AddForm):
grok.context(ICrmContact)
grok.name('package.name.contacts.types.contact')
grok.template('add')
My template lives in package/name/types/contact_templates. It's a typical template. I know it's not being rendered because it has a dummy node that will call a non existing method using tal:content, in order to raise an exception; so I'm sure the template itself is not the issue.
My content type FTI is registered correctly during installation, and the content type is available and addable.
Finally, in profiles/default/types.package.name.types.contact.xml:
<?xml version="1.0"?>
<object name="package.name.types.contact" meta_type="Dexterity FTI"
i18n:domain="package.name" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
...
<!-- Method aliases -->
<alias from="(Default)" to="(dynamic view)" />
<alias from="edit" to="##edit" />
<alias from="sharing" to="##sharing" />
<alias from="view" to="(selected layout)" />
<!-- Actions -->
<action title="View" action_id="view" category="object"
condition_expr="" url_expr="string:${object_url}" visible="True">
<permission value="View" />
</action>
<action title="Edit" action_id="edit" category="object"
condition_expr="" url_expr="string:${object_url}/edit" visible="True">
<permission value="Modify portal content" />
</action>
</object>
Unrelated, but maybe I have to add something here...
I think I followed the correct procedure, as you may see, but I still cannot get it to work.
I know class Add is getting instanced because if I provide an updateWidgets() method and insert a breakpoint, it gets called; and when I introspect the object, self.template is None; even though:
(Pdb) getattr(self, 'grokcore.view.directive.template')
'add'
How can I provide a custom template to the Add Form of my custom type?
You should remove the line grok.context(ICrmContact).
From http://plone.org/products/dexterity/documentation/manual/developer-manual/advanced/forms:
Also note that we do not specify a context here. Add forms are always registered for any IFolderish context.

Resources