Knockout + Ajax Content results in multiple binding error - javascript

I have built a web application with multiple pages. Some of them are Knockout-driven.
I am trying to apply some Ajax-optimized page loading and stumble over the following issue.
Say I have the following general page structure
<body>
<div id="content">
</div>
</body>
And the following view, which is using Knockout. I include the call to applyBindings inline for being able to load the right ViewModel for every view.
<section id="editor">
<ul data-bind="foreach: items">
....
</form>
</section>
<script>
ko.applyBindings({items: {}}, $("#editor").el)
</script>
I load the view asynchronously into div#content for example using JQuery.load("editor.html #content")
The first page load works fine, but when navigating away (again using JQuery.load) from this view and coming back again I receive the error:
You cannot apply bindings multiple times to the same element.
I have already tried to apply ko.cleanNode but with no success. What am I missing? The #editor node should be removed from the DOM when other content is shown. So I really do not understand how to clean bindings or reinitialize knockout.
Note: I do not want the old data, I want to initialize the Bindings like on a freshly loaded page

Could you test your $("#editor").el in console? It doesn't work in standard jQuery.
If your $("#editor").el returns undefined, your ko.applyBindings({items: {}}, $("#editor").el) is essentially binding to window.document.body.
You may try
ko.applyBindings({items: {}}, $("#editor").get(0));
...
// call cleanNode before loading new page.
ko.cleanNode($("#editor").get(0));
$("#content").load( "newpage.html" );

if your bindings in "editor" section doesn't change,i suggest you to load(AJAX) only json data from server,and replace(modify) your viewModel in the browser,in that way knockout will refresh the dom automaticly.

Related

Can you change the order in which RenderSection executes?

Is there a way to tell the RenderSection to run after everything else on the _layout page is completed?
I have a RenderSection which contains Javascript. I want to modify elements on the _Layout.cshtml page.
If the RenderSection is called after the element on the _Layout page then it works fine but if RenderSection is called before the end it doesn't work since the element in the _Layout does not yet exist.
<h1 id="AA"> NewContent </h1> <!-- works because AA exists -->
#RenderSection("MySection") <!-- Contains javascript to add New Content to AA and BB -->
<h1 id="BB"> </h1> <!-- Does not receive content because it does not exist yet -->
I was unable to find a way of changing the order that Razor renders sections but I found a work around.
There's a tutorial on W3Schools which allows you to insert HTML from a URL into a DIV of your choice.
How to insert HTML tutorial
This allows you to specify a URL in your DIV from where to load the HTML. In my case, I just loaded it from one of my MVC controllers.
<div w3-include-html="content.html"></div>
You can then just call a method when you are ready to load your HTML in.
includeHTML();

Problems with vue.js content rendering

I've just learning Vue.js and I'm getting stuck with some problems with its rendering.
Let's say I have the following lines of code:
index.html
<div id="header">
<h5>{{pageName}}</h5>
<p>{{pageSubtitle}}</p>
</div>
app.js
var header = new Vue({
el: '#header',
data: {
pageName: 'CuteCat',
pageSubtitle: 'World of cats'
}
});
When I load the page, the CuteCat and World of cats is shown perfectly but when I view source, this is what I get:
<div id="header">
<h5>{{pageName}}</h5>
<p>{{pageSubtitle}}</p>
</div>
What can I do to replace the mustaches brackets in the view source with its declared value like this?
<div id="header">
<h5>CuteCat</h5>
<p>World of cats</p>
</div>
Just as #yuriy636 is telling you, this is not an error by any means.
Vue is a JavaScript UI framework, making its magic in the client (i.e. in the browser). In the source view you see what's been loaded from the server and what you see is exactly that.
If you disable JavaScript for a session and reload your app, the double mustaches will be visible. Because they are replaced by Vue when JS is on.
Edit: In the DOM, however everything is normal after Vue rendering, just as you would expect it.

load react script inside text/template script

I'm trying to contribute to a project that uses <script type="text/template"></script> for rendering the elements of a page.
My contribution is to change the elements of the page into react components. However when I order the react to render in a specific div with ReactDOM.render() I get an error saying
Uncaught Error: _registerComponent(...): Target container is not a DOM element.
I know that means that react doesn't find the div where to render so propably I will need to load the react script after the div, I've tried that but the error is there again.
I've tried loading the react script otherwise like this
<script type="text/template">
<ul class="new-component-template" id="xblocklist" >
</ul>
<script type="text/babel" src="path to react file"/>
</script>
but when I load the page the error is gone an the script is not loaded at all.
What I need is to load the script when the outer script is called, I.E can I write a function inside <script type="text/template"></script> that actually loads the react script inside.
UPDATE
var ListXblocks = React.createClass({
componentWillMount: function(){
this.setState({Problemstyle: this.props.Problemstyle})
fetch('http://192.168.33.10:8002/XBlocks/')
.then(result=>result.json())
.then(result=> {
this.setState({items:result})
})
},
render:function(){
return(
<div>
{
this.state.items.map((item)=>{return(
<li className="editor-manual" key={item.title}>
<button type="button" className="button-component" data-category="problem" data-boilerplate="">
<span className="name">{item.description}</span>
</button>
</li>)})
}
</div>
);
}
});
ReactDOM.render(<ListXblocks/>, document.getElementById('xblocklist'));
Script tag with type="text/template" doesn't do anything particularly and it just let browser to ignore what inside it. This approach usually uses by templating systems like handlebarjs and React doesn't support it. You can read more about this here. So if you put your React scripts also inside that, the browser is just going to ignore that as well.
Beacuse your ul tag is not a html element, document.getElementById('xblocklist') is going to return null. That's why you get "Target container is not a DOM element." error. So you have to get the html out of the script tag either manually or using JavaScript.

Angularjs: Get document.querySelectorAll in ng-include

I'm trying to get the reference of my button in a ng-include! But it doesn't work. (I load my script.js in my index.html).
Code in my script.js:
buttons = Array.prototype.slice.call( document.querySelectorAll( '#st-trigger-effects > button' ))
Code in my ng-include controller:
<div id="st-trigger-effects" class="column">
<button data-effect="st-effect-3" class="pushmenutest">Push</button>
</div>
For your information, when i put my 'div' code directly in the index.html it Works! I don't know why, when i put my code in the ng-include view it doesn't work. Certainly, because the 'DOM' hasn't be loaded ?
I tried inserting script.js at the end of the file. But I have the same result.
I'm trying to use this library : http://tympanus.net/Development/SidebarTransitions/
If that line is by itself and not within say an angular context. It will run before the ng-include is done. Therefore the querySelector won't find the element.
I guesstimate this is the case because you say if you include the in the .html directly it works.
Is there a reason why you need to get a hold of the button like that and can not use ng-click?
If you give us a bit more information we can help you further.

Load HTML within AJAX call

I'm having some difficulty with a Javascript function I am writing. The basic function of the script is that when a specific AJAX function is called and returns successful, it loads some HTML from a file and inserts that HTML into a on the main page and then (once loaded), fades in the parent div.
jQuery.ajax({
type: "POST",
url: "fns/authenticate.php",
data: dataString,
success: function (data) {
if (data=='1') {
jQuery("#authlogin").fadeOut(500, function(){
$(this).remove();
jQuery("#result").load("fns/logic.html", function() {
jQuery('#authtrue').fadeIn(1000);
});
});
} else {
jQuery('#details-error').fadeIn(200);
}
}
});
return false;
Now the AJAX seems to function properly, in that it will execute under the correct conditions and fade out and in the correct divs, the problem seems to be that the content isn't being loaded from logic.html or it is not being bound to the #result div correctly.
The main page's html looks like:
<div id="authlogin">
<!-- HTML form -->
</div>
<div id="authtrue" style="display: none;">
<div id="result"></div>
</div>
Any help would be much appreciated.
This is one of those things that you must troubleshoot yourself, because we do not have access to your fns/logic.html and therefore cannot test fully.
However, some thoughts:
(1) The basic logic of your .load() success function seems correct. Here is a jsFiddle that approximates the AJAX success function's logic. I substituted .html() for .load() because jsFiddle cannot do ajax. Anyway, assuming that .load() is doing what it should, that part should be working.
(2) You may already know this, but note that .load() is shorthand for $.ajax() -- as are .post() and .get(). You might find $.ajax() easier to troubleshoot as the code block is more structured. As a general rule, troubleshooting the shorthand constructions is slightly more abstract/difficult than troubleshooting $.ajax()
(3) Use developer tools in Chrome (press F12 key) to verify that the contents of logic.html have been inserted into the #result div. You might find, as I did in playing with my jsFiddle, that the contents were injected but the #authtrue div remained hidden. At least you will know that the logic.html document has been found and contents inserted. Knowing exactly where the problem is, finding/fixing the rest might now be trivial.
(4) Does your logic.html file include unnecessary header information? If so, you can strip it out by only inserting the BODY of the document, or a top-level containing div. See this section of the jQuery docs:
jQuery("#result").load("fns/logic.html #container", function() {//CALLBACK IN HERE});
(5) It would be a smart idea to create a test document that just and only loads the logic.html document, using various methods:
Method A: Using PHP (or whatever server-side language you use)
<div id="authlogin">
<!-- HTML form -->
<input type="button" id="mybutt" value="Click Me to Start" />
</div>
<div id="authtrue" style="display:none;">
<div id="result"><?php include 'logic.html'; ?></div>
</div>
Method B: Using load()
HTML:
<div id="authlogin">
<!-- HTML form -->
<input type="button" id="mybutt" value="Click Me to Start" />
</div>
<div id="authtrue" style="display:none;">
<div id="result"></div>
</div>
jQuery:
jQuery('#authtrue').show();
jQuery("#result").load("fns/logic.html");
(6) Ensure you do not have a typo in the destination element jquery selector: If no element is matched by the selector — in this case, if the document does not contain an element with id="result" — the Ajax request will not be sent. (from the docs)
I managed to fix this myself, thanks to the help of everyone here. It ended up being a browser caching problem. As soon as I cleared the cache everything magically worked.

Categories