<head>
<title></title>
<script src="javascript/vendor/jquery-1.6.4.min.js" type="text/javascript"></script>
<script src="javascript/vendor/underscore.js" type="text/javascript"></script>
<script src="javascript/vendor/backbone.js" type="text/javascript"></script>
</head>
<body>
<script type="text/javascript" >
var MyApp = (function(_, Backbone){
var myApp = {};
var initializers = [];
myApp.addInitializer = function(callback){
var initializer = {
obj: this,
callback: callback
}
initializers.push(initializer);
};
myApp.initialize= function(){
_.each(initializers, function(initializer){
initializer.callback.call(initializer.obj);
});
};
// the event aggregator
myApp.vent = _.extend({}, Backbone.Events);
// the other app initialization code ...
return myApp;
})(_, Backbone);
var MyModule = (function(MyApp, Backbone){
var MyView = Backbone.View.extend({
initialize: function(){
MyApp.bind("some:event", this.someCallback, this);
},
someCallback: function(){
alert("I'm Doing Stuff!!!");
}
});
// ... other code, including MyApp.addInitializer
})(MyApp, Backbone);
var AnotherModule = (function (MyApp, Backbone) {
var anotherModule = {};
anotherModule.SomeFunction = function () {
MyApp.trigger("some:event");
//alert("Hello");
};
return anotherModule;
})(MyApp, Backbone);
// kick it all off and show the alert box
//$(function(){
// MyApp.initialize();
// AnotherModule.SomeFunction();
//});​
$(function () {
MyApp.initialize();
AnotherModule.SomeFunction();
});
</script>
</body>
I am getting error on this line MyApp.trigger("some:event"); . I copied the code from following link
URL: http://lostechies.com/derickbailey/2011/11/17/introduction-to-composite-javascript-apps/
Can you help me using modules two or more and each of them have multiple views. I need to communicate them using backbone as the above URL did.
Thanks.
I tried to solve this in different ways but kept ending up with the following solution. The solution involves writing my code in modules and using marionette modules and vent to communicate with them. Marionette helped me a lot and I hope the different features further in my development.
index.html
<script type="text/javascript">
$(function () {
//var myModule = MyApp.module("MyModule");
// console.log(MyApp.MyModule.someData); //=> public data
MyApp.OrganizerModule.someFunction(); //=> public data
//var LayOutModel = new MyApp.ShellModule.LayOut();
var LayoutView = new MyApp.ShellModule.LayoutView();
LayoutView.render();
var Explorer = new MyApp.OrganizerModule.ExplorerView();
});
</script>
The following templates are used:
<script id="layout_temp" type="text/template">
<div id="layout">
<div id="header">
header
</div>
<div id="container">
<div id="center" class="column">
center
</div>
<div id="left" class="column">
left
</div>
<div id="right" class="column">
right
</div>
</div>
<div id="footer">
footer
</div>
</div>
</script>
<script id="Explorer" type="text/template">
<div > start :
<button>Say hello</button>
</div>
</script>
Here is the Module definition and the subscription of the event using Marionette
MyApp.module("ShellModule", function (ShellModule, MyApp, Backbone, Marionette, $, _) {
ShellModule.LayoutView = Backbone.View.extend({
initialize: function () {
//Backbone.ModelBinding.call(this);
alert("Hello2");
MyApp.vent.on("toolClick_Event", function (cat) {
alert("test toolClick_Event fired");
});
}
// , events: {
// 'toolClick_Event': 'toolClick_Event'
// }
, render: function () {
var template = _.template($("#layout_temp").html(), {});
$("#Maincontainer").html(template);
//$(this.el).append("<ul> <li>hello world</li> </ul>");
}
});
});
And the other Module that triggers the event using MyApp.vent.trigger.
MyApp.module("OrganizerModule", function (OrganizerModule, MyApp, Backbone, Marionette, $, _) {
// Private Data And Functions
// --------------------------
var myData = "this is private data";
var myFunction = function () {
console.log(myData);
}
// Public Data And Functions
// -------------------------
OrganizerModule.someData = "public data";
OrganizerModule.someFunction = function () {
//console.log(MyModule.someData);
alert(OrganizerModule.someData);
}
OrganizerModule.ExplorerView = Backbone.View.extend({
el: "#center",
events: {
'click button': 'toolClick'
}
, initialize: function () {
this.render();
this.setElement(this.el);
}
, render: function () {
var template = _.template($("#Explorer").html(), {});
//this.$el.html.add(template);
// $("#center").append(template);
//return this.el;
$(this.el).html(template);
return this;
}
, toolClick: function () {
alert("test toolClick");
// var template = _.template($("#Explorer").html(), {});
//$("#center").append(template);
MyApp.vent.trigger('toolClick_Event');
$("#Maincontainer").append("<div> other data </div>");
}
});
});
Hope this will be helpful to others.
Related
It looks like this questions already asked few times but there is no correct answer to it.
my case: i'm loading a template (with html and script) init to div using ajax (not using angular routing temple for some reason).
index.html (main)
<!DOCTYPE html>
<html ng-app="app" ng-controller="AppCtrl">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Web</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
</head>
<body>
<div class="container body-content">
<div class="dynamic-content" >
<!-- Loading content Here -->
</div>
<button ng-click="loadTemplate()">Load Template</button>
</div>
<script>
angular.module('app', [])
.controller('AppCtrl', function ($scope) {
$scope.someData = {};
$scope.loadTemplate = function() {
....
//AJAX to get the templet.html
//and load it into .dynamic-content
//then applying scope();
}
});
</script>
</body>
</html>
template.html (template)
<div ng-controller="TempCtrl">
<h2>About</h2>
<h3>{{total}}</h3>
<p>Testing the total</p>
<button ng-click="update()">Update</button>
</div>
<script>
console.log('begin')
angular.module('app')
.controller('TempCtrl', function ($scope) {
$scope.total = 0;
console.log('inside')
$scope.update = function () {
$scope.total += 1;
};
});
console.log('end')
</script>
when i click the button Load Template it loads the template.html file in to container but i'm getting error
Error: [ng:areq] Argument 'TempCtrl' is not a function, got undefined
though its being added to app controllers.
How can i add controllers dynamically and get it work with dynamic html nodes
DEMO HERE https://plnkr.co/edit/EAa9Md36hDzpQ1BgIQKg?p=preview
This blog describes how to fiddle with angular to force it to load additional controllers after it has been bootstrapped:
http://www.bennadel.com/blog/2553-loading-angularjs-components-after-your-application-has-been-bootstrapped.htm
Of course, this is completely unsupported, and could be broken by changes to angular at any time.
But, here is an updated version of your code using this method:
var app = angular.module('app', [])
app.config(
function($controllerProvider, $provide, $compileProvider) {
// Since the "shorthand" methods for component
// definitions are no longer valid, we can just
// override them to use the providers for post-
// bootstrap loading.
console.log("Config method executed.");
// Let's keep the older references.
app._controller = app.controller;
app._service = app.service;
app._factory = app.factory;
app._value = app.value;
app._directive = app.directive;
app.controller = function(name, constructor) {
console.log("controller...");
console.log(name);
console.dir(constructor);
$controllerProvider.register(name, constructor);
return (this);
};
// Provider-based service.
app.service = function(name, constructor) {
$provide.service(name, constructor);
return (this);
};
// Provider-based factory.
app.factory = function(name, factory) {
$provide.factory(name, factory);
return (this);
};
// Provider-based value.
app.value = function(name, value) {
$provide.value(name, value);
return (this);
};
// Provider-based directive.
app.directive = function(name, factory) {
$compileProvider.directive(name, factory);
return (this);
};
});
app.controller('AppCtrl', function($scope, $http, $compile) {
$scope.someData = {};
$scope.loadTemplate = function() {
$http.get("template.html")
.then(function(r) {
// load in the html, including the script, which will be executed
$(".dynamic-content").html(
r.data
);
// compile the loaded html into an actual template and put it back where it was
$(".dynamic-content").html($compile($(".dynamic-content").html())($scope));
})
}
});
Note that I used jQuery to place the HTML into the DOM, causing the script to execute, then to grab the HTML back out of the DOM so it could be compiled, after which I placed it back in the DOM again.
Also, there is an undefined variable in your template.html, so I updated it to look like this:
<script>
console.log('begin')
angular.module('app')
.controller('TempCtrl', function ($scope) {
$scope.total = 0;
console.log('inside')
$scope.update = function () {
$scope.total += 1;
};
});
console.log('end')
</script>
<div ng-controller="TempCtrl">
<h2>About</h2>
<h3>{{total}}</h3>
<p>Testing the total</p>
<button ng-click="update()">Update</button>
</div>
Here is a working plunk: http://plnkr.co/edit/cB5N05
Updated Dave's example with angular components support
app.config(
function ($controllerProvider, $provide, $compileProvider) {
var app = angular.module('app');
// Let's keep the older references.
app._controller = app.controller;
app._service = app.service;
app._factory = app.factory;
app._value = app.value;
app._directive = app.directive;
app._component = app.component;
// Provider-based controller.
app.controller = function (name, constructor) {
$controllerProvider.register(name, constructor);
return ( this );
};
// Provider-based service.
app.service = function (name, constructor) {
$provide.service(name, constructor);
return ( this );
};
// Provider-based factory.
app.factory = function (name, factory) {
$provide.factory(name, factory);
return ( this );
};
// Provider-based value.
app.value = function (name, value) {
$provide.value(name, value);
return ( this );
};
// Provider-based directive.
app.directive = function (name, factory) {
$compileProvider.directive(name, factory);
return ( this );
};
// Provider-based component.
app.component = function (name, options) {
$compileProvider.component(name, options);
return ( this );
};
});
Try loading the templates controller in the index.html, instead of in the template.html, that way it is already existing when the markup is looking for it.
In index.html script portion:
angular.modules('app', [])
.controller('AppCtrl', function ($scope) {
$scope.someData = {};
$scope.loadTemplate = function() {
....
//AJAX to get the templet.html
//and load it into .dynamic-content
//then applying scope();
}
}).controller('TempCtrl', function ($scope) {
$scope.total = 0;
console.log('inside')
$scope.update = function () {
total += total;
};
});
EDIT:
This is when having multiple JS files that reference the app module would be handy, that way each file is owning a controller.
Is it possible to write a function like this?
$scope.myFunc = function () {
this.details = 1;
this.setDetails = function (d) {
this.details = d;
}
}
How can I call this function from view?
set Details
It is not even calling my function.
I prepare working example for you. First use constructor function and then create new instance $scope.myfunc by calling new MyFunc().
var myApp = angular.module('myApp', []);
myApp.controller('myController', function($scope) {
//Constructor function
function MyFunc () {
this.details = 1;
this.setDetails = function(details) {
this.details = details;
}
}
//New instance of MyFunc
$scope.myfunc = new MyFunc();
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myController">
<input type="text" ng-model="myfunc.details">
<button ng-click="myfunc.setDetails('hi')">Click me</button>
</div>
</div>
I new to Handelbars.js and I wanted to test it out. I just have one html file, whose content I want to be rendered by handlebars.js:
HTML file:
<html>
<head>
<script type="text/javascript">
console.log("Head:getting css");
</script>
<link rel="stylesheet" type="text/css" href="css/main_2.css">
</head>
<body>
<div class="phone-screen">
<script type="text/javascript">
console.log("Body:start MasterPage");
</script>
<!--MasterPage -->
<script id="app-bar" type="text/x-handlebars-template">
<div class="app-bar-container">
</div>
<div class="nav-bar-container">
<div class="nav-tab-left nav-tab-selected nav-tab-label-Selected">
<div class="tab-lable-centered ">History</div>
</div>
<div class="nav-tab-right nav-tab-notSelected ">
<div class="tab-lable-centered ">New</div>
</div>
</div>
</script
<!-- Content -->
<script id="workout-list-tpl" type="text/x-handlebars-template">
<div class="list-container">
<div class="week-seperator">
<div class="week-lable-right ">Week 12</div>
</div>
<div class="workout-card-white">
<div class="workout-card-label ">Workout</div>
</div>
</script>
<script type="text/javascript">
console.log("Body:getting Scripts________________________");
</script>
<script src="lib/handlebars.js"></script>
<script src="js/storage/memory-store.js"></script>
<script src="lib/jquery-1.8.2.min.js"></script>
<script src="js/MainView.js"></script>
<!--<script src="js/EmployeeView.js"></script>-->
<script src="js/main_2.js"></script>
</div>
</body>
The app javascript:
var app = {
initialize: function() {
console.log("initializer start: ");
var self = this;
//this.detailsURL = /^#employees\/(\d{1,})/;
//this.registerEvents();
this.store = new MemoryStore(function() {
self.route();
});
},
route: function() {
console.log("route function start.");
var self = this;
var hash = window.location.hash;
if (!hash) {
if (this.homePage) {
//this.slidePage(this.homePage);
} else {
console.log("homePage: ");
this.homePage = new MainView(this.store).render();
console.log("homePage = " + this.homePage );
//this.slidePage(this.homePage);
}
return;
}
var match = hash.match(this.detailsURL);
if (match) {
//this.store.findById(Number(match[1]), function(employee) {
//self.slidePage(new EmployeeView(employee).render());
//});
}
},
showAlert: function (message, title) {
if (navigator.notification) {
navigator.notification.alert(message, null, title, 'OK');
} else {
alert(title ? (title + ": " + message) : message);
}
},
};
console.log("app.initialize();");
app.initialize();
And the view.js:
var MainView = function(store) {
this.render = function() {
console.log("render function execution:" + MainView.template() );
this.el.html(MainView.template());
return this;
};
this.initialize = function() {
console.log("MainView init start.");
// Define a div wrapper for the view. The div wrapper is used to attach events.
this.el = $('<div/>');
//this.el.on('keyup', '.search-key', this.findByName);
};
this.initialize();
}
console.log("Handlebars.compile($(app-bar).html()); " );
MainView.template = Handlebars.compile($("#app-bar").html());
console log:
Head:getting css main_2.html:4
Body:start MasterPage main_2.html:12
Body:getting Scripts________________________ main_2.html:41
Handlebars.compile($(app-bar).html()); MainView.js:19
app.initialize(); main_2.js:46
initializer start: main_2.js:5
route function start. main_2.js:15
homePage: main_2.js:22
MainView init start. MainView.js:10
render function execution:
<div class="app-bar-container">
</div>
<div class="nav-bar-container">
<div class="nav-tab-left nav-tab-selected nav-tab-label-Selected">
<div class="tab-lable-centered ">History</div>
</div>
<div class="nav-tab-right nav-tab-notSelected ">
<div class="tab-lable-centered ">New</div>
</div>
</div>
MainView.js:4
homePage = [object Object]
The code goes through to the render function in view.js getting the html code but I do not know what happens next and hence why it is not rendered in the browser.
It seems I forgot the last step to append the generated html string to the html page:
$('body').append(page.el);
I have a SPA using knockout JS for data binding and sammy for routing. I have a deck of cards that I am trying to have a dynamic routing to. My problem is that it doesn't work when I try to set a knockout observable from the routing function in sammy.
My HTML, where I try to bind the name of the deck, looks like this:
<!-- Create Deck -->
<div id="createDeck" class="page" style="display:none;">
<input type="text" class="form-control" placeholder="Untitled Deck..." data-bind="value: $root.deck.name" />
</div>
<script type="text/javascript" src="lib/jquery-1.9.1.js"></script>
<script type="text/javascript" src="lib/knockout-2.3.0.js"></script>
<script type="text/javascript" src="lib/bootstrap.min.js"></script>
<script type="text/javascript" src="lib/sammy.js"></script>
<script type="text/javascript" src="js/Models/Deck.js"></script>
<script type="text/javascript" src="js/Models/Card.js"></script>
<script type="text/javascript" src="js/ViewModels/DeckViewModel.js"></script>
<script type="text/javascript" src="js/ViewModels/CardViewModel.js"></script>
<script type="text/javascript" src="js/routing.js"></script>
The Deck.js and DeckViewModel.js looks like below
function Deck(deckid, name, cards) {
var self = this;
self.id = deckid;
self.name = name;
self.cards = cards;
}
function DeckViewModel(deck, cards) {
var self = this;
self.deck = ko.observable(deck);
self.cards = ko.observableArray(cards);
self.goToCard = function (card) { location.hash = card.deckid + '/' + card.id };
}
// Bind
var element = $('#createDeck')[0];
var deckView = new DeckViewModel(null, null);
ko.applyBindings(deckView, element);
Finally, in my routing I try to create a new Deck, like this:
// Client-side routes
(function ($) {
var app = $.sammy('#content', function () {
this.get('#deck/:id', function (context) {
showPage("createDeck", ": Create Deck");
console.log(this.params.id);
deckView.deck = new Deck(1, "test", null);
console.log(deckView.deck);
});
});
$(function () {
app.run('#/');
});
})(jQuery);
function showPage(pageID, subHeader) {
// Hide all pages
$(".page").hide();
// Show the given page
$("#" + pageID).show();
// change the sub header
$("#subHeader").text(subHeader);
}
As you can see, I'm trying to create a test deck with the name 'test', but the binding <input type="text" class="form-control" placeholder="Untitled Deck..." data-bind="value: $root.deck.name" /> seems to bind the letter 'c'.
I'm at a loss, please help.
I tried to make a jsfiddle to demonstrate my problem
In your code the value assignment is not correct unless you are using Knockout-es5 plugin. here is the correct code
var app = $.sammy('#content', function () {
this.get('#deck/:id', function (context) {
showPage("createDeck", ": Create Deck");
console.log(this.params.id);
deckView.deck(new Deck(1, "test", null));
console.log(deckView.deck());
});
});
The way I've done this before is to define my Sammy() routes within the ViewModel. Shorter example for brevity:
(function($) {
function ViewModel() {
var self = this;
self.deckId = ko.observable(null);
Sammy(function() {
this.get('#/deck/:deckId', function(context) {
self.deckId(this.params.deckId);
});
});
}
$(function() {
ko.applyBindings(new ViewModel());
});
})(jQuery);
That way you can access your observables, etc, via self.
I'm using Backbone.js 0.9.10, 1.4.4 Underscore.js.
When I call my html page there is no javascript error and the console shows that the collection was iterated.
However, the browser does not show my html I get the blank page. I log the result of html on console and appears without replacing variables of model ​​by json, like that:
(I retired the signals (< >) in the tags to show that the html template that appears on console.)
my html come from view =
p
a href="#" id="remove-button" >Remover este Post/a
/p
h2 /h2
p /p>
//HTML
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Backbone COLLECTION</title>
<script src="../js/jquery/jquery-1.8.3.js" type="text/javascript" charset="utf-8">
</script>
<script src="../js/backbone/underscore.js" type="text/javascript" charset="utf-8">
</script>
<script src="../js/backbone/backbone.js" type="text/javascript" charset="utf-8">
</script>
<script src="../js/mvc/PostModel.js"></script>
<script src="../js/mvc/PostView.js"></script>
<script src="../js/mvc/PostCollectionView.js"></script>
<script src="../js/mvc/PostCollection.js"></script>
<script>
$(document).ready(function() {
var postList = new PostList();
var postCollectionView = new PostCollectionView({collection:postList});
postList.fetch();
});
</script>
</head>
<body>
<script type="text/template" id="post-template">
<p>
Remover este Post
</p>
<h2><%=title%></h2>
<p><%=text%></p>
</script>
</body>
</html>
//VIEW
var PostView = Backbone.View.extend({
events: {
"click #remove-button": "removePost"
},
initialize: function() {
_.bindAll(this, 'render', 'removePost', 'refresh');
this.template = _.template($('#post-template').html());
this.model = new PostModel();
this.model.on("change", this.render, this);
this.model.on("destroy", this.refresh);
},
render: function() {
console.log('PostView - render ');//ok
var template = this.$el.html(this.template(this.model.toJSON()));
return this;
},
removePost: function() {
this.model.destroy();
},
refresh: function() {
this.model.clear({silent: true});
this.model.fetch();
}
});
//MODEL
var PostModel = Backbone.Model.extend({
defaults : function(){
return {
//some
title: [],
text: []
}
}
});
//COLLECTION
var PostCollectionView = Backbone.View.extend({
initialize: function(){
this.collection.on('add', this.addOne, this);
this.collection.on('reset', this.addAll, this);
},
addOne: function(modelItem){
var postView = new PostView({model: modelItem});
console.log('my html come from view = ' + postView.render().$el.html());
this.$el.append(postView.render().$el.html());
},
addAll: function(){
this.collection.forEach(this.addOne, this);
},
render: function(){
this.addAll();
}
});
//Collection with URL
var PostList = Backbone.Collection.extend({
url: 'http://www.mysystem.com/newproject/project/cadTarefas/recuperarListaTeste',
model: PostModel
});
//My JSON
[{"title":"titulo_1","text":"texto_1"},{"title":"titulo_2","text":"texto_2"},
{"title":"titulo_3","text":"texto_3"},{"title":"titulo_4","text":"texto_4"},
{"title":"titulo_5","text":"texto_5"},{"title":"titulo_6","text":"texto_6"},
{"title":"titulo_7","text":"texto_7"},{"title":"titulo_8","text":"texto_8"},
{"title":"titulo_9","text":"texto_9"},{"title":"titulo_10","text":"texto_10"}]
The one thing I don't see in any of your code is where PostCollectionView's element is attached to the DOM. It should either have a static declaration, i.e.
var PostCollectionView = Backbone.View.extend({
el: $('#somediv'),
Or an external one,
$(document).ready(function() {
var postList = new PostList();
var postCollectionView = new PostCollectionView({collection:postList, el: $('#somediv')});
Or your controller is responsible for attaching the dynamically allocated one to the DOM after the View has been initialized. One of those has to be true.
Secondly, in PostView, after you pass in an existing model, you destroy it with the line this.model = new PostModel();, which overwrites the object-local reference passed in with a new, blank model, which is why you're not seeing anything.
Try change string in render view
var template = this.$el.html(this.template(this.model.toJSON()));
to string
this.$el.html(this.template(this.model.toJSON()));