Knockout - Join Template with viewmodel - javascript

I am trying to associate the viewmodel with a html page in KnockoutJS.
The viewmodel is defined in an external file. And the html page has a reference to this viewmodel. I am using requirejs to load the file dependencies.
define(['knockout-latest'], function (ko) {
var SimpleListModel = function(items) {
this.items = ko.observableArray(items);
this.itemToAdd = ko.observable("");
this.addItem = function() {
if (this.itemToAdd() != "") {
this.items.push(this.itemToAdd()); // Adds the item. Writing to the "items" observableArray causes any associated UI to update.
this.itemToAdd(""); // Clears the text box, because it's bound to the "itemToAdd" observable
}
}.bind(this); // Ensure that "this" is always this view model
};
ko.applyBindings(new SimpleListModel(["Alpha", "Beta", "Gamma"]));
//return SimpleListModel;
});
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="styles.css" />
<script type="text/javascript" src="knockout-latest.js"></script>
<script type="text/javascript" data-main="viewmodel" src="require.js"></script>
<script type="text/javascript" src="jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="viewmodel.js"></script>
</head>
<body>
<form data-bind="submit: addItem">
New item:
<input data-bind='value: itemToAdd, valueUpdate: "afterkeydown"' />
<button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
<p>Your items:</p>
<select multiple="multiple" width="50" data-bind="options: items"> </select>
</form>
</body>
</html>
I am getting the following error:
require.js:5 Uncaught Error: Mismatched anonymous define() module
I would like to know how can I join the html pages with its viewmodel in a separate file.

Looking at the code you posted, you have the links to the script files just listed on the page. With RequireJS, the list of script files should be removed, and only RequireJS and the script used for the application entry point should be included in the page. The application entry point can either be a <script> tag or as the data-main attribute in the RequireJS <script> tag.
I copied your code over to this example in plunker. I also moved the view model into a knockout component and loaded the HTML using the RequireJS text plugin. In main.js, there is a require.config() call that sets the paths for the text and knockout modules that point to a CDN. The paths could point to local files also, but note that the <script> tags for knockout and text are not in the index.html file. RequireJS will dynamically add them as it determines the dependencies based on the define() calls.

Related

Adding app.js to angular application

I'm triying to add bootstrap ui modules to an angular project. In every tutorial I read that after download the module i'm supposed to add a directive (angular.module(for example: 'App', ['ui.carousel']) to certain file that in some forums i've read is called app.js. This file doesnt exist in my project. The question is: Should I create it? Where? Is a controller necesary? How is the flow that this must follow? Then I should add it to the index.html?
Getting advanced here with components without even knowing the basics. I recommend starting with just controllers (without components). But, here is a basic example of how scripts are added:
<!DOCTYPE html>
<html ng-app="myApp">
<!-- Adding scripts here. The order matters: angular.js first, then its dependancies, e.g. ui-bootstrap -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.5.0/ui-bootstrap.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<body>
<div>
<!-- main body -->
<div>
<hello-world name="World"> </hello-world>
</div>
</div>
<!-- ng-template simulates a separate file -->
<script type="text/ng-template" id="my_component.html">
<input class="form-control" ng-model='$ctrl.name'><br><span>Hello {{$ctrl.name}}, I'm {{$ctrl.myName}}!</span>
</script>
<!-- This script can be also written as:
<script src="/app.js"></script>
if the file `app.js` is located in root directory of the server,
which should contain the AngularJS code below
-->
<script>
var app = angular.module('myApp', ['ui.bootstrap']); // injected dependancies
app.component("helloWorld", {
templateUrl: "my_component.html",
/* component template */
bindings: {
name: '#'
},
controller: function() {
this.myName = 'Karolyn'; // use of `this` requires `$ctrl` in the template HTML
}
});
</script>
</body>
</html>

Handlebars - You must pass a string or Handlebars AST to Handlebars.compile not working

Good Day,
I am trying to create a handlebars demo using an external precompiled file. Here is my index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Handlebars Demo</title>
</head>
<body>
<h2>Hello Handlebars!</h2>
<!--Your new content will be displayed in here-->
<div class="content-placeholder"></div>
<script src="components/jquery/dist/jquery.min.js"></script>
<script src="components/handlebars/handlebars.min.js"></script>
<script src="js/first.js"></script>
<script src="js/finished/first.tpl.js"></script>
</body>
</html>
Here's my javascript:
$(function () {
// Grab the template script
var theTemplateScript = $("#address-template").html();
// Compile the template
var theTemplate = Handlebars.compile(theTemplateScript);
// Define our data object
var context={
"city": "London",
"street": "Baker Street",
"number": "221B"
};
// Pass our data to the template
var theCompiledHtml = theTemplate(context);
// Add the compiled html to the page
$('.content-placeholder').html(theCompiledHtml);
});
Here's my first.hbs
<!--This is our template. -->
<!--Data will be inserted in its according place, replacing the brackets.-->
<script id="address-template" type="text/x-handlebars-template">
<p>You can find me in {{city}}. My address is {{number}} {{street}}.</p>
</script>
I compiled first.hbs this way:
handlebars templates/first.jbs -f finished/first.tpl.js
and my file structure (where my javascript libraries reside in components):
+ HandlebarsDemo
+ components
+ js
+ finished
+ first.tpl.js
+ templates
+ first.jbs
first.js
index.html
I looked at the following: Handlebars Pass a string or Handlebars AST to Handlebars compile
I understand that I need to have the Handlebars.compile() before theTemplateScript is loaded into the DOM, so I moved my javascript code to the bottom and am still getting this error.
Does anyone have any suggestions?
TIA,
coson

HTML with knockout SPA attempt with components, bindings not working

I'm trying to make an SPA website with knockout and requirejs from websites I've seen and tutorials, in order to split up the website so it isn't a single gigantic thing. At one point I'm expecting to see:
My first name is: Bryan
But instead I'm getting:
My first name is: function c(){if(0<arguments.length)return c.tb(c[E],arguments[0])&&(c.ga(),c[E]=arguments[0],c.fa()),this;a.l.oc(c);return c[E]}
Starting with my index.thml:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>asdf</title>
</head>
<body>
<mainview></mainview>
<!-- global imports -->
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.22/require.min.js"></script>
<script type='text/javascript' src="./index.js"></script>
</body>
</html>
My index.js:
"use strict";
requirejs.config({
baseUrl: '',
paths: {
knockout: 'https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min',
text: 'https://cdnjs.cloudflare.com/ajax/libs/require-text/2.0.12/text.min'
}
});
ko.components.register(
'mainview',
{
require: './indexViewModel'
}
);
ko.applyBindings();
indexViewModel.js:
"use strict";
define(['knockout', 'text!./indexViewModel.html'], function(ko, htmlString) {
function indexViewModel(params)
{
var self = this;
self.firstName = ko.observable('Bryan');
}
return { viewModel: indexViewModel, template: htmlString };
});
Finally my indexViewModel.html:
<div>
<p>input name: <input data-bind="value: firstName"></input></p>
<p>My first name is: <span data-bind='text: firstName'></span></p>
</div>
All this gives the result I stated above.
Now if I change firstname to firstName(), then it initially comes up right, but if I change the input, nothing happens.
ok, with more digging and googling, I happened upon the solution. I don't get the details, but it's requirejs and knockoutjs are colliding some how
Because on my index.html, I import knockout and I have knockout setup as a requirejs parameter from the config.
I found this
Issue loading knockout components view model using requireJS
that clued me in.
ok so I made these changes:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Battlestations Character Generator</title>
</head>
<body>
<mainview></mainview>
<!-- global imports -->
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.22/require.min.js"></script>
<!-- no more knockout reference here -->
<script type='text/javascript' src="./index.js"></script>
</body>
</html>
now my index.js:
"use strict";
requirejs.config({
baseUrl: '',
paths: {
knockout: 'https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min',
text: 'https://cdnjs.cloudflare.com/ajax/libs/require-text/2.0.12/text.min'
}
});
// "app main()"
requirejs(['knockout'], function(ko) {
var self = this;
ko.components.register(
'mainview',
{
require: './indexViewModel'
}
);
ko.applyBindings();
});
and changed <input></input> to <input />, although that changed nothing, but if that's "good practice", I'll go with it.
after those changes, reload, all works, and changing the input changes the <span> right after it.
yay!
I know an answer is accepted, Here I am posting an answer just for the users who have tried the answer and failed. I was also experiencing this issue in my MVC application. The problem was, I was referring the knockout-3.4.0.js file both in _Layout.cshtml and the my page. When I remove the JS reference from _Layout.cshtml and reference the same to only to the page, it was all fine. Cheers!

Properly use custom angularjs service, function not defined error

I created my own service for some util methods. The idea was to simply inject the utilsservice into the modules where I need the methods. The Problem is I get an ReferrenceError: myFunction is not defined.
I think it has to do with the wrong injecting of the service, but i can't figute out myself what's wrong with my approach.
The service i made:
angular.module('utils',[]).service('UtilsService',function(){
this.myFunction = function(){};
});
In my app.js file i have following structure:
(function(){
angular.module('utils',[]);
angular.module('translation',[]);
var app = angular.module('myApp',['translation','utils']);
app.controller('myController',['$http',function($http,UtilsService){
UtilsService.myFunction();
}]);
});
The order I included the scripts in my .html file:
<script type="text/javascript" src="../Libraries/angular.min.js"></script>
<script type="text/javascript" src="../js/angular-js/services/utilService.js"></script>
<script type="text/javascript" src="../js/angular-js/app.js"></script>
<script type="text/javascript" src="../js/angular-js/translate.js"></script>
I already tried to change the order but it doesn't make any difference.
I am thankfull for any advice you may have!
Please try the below. You will need to change the script references to point to the files where you have them. Once the index.html file has loaded, you should see the output "you called myFunction()" in the console window. That is being printed from within the service which shows it's being called correctly. I've also created a fiddle
index.html:
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>Directives</title>
</head>
<body>
<div ng-controller="myController"></div>
<script src="angular.js"></script>
<script src="app.js"></script>
<script src="utilsService.js"></script>
</body>
</html>
app.js (I have moved the code out of the function you created since it wasn't working. Also, you had a typo for spelling anguar on the line that begins with var app. I have also removed the dependency for translation in my code since I didn't create any module by that name):
(function(){
//angular.module('utils',[]);
//angular.module('translation',[]);
});
var app = angular.module('myApp',['utils']);
app.controller('myController',['$scope', 'UtilsService',function($scope,UtilsService){
UtilsService.myFunction();
}]);
utilsService.js:
angular.module('utils',[])
.service('UtilsService',function(){
this.myFunction = function(){ console.log ('you called myFunction()')};
});

Unobtrusive JavaScript - Separate JS File Not Found

Using the concept unobtrusive JavaScript, I'm trying, for the first time, to place my JavaScript in a separate file from the HTML. But, no matter what I do, I get an error that the file wasn't found.
This is the actual error in the google chrome console (ctrl-shift-j):
GET http://localhost:14385/Home/TestPage.js 404 (Not Found)
I started with a new MVC 4 app. I created a new test page:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<!DOCTYPE html>
<html>
<head runat="server">
<meta name="viewport" content="width=device-width" />
<script src="TestPage.js"></script>
<title></title>
</head>
<body>
<div>
<input id="foo" value="snuh" />
</div>
</body>
</html>
And I created a new TestPage.js in the same folder:
$(document).ready(function() {
function dosomething() {
alert('snuh');
}
document.getElementById('foo').onclick = dosomething;
});
I've tried the tips posted here, but I always get an error that the JavaScript file isn't found. Any idea what's wrong with this simple test?
Note: The TestPage actually displays, with the input box showing.
This is the layout in solution explorer:
Make sure you reference your javascript from the correct location using server side helpers:
<script src="<%= Url.Content("~/TestPage.js") %>"></script>
This will properly reference the javascript file no matter from which location you have rendered this view. It assumes obviously that you have placed your javascript file in the root of your application. The The convention is to use the Scripts folder for this:
<script src="<%= Url.Content("~/Scripts/TestPage.js") %>"></script>
UPDATE:
Now that you have shown your project structure you seem to have placed the TestPage.js file inside the ~/Views folder. This won't work. This folder is not accessible from the client side. It is explicitly forbidden and not served by IIS. You should never place any static files inside. Move your javascript folder to the ~/Scripts folder.
Also you seem to be using jQuery inside your TestPage.js file but you never referenced it so your script won't work. If you want to use jQuery make sure that you have added it as well:
<script src="<%= Url.Content("~/Scripts/jquery-1.8.2.js") %>"></script>
<script src="<%= Url.Content("~/Scripts/TestPage.js") %>"></script>
or if you don't want to use jQuery fix your script so that it doesn't depend on it:
function dosomething() {
alert('snuh');
}
window.onload = function() {
document.getElementById('foo').onclick = dosomething;
};
or if you place your scripts at the end of your DOM you don't even need to wrap them in a document ready handler because at this stage the DOM will be ready and you can manipulate it:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<!DOCTYPE html>
<html>
<head runat="server">
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
<input id="foo" value="snuh" />
</div>
<script src="<%= Url.Content("~/Scripts/TestPage.js") %>"></script>
</body>
</html>
and then inside your script:
function dosomething() {
alert('snuh');
}
document.getElementById('foo').onclick = dosomething;

Categories