I have a web page with a couple of view components, when I click on these components I open a simple editor for it, see image below. If I edit the text and hit enter, I would like to rerender the view component and not the hole page. Is it possible to invoke a view component using javascript to get this behaviour?
With updates, you should now be able to do this with asp.net core. You can return a ViewComponent from an IActionResult, then just call it with jQuery:
[Route("text-editor")]
public IActionResult TextEditor()
{
return ViewComponent("TextEditorViewComponent");
}
Then this can be called from your View with something as simple as this:
function showTextEditor() {
// Find the div to put it in
var container = $("#text-editor-container");
$.get("text-editor", function (data) { container.html(data); });
}
If you need to pass parameters to the ViewComponent you can do so in the IActionResult (maybe they trickle through from the View to there, to the view component, etc).
This is not possible today but you could build a standard proxy for viewcomponents if you'd like though (similarly you could build a proxy to partial views). e.g. you a catchall attribute route on an action.
Related
i currently have the problem, that i want to use a reference of an existing java class in javascript to be able to execute methods from Javascript of the Java Class to use Shepherd (Javascript Tool for guiding users through the web app). Im using Vaadin for Web deployment with java but Shepherd only works through Javascript.
I dont want to execute the JS from the Class with the methods, moreover i want to execute it from an other class, so it looks like this:
User clicks on a Button
Button button = new Button(); button.addClickListener (c -> tourPageController.executeShepherd(UI.getCurrent().getInternals().getTitle());
Java Code to find active Page
public static void executeShepherd(String appTitle) {switch(appTitle) {case "example": UI.getCurrent().getPage().executeJs("window.startTour($0)", <here i want to reference the existing class with the methods);
execute the Shepherd Tour four the explicit page, the user is on (For that, i need to hand over
a reference of the class holding the methods to execute via javascript), its looks something like this in the header:
window.startTour = () => { ... }
Javascript is getting the parameter (the class)
From Javascript im going to use Shepherd, but there are references to Java Methods (these Methods are for the Vaadin Components, to open and close the respectively tabs i want to use in the tour). I use it like this:
beforeShowPromise: function () {
return new Promise(function(resolve) {
classParameter.$server.<method()>;
resolve();
});
},
How can i do this?
What you can do is to set id to the component in Java
component.setId("component-x");
This will mean that the DOM element will be have the id attribute set.
So if you have some bookkeeping of the components in your Java code, you can pass the id as a parameter in that #ClientCallable call, and then search for the component matching that id in your Java code.
If this is about only one view specific component, you do not need anything else than storing reference to the component in a class field (not even the id). But if you indeed have more than one Accordion in your view, the idea is like this.
public class MyView extends Div {
List<Accordion> accordions;
public MyView() {
Accordion accordion = new Accordion();
accordion.setId("accordion-1");
accordions.add(accordion);
...
}
#ClientCallable
public openAccordion(String id, int index) {
accordions.stream().filter(acc ->
acc.getId().equals(id)).findFirst().ifPresent(acc -> acc.open(null));
accordion.open(index);
}
}
For context, see: How can I change Vaadin Components in Java through Javascript
I'm getting used to view components in MVC 6, and I asked a similar question a few years ago about partial views. If I build a view component encapsulating a common use-case that requires its own Javascript, where do I put that Javascript? I know that it is dangerous at best to have Javascript in partial views, but it would be a lot simpler to include it in the view component, rather than in the containing view or a separate file that has to be referenced by the containing view.
For example, say I have a view component that has two drop-downs. The selection in the first drop-down determines what items appear in the second drop-down. This is easily handled in Javascript, of course, but where do I put it?
From my experience with ASP.NET 5 View Components, I would say that the best thing to do with them is to keep them isolated and in one place, so they will be easily to manage in long-term projects.
In one of my ASP.NET projects, I've developed View Components structure like this one:
View, Backend code and Model are all in one place, so when you move around the folder, you are sure that you move whole component. Moreover, when you are modyfying them, you have quick access to all of their parts.
It will be convinient to put JavaScript which is highly coupled with a component also in such structure. You can do this by simply creating the file under the component's folder, and then writing a GULP TASK that will copy JS file to wwwroot. From that point, you will be able to link that JavaScript code on component's .cshtml using standard syntax:
<script src="~/Components/yourcomponent.js"></script>
To obtain such a structure in my project, I've extended Razor, to be able to search for my component's CSHTML's in proper place. To do this, I've added this code in Startup.cs:
public partial class Startup
{
public void ConfigureServices(IServiceCollection services)
{
//non relevant code skipped
services.AddMvc().AddRazorOptions(ConfigureRazor);
}
public void ConfigureRazor(RazorViewEngineOptions razor)
{
razor.ViewLocationExpanders.Add(new ViewLocationExpander());
}
}
and the ViewLocationExpander class is:
public class ViewLocationExpander : IViewLocationExpander
{
protected static IEnumerable<string> ExtendedLocations = new[]
{
"/{0}.cshtml"
};
public void PopulateValues(ViewLocationExpanderContext context)
{
//nothing here
}
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
{
//extend current view locations
return viewLocations.Concat(ExtendedLocations);
}
}
Then, you invoke component like this (from any .cshtml view):
#await Component.InvokeAsync("NavigationComponent",new NavigationComponentModel())
I know the sendAction will send actions from a component to the controller associated with the template where it has been placed, but in my case the component is not placed directly inside a route's template. Instead, my component is inside a view's template:
<script type="text/x-handlebars" data-template-name="displayTemplate">
<h3>View</h3>
...
{{componentA}}
</script>
This component has a controller associated with it:
App.ComponentAController = Ember.Controller.extend({
...
}
But the wrapping view does not, it's just a view:
App.DisplayView = Ember.View.extend({
templateName: 'displayTemplate',
actions: {
updateData: function(data) {
/* how can I get this triggered when an action happens in the embedded componentA? I'd like to
process the data object here so I can update the wrapping view accordingly. */
}
}
...
}
If I implement the action in the component's controller, it is not triggered when I perform a sendAction('updateData', data) from within the component. If I implement that in DisplayView, it is not triggered either.
So the question is: when an action is triggered in componentA, how can I send that action to the view DisplayView for handling?
More specifically, I'm trying to send context data to that view so it can be updated depending on what is selected on the embedded component. So if there is another way of communicating this data that'd work too. I chose actions just because it seemed appropriate, but maybe it is not.
UPDATE: The actual scenario
The actual code refers to a grid view, which in it's template has a pagination component. When the user switches to a new page, the pagination component sends a request to the server for the selected page.
Once data is returned, it then needs to let the wrapping view (which contains the grid) know that new data is available. When the user clicks on the page number, the action is handled in the pagination component and that's why I make the data call from there. If I needed to paginate something else, I wanted to reuse this component so I didn't want to make the pagination part of the grid view.
Normally you pass target, but the context of a component the property name is targetObject.
{{foo-bar action='actionName' targetObject=view}}
I have a SAPUI5 master detail page application. So in master page controller I need to access an element which is defined in detail view. How can I do it?
jQuery.sap.require("util.Formatter");
jQuery.sap.require("util.Networkaccess");
sap.ui.controller("view.Sales.SalesOrder.SoMaster", {
myFunc: function() {
var iconTabBar = this.byId('itabBar');
iconTabBar.setSelectedItem(iconTabBar.getItems()[1]);
}
})
The above code will through error because the itabBar element is not there in master view, it's defined in detail view file.
i am not sure i exactly know what you are trying to do, controls have unique id's prefixed by the view, you could try
var iconTabBar = sap.ui.getCore().byId("vwDetails--itabBar")
I'm trying to call view method from controller, but no idea how to do this. From view I can easily call controller method like this.get('controller').send('method');
How to do something like that from controller this.get('view').send('method');?
To give you better overview what I'm trying to do.
I have application controller Ember.Controller.extend({}) I have application view Ember.View.extend({}) and application template.
In application template is login form, when user submit it controller method is executed. In this method if login credentials are incorrect I need to call view method which is executing jQueryUI method on login form (shake method to be exact and showing some text).
This sounds like a good use for Ember.Evented. By using event subscription and dispatching you can avoid coupling your view and controller.
Simply mixin Ember.Evented:
Controller = Ember.Controller.extend(Ember.Evented)
Now you can call on and trigger methods on your controller, to subscribe to an event and then to kick off the event. So, in your view you might do:
didInsertElement: function () {
this.get('controller').on('loginDidFail', this, this.loginFail);
}
And then in your controller call this.trigger('loginDidFail') to kick off your loginFail view method.
Remember to remove the handler after the view is dismissed... see the answer below.
Just wanted to answer on this question to address the issue with properly removing the listener if the view is cleared (when the route changes). It's also not necessary to use a jquery proxy, since the on/off methods support a target, which is good because unsubscribing a proxy is definitely more complicated. Revising what Christopher provided:
didInsertElement: function()
{
this.get('controller').on('loginDidFail', this, this.loginFail);
},
willClearRender: function()
{
this.get('controller').off('loginDidFail', this, this.loginFail);
}
Without removing the subscription any subsequent visits to the login route (without reloading the page) will add additional listeners; i.e. memory leaks, errors, and unexpected behavior.