How can i use child only actions in asp.net mvc - asp.net-mvc-3

I am having a kendoUI TabStrip widget control. In it, I have two tabs : products and support.
The two tabs are two different views. I have defined the tabstrip like this :
<div id="tabs">
<div>
<iframe src="../Products/Index"></iframe>
</div>
<div>
<iframe src="../Support/Index"></iframe>
</div>
</div>
And in script :
$("#tabs").kendoTabstrip({});
Then tabs with their respective view pages are coming. But the products and support pages will not open directly. I mean in the url bar when I enter localhost:4567/Product/Index, it will not open the page, it will only open when we select the tab.
I have defined for both controllers such actions :
[ChildActionOnly]
public ActionResult Index()
{
return View();
}
Then when I run the main page, it is getting an exception it will be called only by child action only. Where when I select the tab it should become child action.
How can I rectify that problem? I have not included :
#Html.action("Index","Products")
If I need to include that, where should I add that?
Hope you understand my question...

The ChildActionOnly attribute ensures that an action method can be called only as a child method from within a view. An action method doesn’t need to have this attribute to be used as a child action, but we tend to use this attribute to prevent the action methods from being invoked as a result of a user request. Having defined an action method, we need to create what will be rendered when the action is invoked. Child actions are typically associated with partial views, although this is not compulsory
So instead
#Html.action("Index","Products").
you should try
#Html.Partial("Products").

Related

Open component view from code behind

I am using mvc 6 and I would like to "open" component view by using some button onClick.
I can display it by using
<div id="DialogDiv">
#Component.Invoke("MyComponent");
</div>
but I would like to do it so that some button onclick method would call that invoke method and give that div as a reference. Is that possible?
I assume that you want to render component by using ajax request. Since MVC6-beta7 you can do this by utilising View Component Action Result:
See:
https://github.com/aspnet/Mvc/issues/2338
https://github.com/aspnet/Mvc/releases/tag/6.0.0-beta7
The idea for your scenario will be to use JavaScript and call action in controller which will return ViewComponentResult and then render result of that action into your page. You can do this by using jQuery, so it will look like:
$.get("/YourController/YourAction",function(data){
$("#DialogDiv").html(data);
});

Update template inside a view rendered from another controller

I am looking for the way to refresh a template inside a view rendered from another controller than the template's controller, I mean:
I got two controllers AdminController & UserController. And two gsps /admin/listUsers & /user/_searchResult.
Then a want to render view listUsers who have inside the template _searchResult and all right.
Now, i want to refresh the template _searchResult, but cant find how. I tryed calling render(view:"/admin/listUsers", template:"/user/_searchResult", model:[searchResult:result])
AdminController.groovy
#Secured(['ROLE_ADMIN'])
def listUsers(){
//...
}
UserController.groovy
#Secured(['ROLE_ADMIN'])
def search(){
//search users for the givven params and send result by chain if there's an action or update a template if it's needed
//in my case this method need to update the template _searchResult
}
#Secured(['ROLE_ADMIN'])
def searchResult(){
//...
[searchResult:result]
}
listUsers.gsp
//...
<formRemote name="searchForm" url="[action:"search", controller:"user"]">
//Some fields for the search
//I need to place here some hidden inputs to send which
//template i want to update or action to redirect
</formRemote>
<g:render template="/user/_searchResult"/>
//...
_searchResult.gsp
//Just itterate and print the search result in a table
I hope I have explained the problem correctly, thanks!
I don't think I entirely understand your question, but I think the source of your confusion is that the way you are naming things doesn't follow regular conventions and you're not using the right tools for the job. Let me explain...
The methods on Controllers are called Actions. They send some data (the Model) to a View to be rendered into HTML. Views can be composed from smaller, reusable fragments called Templates. (sorry if I sound like I'm being condescending here, but I'm just trying to make sure we're all on the same page).
Now, what you've called templateA is actually a View, not a Template. You're correct that templateA (your View) can call templateB to render some markup, but then having the templateB try to call a method on another Controller doesn't make sense. That's not how things flow.
If you have some logic that needs to be executed after you've sent your Model to the View, you want to use a Tag Library (http://grails.org/doc/latest/guide/theWebLayer.html#taglibs).
To summarise, here's a quick recap.
A request should only call one Action, which sends the model to only one view.
If you need to reuse logic between Controllers, move that code to a Service.
If you need to reuse markup between Views, move that markup to a Template.
If you have logic that you want to have executed after you've sent the Model to the View, use a Tag Library.
Hopefully this will point you in the right direction.
--- UPDATE ---
OK, with the real code I can see better what you're trying to achieve. Firstly, as you're using the <g:formRemote> tag, you should have a good read of the docs at http://grails.org/doc/latest/ref/Tags/formRemote.html to understand what it does.
What you will have here is 2 separate requests. The first will be a regular page load by your browser, which is handled by the listUsers() action. Once the page is then finished loading, the user will enter a search term and hit the submit button. This will fire off a second ajax request, which will be handled by the search() action. This action could use the _searchResult.gsp template to render a HTML table to display the search results. When the browser get this, it will insert it into the DOM where you've told it to put it using the "update" attribute of the <g:formRemote> tag.
The important thing here is that from the server's perspective, these are 2 separate requests that are completely independent. They both first call an action, then send a model (a Map containing some data) to a view, which renders/merges the data with HTML and sends it back to the browser.
The difference between the 2 is that the first is a complete page load by the browser, whereas for the second request, the browser only loads a small chunk of HTML (the search results table) and updates the page content without reloading it.
So your code would look more like this...
AdminController.groovy
#Secured(['ROLE_ADMIN'])
def listUsers() {
render(view:"/admin/listUsers")
}
listUsers.gsp
<g:formRemote name="searchForm" update="insertSearchResultsHere"
url="[controller: 'user', action:'search']">
<input name="searchTerm" type="text" />
</g:formRemote>
<div id="insertSearchResultsHere"></div>
UserController.groovy
#Secured(['ROLE_ADMIN'])
def search() {
// use the search term to get a List<User>
render(template: "/user/searchResult", model: [users: users])
}
_searchResult.gsp
<table>
<g:each var="user" in="${users}">
%{-- Iterate through your search results --}%
</g:each>
</table>
I solved it by placing the attribute update and rendering the template alone:
gsp:
<formRemote name="searchForm" url="[action:"search", controller:"user"]" update="divToUpdate">
//Some fields for the search
</formRemote>
<div id="divToUpdate">
<g:render template="/user/_searchResult"/>
</div>
Controller:
def search(){
render(template:"/user/_searchResult", model:[searchResult:result])
}
When i asked this question, i was new on Grails community and i was confused with the use of remoteFunction and tags that use it like remoteForm. But i had this confusion because of i had not read the documentation. So in my case, the solution was search for documentation about how to use remote tags and render. Thanks to #AndrewW for show me the way.

How do I rebind the knockout viewmodel when the page is loaded in as a partial via ajax?

The page that I'm working with has a couple tabs and the content of each tab is loaded in via ajax by requesting a partial view from the controller. The problem is that the partial view uses knockoutjs, so it is bound to a view model. In this particular scenario, the page is loaded up in its entirety first time through, so all of the bindings work fine. When you switch tabs, it requests a partial view and replaces the tab content area with the new page. When you switch back to the first tab, it'll successfully loads the partial, except it would appear that all of the knockout bindings have been lost so there is a lot of missing data.
I can't place the viewmodel declaration and model bind in the partial because jquery hasn't been loaded by that point. Or so it would seem ($ is not defined).
The view model is declared and bound on the main page that calls the partial view(s), not the partial view itself, so I thought the model would still be available and bind successfully, but it does not. I know I'm doing this wrong, and partial view are super wonky when it comes to javscript so I'm hoping to steal a bit of insight from you guys.
Here's the basic setup:
If you are able to bind to specific non-overlapping areas of the page, then you could choose to call ko.applyBindings(someViewModel, someDomElement) like in this answer: Can you call ko.applyBindings to bind a partial view?
However, if you have an overall view model bound to the page and then "islands" of content that are loaded via a partial that you want to bind later, then one option would be to go for something like this: http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html. So, you would set up a binding on the container of where your partial goes that tells Knockout to keep its hands off of that area. Then when you load the partial, you can safely call ko.applyBindings(someViewModel, innerContainer).
The binding might look like:
ko.bindingHandlers.stopBinding = {
init: function() {
return { controlsDescendantBindings: true };
}
};
and you would use it like:
<div id="outerContainer" data-bind="stopBinding: true">
<div id="innerContainer">
...load your partial here
</div>
</div>
Then, ko.applyBindings(someViewModel, document.getElementById("innerContainer"));

Complex Pages and Data

I am trying to figure out the best way to handle pages in our application like the dashboard, where there are a number of different panes with various bits of data in it.
The main issue is that the controller action becomes unwieldy when the page needs so much data. The client side can be broken up into partial views to make it more manageable, but all the data still needs to passed into the View to be distributed down to the partials. Or does it?
Obviously some of the panels could be loaded dynamically or something like that, but I was looking for the best approach besides loading individual piece of the page from the browser.
Have you considered using Html.Action in your view? You would pass enough data to the main view to enable you to give the required data to each of the child actions. The main action would render the main view which would call actions for each pane. Each action would be responsible for that pane, rendering its own partial view. Additionally, you could call back to each of the child actions directly from the client to update that pane dynamically via AJAX.
Here's an example with some mocked up actions of what your main view might look like.
<div class="left-pane">
#Html.Action("Summary", new { id = Model.ID } )
</div>
<div class="middle-pane">
#Html.Action("PendingItems", new { id = Model.ID, timestamp = DateTime.Now } )
</div>
<div class="right-pane">
#Html.Action("News")
</div>

View that hides/shows controls

I am in the process of porting a site I wrote from ASP.NET webforms to MVC3 and need some guidance as outlined below. I'm new to MVC3.
In my existing ASP.NET web forms project I have a simple page where the user enters a username, they then click a button which causes a postback, on postback there is some basic code that checks if the entered username exists in a user repository - if it does, a textbox containing the users e-mail is shown and the username textbox is made invisible. This happens with ajax and so when the username is entered, the textbox containing the e-mail along with an "Update" button is shown without a full page refresh.
I created a model such as:
public class ChangeEmailModel
{
[Required]
public string Username { get; set; }
[Required]
public string Email { get; set; }
}
Problem is that when the user first enters the page, they should only see a textbox prompting them to enter a username. Once the username is entered and an update button clicked, only then their e-mail is shown (retrieved from the database). Once the e-mail is shown, they can edit the e-mail and click update, which then will need to post to a controller action that saves the updated e-mail. I'm not yet fully used to thinking in the MVC way, so I'm not sure if I've started on the wrong foot with the model above...
Can someone give me some guidance on how this can be accomplished in MVC3 so I can give it a try?
I will start off by suggesting that you start using JQuery for your javascript/ajax functions. ASP.Net MVC3 supports JQuery nicely. I will ignore validation of the email for now as it will be much easier to get you started without it. A high level overview will be:
Add the JQuery script to your page
Add the JQuery vsdoc script to your page so you have some intellisense
Create a partial view to show the email and submit button
Create a controller action that performs the email lookup you mentioned
Create a div to accept the newly returned Email Update form
Use JQuery to override the submit on your username lookup to perform an ajax update instead (and populate the Email Update form div)
1. Add the JQuery script to your page
This should be pretty easy - just drag it from your scripts folder. I think mvc3 comes with jquery-1.5.1.js. Use the min (minified) version when you release to production.
2. Add the JQuery vsdoc script to your page so you have some intellisense
Not quite as easy here - you will want to use an if statement that always evaluates to false so the script is not actually included in your content. Having it on the page though, will cause VS to use it for intellisense. Put this near the top of your view:
#if (false) { <script src="../../Scripts/jquery-1.5.1-vsdoc.js" type="text/javascript"></script> }
Hopefully you are using Razor. If not, start using it. It seemed a little foreign to me at first, but it requires much less markup.
3. Create a partial view to show the email and submit button
You could use the ViewBag to pass the Email address and UserName (for now as we are ignoring validation), but go ahead and make it strongly typed to your Model from above. Your view may look something like this:
#model ChangeEmailModel
#{using (Html.BeginForm("UpdateEmail", "Home", FormMethod.Post, new { id = "UpdateEmailForm" }))
{
<input type="hidden" name="userName" value="#Model.UserName" />
#Html.EditorFor(m => m.Email)
<button id="submitEmailUpdate" type="submit">Submit</button>
}
}
Note that we have given Ids to the form and the submit button. JQuery will find the form and button based on these ids. (if we need to, which we will if we want to "ajaxify" the action of updating the email. I did not go into that detail here, but it will be the same process to get that working as it is for the original username lookup)
4. Create a controller action that performs the email lookup you mentioned
I won't go into controllers much here (as you are asking about ajax type updates) but it might look like:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LookupEmail(string userName)
{
//connect to db and lookup email based on passed in username
//create a new instance of your model
var changeEmailModel = new ChangeEmailModel(.....)
//return a partial view
return PartialView("EmailUpdateForm", changeEmailModel);
}
Make sure to return a PartialView here rather than a View.
5. Create a div to accept the newly returned Email Update form
Make sure this div is not contained in your Username lookup form (as you want to hide it). We will be working with two separate forms. This div could be hidden if you prefer (but will start out empty anyway) I am calling it emailFormDiv
6. Use JQuery to override the submit on your username lookup to perform an ajax update instead
JQuery will allow you to attach functions to... well a lot of things, but we will be using it to override the submit button on your username lookup form. Assume that your original username lookup form with an id of "formUserNameLookup" that has a submit button with an id of "submitUserNameLookup". You would then create a script tag that looks something like this:
<script type="text/javascript" language="javascript">
$(document).ready(function () { //The document.ready function will fire when the html document is... ready
$('#submitUserNameLookup').click(function (ev) { //fires when the submit button is clicked
ev.preventDefault(); //prevent the normal action of the button click
$.post($('#formUserNameLookup').attr('action'), //get the url from the form's action attribute. Could be hard coded for simplicity
$('#formUserNameLookup').serialize(), //serialize the data in the form
function (response, status) {
$('#emailFormDiv').html(response); //replace the html of your div with the response
$('#formUserNameLookup').hide(); //hide the original form
}, 'html'); //states that we are expecting html back from the post
});
});
</script>
The code above is attaching a function to be run when the submit button is clicked. It won't run, of course, until the button is actually clicked. Using JQuery/Javascript to attach functions to html elements, rather than embedding them directly inside the element is definitely preferred, and is referred to as unobtrusive javascript. If you continue with ajaxifying more of your page, you will want to look into JQuery's live and/or delegate functions. Note that there are plenty of things that can be changed once you start looking toward performance and/or best practices. The above should get you going though. I hope I haven't made too many assumptions on your current level of familiarity with ASP.Net MVC (like controllers and posting to controllers) but by all means, ask if you need further help.

Resources