First, I'm sure I'm missing a best-practice way to navigate to and load partial views. So, if I'm going about this all the wrong way, please let me know. I have a feeling I'm making this more difficult than it needs to be.
What I'm trying to accomplish is having my web site behave like a single page app. In order to do that, I have many partial views within divs. I hide all divs but the active one. I'm not sure how to run code in a partial view only when it is shown.
Here is the div and partial view that I'm concerned with in this scenario (this is in Index.cshtml):
<div id="app">
#Html.Partial("~/Views/PartialViews/App.cshtml")
</div>
And this is the JavaScript that shows that partial view (also in Index.cshtml):
function showApp(id) {
hideAll();
$('#txtAppId').val(id); // This is how I'm passing data to the partial view
$('#app').show();
}
This works well (at least as far as showing the partial view), but how can I get code in App.cshtml to run only when it is shown like this?
In App.cshtml (the partial view):
<script>
$(function() {
// Note: This will execute as soon as the main page is loaded. I don't want
// code to execute right away, but only when this view is shown.
});
function doSomething() {
// Only when this view is shown do I want code to execute here.
}
</script>
How can I have doSomething() run when the partial view is shown?
Invoke the function after you show the partial view, in index.cshtml
function showApp(id) {
hideAll();
$('#txtAppId').val(id); // This is how I'm passing data to the partial view
$('#app').show();
doSomething(); //Here invoke it.
}
You cannot directly detect when an element changes its visibility and run some script, here you have the control of when it becomes visible so invoke the method once you make it visible.
Related
I have an MVC 5 view that acts as a parent view. Based on certain activities a user performs, I will load a partial view into the parent view. The partial view is loaded as part of a javascript function call. In my javascript call, I am loading my partial view with the content returned in the "data" variable below:
$.get(url, function (data) {
$('#id-ContainerForMainFormPartialView').html(data);
});
The data is written to an HTML div as follows:
<div class="container" id="id-ContainerForMainFormPartialView">
</div>
Immediately after the $.get call I run the following statement to disable a button that is part of the view that has been returned and written to the div:
$('#idAddLineItem').prop("disabled", true);
When the javascript function has completed the button is not disabled. Yet, I am able to disable the button using the same disable statement above using a button. I think that after the $.get invocation has written the partial view it is too soon to try and do something to any elements that are part of the partial view.
Is there an event I can hook into or something that will signal me when the time is right to try and do something to any of the elements of the partial view that has been loaded such as disabling a button which is what I am trying to do? Something like the javascript addEventListener() method that allows you to run code when certain events happen but in this case I need it to fire after a partial-view load is considered completely rendered and ready to use. An example would be greatly appreciated.
Thanks in advance.
Based on blex's statement the solution is as follows:
The correct way to disable a button that's part of a partial view being output:
// Correct Approach
$.get(url, function (data) {
$('#id-ContainerForMainFormPartialView').html(data);
$('#idAddLineItem').prop("disabled", true);
});
The incorrect way to disable a button that's part of a partial view being output:
//Incorrect Approach
$.get(url, function (data) {
$('#id-ContainerForMainFormPartialView').html(data);
});
$('#idAddLineItem').prop("disabled", true);
I originally had the disable statement outside the $.get call which allowed the code to run past it before the view was ready due to the asynchronous nature. Placing it inside the $.get allows it to not run until the partial view is done being output.
good day all.
I'm working on a project in which there is an application that has one of its view implemented with an iframe, the iframe src is changed when the user clicks on some of the "parent" document. So basically there is always the same container, but the contents of the iframe will change according to the user choices.
let's say that there will be:
parent.html (which will have all the js logic)
child1.html
child2.html
...
each "child" page will be an html page with no (or very little) javascript. What I want to obtain is that when the user arrive on the child1.html, only the code that is global to every child is execute and of course also the code related to that page.
Let's say that on the child1.html there must be executed a couple of ajax calls, then some js to handle tables, and things like that. while on the child2.html there will be some forms whith their own validations, and another ajax call to send the forms (displayed on the child1.html).
There will be a big js library on parent.html that will contain the js code of every child page, so what I'd like to have is a way to "know" in which page I am and execute only the portion of code that is related to that page.
the structure should be something like:
var myGlobalObject = {username:undefined,foo:1}
if(childpage1.html){
if (myGlobalObject.username == undefined){
$.ajax(retrieve username);
$("#someTableIniFrame",iframeContext).doSomething();
}
}
if(childpage2.html){
$("body",iframeContext2).on("submit","#someFormOnChild2", function(){
//do something
});
}
and/or something on childpages that could execute only its code... like:
document.addEventListener("DOMContentLoaded", function(event) {
//execute only my part of the global js!
});
I hope to have been clear on what I'd like to obtain:
a parent page, with all the js used in childs, executed on demand OR with the capability to understand in which page we are.
several child page without or with a very little js.
Just for information, the iframe src will be changed by js on the parent page, by destroying the previous one and adding a new iframe with the new source.
If you want to keep all the Javascript in the parent page then you just really need a way to map the child pages to any code you wish to execute. This is a long way around doing something, but without further context it's difficult to suggest a more appropriate solution.
With that in mind, here's how I'd approach your problem.
First of all, I'd create an array of objects that defines what script to run for each child page...
var childScripts = [{
"src": "childpage1.html",
"init": function() {
// what to do when childpage1 is loaded
}
},
{
"src": "childpage2.html",
"init": function() {
// what to do when childpage2 is loaded
}
}];
Don't destroy and recreate the iFrame every time you want to load a new page, or (if you really have to), assign an event handler to the load event every time. You only have to do this once if you never destroy the iFrame...
$("#iframeId").on("load", function() {
var scriptInfo = childScripts.filter(function(childInfo) {
return window.location.href.slice(-childInfo.src.length) === childInfo.src;
});
for (var i in scriptInfo) {
scriptInfo[i].init();
}
});
Obviously replace the selector #iframeId with something that will find your iframe.
In short, you create an array that holds each child page filename (prefix with / so you don't run scripts on pages that end with the same thing, but aren't the same page), and a function that you want to execute when that page loads. You then parse that array each time the iframe is loaded and execute all associated functions. Realistically you'll only have 1 init function per child page, but that code will handle multiple instances.
So, I admit that it might be tricky for somebody to follow my design and build patterns. But I will try best to describe the issue, although I feel this might be easier than getting you to understand my project :) But hell I've spent 3 days trying to figure this one out.
Summery of technical layout
I have a global view (lets call this GV, for global view), everything goes through this template.
I have navigation, this can call one of the 5 separate views on click.
Each of these views first uses the same view (DV for defaultView) which then extends this view and loads details from its own JST (OV for OwnView)
The problem is that, when clicking to load an OV the element that it is bound to load into (target) has not yet been rendered to the page. It is however available in the DOM, and in fact updates the DV with its OV but does not render to the page.
Brief overview - To give you a idea of how my views are set up and what extends what
GV - Loads: login, Home, Global Navigation
DV - Loads: the navigation for this section.
DV extends GV: SD.defaultView.extend
OV - Loads: h2, icon for the option and buttons
each OV extends DV so that I can keep all the click events in one view and not have to paste code all over: SD.defaultSexView.extend
The problem
OV loads into an element that is inside DV. GV and DV all load perfectly. I can click from, login -> home -> navigation -> through which would then load in the DV. This is where the interactions break down. All that is loaded is whatever was inside the JST for DV
Build
Just to give some background information:
I am using JST's to precompile all of my templates.
main.js loads all of my dependencies via require. It also handles my routes
sdFunctions.js has globals and other important things. But the most important is 2 global default views.
1: The GV that everything goes through initially
2: The DV that all options go through (The main navigation menu)
Outline
I know that for OV to load it must have the element that its loading into available. This is el: 'sexdetails'. This element gets loaded from the DV. So I realise that DV needs to be in the DOM for the OV to load. Otherwise it has no element to load into.
This is a console load. page is from the GV sexdetails gets loaded in from the DV and this is where the OV gets loaded into. all these consoles are out put in loading order. So from the top to the bottom.
The bottom seems to be seen in the last console.log which is greyed out as the element has been built with all the correct information. But for whatever reason it not output onto the page.
JS
Now the exciting parts :p
DV - This is the second defaultView, the one that handles the menu and where the click events are bound.
SD.defaultSexView = function(){
//set up homeview
var sexView = SD.defaultView.extend({
el: 'page',
jstemplate: JST['app/www/js/templates/sex/sexTemplate.ejs'],
events:{
"click sexoptions > *" : 'changeSex'
},
changeSex: function(elem){
var me = elem.currentTarget.localName;
$('.selected').removeClass('selected');// #update selected from bottom navigation
$(me).addClass('selected');
SD.pageLoad(elem); // #Call function that will re-render the page
},
render: function () {
var compiled = this.jstemplate();
this.$el.html(compiled);
}
});
SD.DSV = new sexView();
SD.DSV.render();
return sexView;
}();
Own View - each of the 5 files has one of these
//set up homeview
var wank = SD.defaultSexView.extend({
el: 'sexdetails',
jstemplate: JST['app/www/js/templates/sex.ejs'],
data: {
url: SD.HTTP+'stats/add',
sextype: 'wank',
header: 'SOME LOUD STRING!!!',
image: '/img/path.jpg'
},
render: function () {
c($('sexdetails'));
c(this.$el);
var compiled = this.jstemplate(this.data);
this.$el.html(compiled);
}
});
return wank;
All the code in full working order can be found here: http://m.sexdiaries.co.uk/ - don't be put off by the URL, I assure you its SFW. Everything is done with cartoons and in no crude.
Interestingly if I update the code to use: $('sexdetails').html(compiled); it will indeed display correctly, but none of the events will be bound correctly.
Sorry there is so much to take in, I'll be very very impressed if anybody takes the time out to read or understand this :)
I couldn't look into full code. But from my past experience I can say that you are rendering view on a element which might have been removed from page DOM. When you update this.$el.html() of a view, it will take away element from DOM but still maintain elements if referred in a closure. I can see that you are passing
SD.pageLoad(elem)
to some function which does rendering. Since elem is an object, it's passed by reference. even after you update view's template later with
var compiled = this.jstemplate();
this.$el.html(compiled);
element that is send to rendering function remains in memory, but not in DOM, so any element rendered on this element will not be displayed on page. Also you are using $ inside a view, but you should be using this.$. this will make sure that, code from a view will never change elements outside that view.
After taking a look at your code, I noticed that in your "Wank" view, that you are setting el to sexdetails, which doesn't fly. I wasn't able to dig into the details of why this is, but I assume it has something to do with lack of DOM specificity.
Setting el to "body content page" however, allowed to view to render without any issues, as this allowed to view to hook directly into the body of the page and lets the render function do the rest of the work for you.
Hope this helps!
I'm trying this (Using Jquery in Codeignitor app to update div every 10 seconds), it is working but it shows my whole view in my div
I work with hooks and I have a masterview where I load other views in it...
When I load my controller in to my div, it shows my masterview in my div with the view I request in my controller.
How can I just show the requested view without my masterview? Can you help me?
thnx Cheers
A quick solution that I've used in CI is to implement a check in the controller to see whether this is an AJAX request. If so, I load the view with data into a variable and return it to the browser as a chunk of html.
if($this->input->is_ajax_request())
{
$view_result = $this->load->view('child_view_for_div', $data,true);
echo($view_result);
}
else
{
//Return view with master template
}
I recently added a feature to our ASP.NET MVC web application. There's a page that is displayed when the user clicks on an item in a table. The page uses AJAX to display a partial view in a single div in the page's HTML. The partial view uses the Telerik Kendo UI to define and display dialogs and DropDownList controls. This is complicated in that the JavaScript imports are all on the View for the page, while the PartialView just builds the HTML to be displayed in the div.
The JavaScript I wrote on the page includes a jQuery document.ready event handler:
$(document).ready(
function () {
if ( $('#details-map').val() != '' )
$('#details-map').remove();
var urlTail = '?t=' + (new Date().getTime());
// Make the AJAX call and load the result into the details box.
$('#detailsbox').load('<%= Url.Action("Details") %>' + '/' + '<%: Model.Id %>' + urlTail, displayDetails);
}
)
This works fine when I run the application on my localhost. The problem appears when I deploy the page to our development server. In this case, there's an additional document.ready event handler that's emitted to very end of the page by the Telerik Kendo / ASP.net MVC extensions:
<script type="text/javascript">
//<![CDATA[
jQuery(document).ready(function(){
if(!jQuery.telerik) jQuery.telerik = {};
jQuery.telerik.cultureInfo=...;
});
//]]>
</script>
On this page, the $(document).ready event handler I wrote runs before the Telerik handler, and my code clearly depends on the Telerik handler running first. When mine runs first, I get a JavaScript error that says "jQuery.telerik.load is not a function'.
Since this does not happen on my localhost, how do I make sure that the second document ready event handler is run first?
Edit:
After more research, I've found that the problem is that the two scripts mentioned in my answer, which are supposed to be written to the page via the following line in the Master Page used by my page:
<%= Html.Telerik().ScriptRegistrar().Globalization(true).jQuery(false).DefaultGroup(group => group.Combined(false).Compress(false)) %>
Are not being loaded. In other words, the above line does nothing. It works on another page that uses the same Master Page, though. I've placed a breakpoint on the line and it is executing. Does anyone have any ideas?
You're either going to have to make it show up after the second one on the page OR do something I hate to suggest:
$(document).ready(function() {
function init {
/* all your code that needs to be run after telerik is loaded */
}
if ( $.telerik ) {
init();
} else {
setTimeout(function check4telerik() {
if ( $.telerik ) {
return init();
}
setTimeout(check4telerik, 0);
}, 0);
}
});
So, if $.telerik is loaded, it runs, otherwise it polls until it's set, and when it is set, it runs the code. It's not pretty, but if you don't have more control, it's probably your only option, unless $.telerik emits some kind of event that you can hook into.
After spending a few hours working on this, I finally got it to work. The problem turned out to be that two JavaScript files that are used by the Telerik DropdownList control and Window control were not being loaded.
In spelunking through the JavaScript on the page, I came across this script that was generated by Telerik:
if(!jQuery.telerik){
jQuery.ajax({
url:"/Scripts/2011.3.1306/telerik.common.min.js",
dataType:"script",
cache:false,
success:function(){
jQuery.telerik.load(["/Scripts/2011.3.1306/jquery-1.6.4.min.js","/Scripts/2011.3.1306/telerik.common.min.js","/Scripts/2011.3.1306/telerik.list.min.js"],function(){ ... });
}});}else{
jQuery.telerik.load(["/Scripts/2011.3.1306/jquery-1.6.4.min.js","/Scripts/2011.3.1306/telerik.common.min.js","/Scripts/2011.3.1306/telerik.list.min.js"],function(){ ... });
}
This script was emitted by a call to Html.Telerik.DropdownListFor() in my partial view. I'm guessing that the code to include for the two JavaScript files mentioned in the code, telerik.common.min.js and telerik.list.min.js, was not emitted since the call was on a partial view. Or I was supposed to include them all along & didn't know it (I'm very new to these controls). Oddly, everything worked when I ran it locally, so I don't get it.
In any case, I added two <script> tags to my View to include these files and everything started working. That is, I added the following tags to my page:
<script type="text/javascript" src="../../Scripts/2011.3.1306/telerik.common.min.js"></script>
<script type="text/javascript" src="../../Scripts/2011.3.1306/telerik.list.min.js"></script>
Live and learn.