I'd love some input on how how to navigate from view to view within the context of a submitHandler using JQuery Validator. What I'm doing currently is roughly,
class Example.ViewOne extends Backbone.View
template: JST['view_one']
render: ->
#$el.html(template())
#$('form').validate(#.validators)
this
validators:
rules:
name:
required: true
submitHandler: (form) ->
view = new Example.ViewTwo()
view.render()
return false
class Example.ViewTwo extends Backbone.View
template: JST['view_two']
render: ->
#$el.html(#template())
this
class ExampleRouter extends Backbone.Router
routes:
'' : 'index'
index:
view = new Example.ViewOne()
$('#container').html(view.render().el)
Hosting Page:
...
<div id="container"></div>
....
ViewOne:
...
<h1>View One</h1>
<form>
<input type="text" name="name" />
<button type="submit" />
</form>
...
ViewTwo:
...
<h1>View Two</h1>
...
What happens is when I hit submit on ViewOne the post triggers and just re-renders ViewOne. I would like to render ViewTwo and not have the post happen. I must work with JQuery Validator and it requires, to my knowledge, a form tag so getting rid of the form tag can't be a solution for me.
Also, I'm aware that controlling workflow within views is not the ideal way, but I'm focused on justing getting something simple to work for now and definitely plan on investigating more robust solutions as I become more comfortable with Backbone.
Related
I am writing an app in AngularJs using Typescript and I am having trouble accessing a method in a directive from the controller class. I added 'control: '=', to the scope of the directive thinking this would bind the control of the methods in the directive to the controller but I am getting an error
The controller code is:
import {DaypartingSegmentsContainer} from "lib/js/publisher/deal/objects/dayparting/dayparting.segments.container";
#Component({
bindings: {
deal: "="
},
controllerAs: "$ctrl",
module: DealApp,
require: {
DealForm: '^form',
},
selector: 'deal-edit-dayparting',
template: require('./templates/deal.edit.dayparting.html'),
})
#Inject(
"$scope",
"$q"
)
export class DealEditDaypartingCtrl extends SxControllerBase {
public daypartingSegmentsContainer : DaypartingSegmentsContainer;
constructor(protected $scope : ng.IScope,
private $q : angular.IQService) {
super($scope);
}
public $onInit() : void {
let self : DealEditDaypartingCtrl = this;
this.daypartingSegmentsContainer.getCommonTimes();
}
}
The directive class is:
import {Inject, Directive} from "lib/js/angularjs/decorators/sx-forward";
import DealApp from "lib/js/publisher/deal/lib.deal.app";
import DealEditDaypartingCtrl from "lib/js/publisher/deal/edit/deal.edit.dayparting";
import "lib/js/publisher/deal/objects/dayparting/dayparting.jquery.factory";
import {DaypartingDayName} from "lib/js/publisher/deal/objects/dayparting/dayparting.day.name";
import {
minutesToString,
stringToMinutes
} from "lib/js/publisher/deal/objects/dayparting/dayparting.time.functions";
#Directive({
selector: 'dayparting-segments-container',
require: '^dealEditDayparting',
replace: true,
module: DealApp,
scope: {
dayName: '<',
control: '=',
},
template: require('./templates/dayparting.segments.container.html'),
controllerAs: '$ctrl',
link: function(scope : any, element : any, attrs : any, daypartingCtrl : DealEditDaypartingCtrl) : void {
scope.daypartingCtrl = daypartingCtrl;
// Delete button watch
scope.$watch(
() => {
return daypartingCtrl.deleteSegment;
},
(shouldDelete : boolean) => {
if (shouldDelete === true) {
scope.$ctrl.onDelete();
}
}
);
// Calendar leave watch
scope.$watch(
() => {
return daypartingCtrl.calendarLeave;
},
(calendarLeave : boolean) => {
if (calendarLeave === true) {
scope.$ctrl.onCalendarMouseLeave();
}
}
);
},
})
#Inject('$scope', 'JQueryFactory' )
export class DaypartingSegmentsContainer {
constructor(private $scope : any,
private JQueryFactory : any) {
this.tooltipIsOpen = false;
this.jquery = JQueryFactory.get();
}
public getCommonTimes() : void {
console.log("i am in getCommon times!!!")
}
}
export default DaypartingSegmentsContainer;
I keep getting a console error saying - Cannot read property 'getCommonTimes' of undefined
The code was staring at me the full time. I added the below piece of code and its now working:
scope.$watch(
() => {
return daypartingCtrl.daypartingOninit;
},
(daypartingOninit : boolean) => {
if (daypartingOninit === true) {
scope.$ctrl.getCommonTimes();
}
}
);
I required to upload a xml file in to server using the angular resource. i am trying but getting an error as Uncaught TypeError: server.uploadXML.post is not a function
any one correct me or show me the correct way to handle this?
to do this process, I am using controller and directive with my html.
I am very new, please guide me wherever I made mistaken.
here is my controller function : //i am calling from directive
$scope.uploadFile = function ( newFile, id ) {
var fileData = new FormData();
fileData.append('file', newFile[0], newFile[0].name);
// console.log("new file", "$scope.packageId", $scope.packageId, "$scope.contractorId", $scope.contractorId, newFile, info );
server.uploadXML.post({
packageId: $scope.packageId,
contractorId : $scope.contractorId,
contractId : id
}, { save: {
type: 'POST',
data: fileData,
cache: false,
dataType: 'json',
processData: false,
contentType: "application/octet-stream",
success : function ( data ) {
},
error : function ( error ) {
cosole.log( error );
}
}} );
}
Here is my directive:
//file upload handled here
var uploadFileDirective = function () {
return {
restrict : 'C',
scope : {
info:"=",
upload:"="
},
link:function ( scope, element, attrs ) {
element.on('change', function ( event ) {
var files = event.target.files;
scope.upload(files, scope.info ); //calling controller
})
}
}
}
angular.module("tcpApp")
.directive("uploadFileDirective", uploadFileDirective);
Here is my HTML :
<div class="row row3">
<div class="cell">
<a ng-href="">Contract Details</a>
<span class="fileUpload">
Upload Report
<!-- hanlded by directive by class name -->
<input info="contractor.Id" upload="uploadFile" class="uploadField upload-file-directive" type="file" />
</span>
</div>
</div>
</div>
I am using my server.js file to upload my file, here is the server.js file:
(function () {
"use strict";
angular
.module("tcpApp")
.factory("server", ['$resource', function ($resource) {
var base = 'http://azvsptcsdev02:678/_vti_bin/CPMD.WEBSERVICE/ProjectInfoService.svc/';
return {
subPage : $resource( base + 'GetSubProjects/:id'),
splash : $resource( base + 'GetProjectDetails'),
projectSummary : $resource( base + 'GetProjectByID/:id', {id : '#id'}), //at the top on the summary page
contractorsList : $resource( base + 'Contracts/:projectId/:subProjectId/:phaseId/:disciplineId'), //left side list of contractos
contractorInfo : $resource( base + 'Projects/:projectId/:subProjectId/:contractId/:disciplineId/:staticId'), //summary page getting contractor info
allProjectList : $resource( base + 'GetAllProjects'), //getting all project for all project page
allSubProjects : $resource( base + 'GetAllSubProjects/:packageId'),
allContracts : $resource( base + 'AllContracts/:packageId/:contractorId'),
uploadXML : $resource( base + 'UploadContract/:packageId/:contractorId/:contractId')
}
}]);
})();
In your server.js file, when you do
uploadXML : $resource( base + 'UploadContract/:packageId/:contractorId/:contractId')
$resource returns on abject with the following methods available:
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
See the angular documentation.
So server.uploadXML.post is indeed not defined (that's why you get the error). Try using server.uploadXML.save instead which corresponds to the http POST method.
Is it possible to make a set of default events which exist in every view? For example if every view in my application includes a settings button
events: {
"click #settings" : "goSettings"
},
...
goSettings: function() {
// settings show function
});
How can I can package this event to be included in every view in my application?
The problem is that View#extend simply overwrites existing properties so you can't put your 'click #settings' in a base class and subclass that. However, you can easily replace extend with something of your own that merges events. Something like this:
var B = Backbone.View.extend({
events: {
'click #settings': 'goSettings'
}
}, {
extend: function(properties, classProperties) {
properties.events = _({}).extend(
properties.events || { },
this.prototype.events
);
return Backbone.View.extend.call(this, properties, classProperties);
}
});
And then extend B instead of Backbone.View for your views.
Demo: http://jsfiddle.net/ambiguous/Kgh3V/
You can create a base view with the event(s) and functions, then make your other views inherit from it. I like the pattern described here, because it's simple to set up and easy to override as needed: http://www.scottlogic.com/blog/2012/12/14/view-inheritance-in-backbone.html
A base view looks like this:
var BaseSearchView = function(options) {
this.inheritedEvents = [];
Backbone.View.call(this, options);
}
_.extend(BaseView.prototype, Backbone.View.prototype, {
baseEvents: {},
initialize: function(options) {
// generic initialization here
this.addEvents({
"click #settings" : "goSettings"
});
this.initializeInternal(options);
},
render: function() {
// generic render here
this.renderInternal();
return this;
},
events: function() {
var e = _.extend({}, this.baseEvents);
_.each(this.inheritedEvents, function(events) {
e = _.extend(e, events);
});
return e;
},
addEvents: function(eventObj) {
this.inheritedEvents.push(eventObj);
},
goSettings: function() {
// settings show function
}
});
BaseView.extend = Backbone.View.extend;
And your child classes like this:
var MyView = BaseView.extend({
initializeInternal: function(options) {
// do something
// add event just for this child
this.addEvents({
"click #differentSettings" : "goSettings"
});
},
renderInternal: function() {
// do something
}
});
I am looking for a better way to implement a PubSub type functionality with Backbone. I currently am achieving it this, but am looking for a better way to go about it.
Sample Routing Code
var AppRouter = Backbone.Router.extend({
customEvents: _.extend({}, Backbone.Events),
routes: {
"login": "login"
},
//Injecting custom event
login: function(){
this.before(function () {
app.showView('#Content', new LoginView({customEvents:this.customEvents}), "login", true);
});
},
//Injecting custom event
otherView: function(){
this.before(function () {
app.showView('#Header', new OtherView({customEvents:this.customEvents}), "login", true);
});
},
..... more code
});
Sample View code. Notice that I am using bind to listen for customEvent. This works fine, but looking for alternative method
LoginView = Backbone.View.extend({
initialize: function(options){
this.customEvents = options.customEvents;
this.customEvents.bind('app:loggedin', this.loggedIn);
},
loggedIn: function(){
console.log("LOG CHANGED");
},
...more code
I'd much rather keep my events with the rest of the events that I am using in my View. Not sure how to achieve this. Should I be extending the View class?
What I'd like to do on my Views
LoginView = Backbone.View.extend({
events: {
"app:loggin" : "loggedIn"
},
loggedIn: function(){
console.log("LOG CHANGED");
},
...more code
Gist : https://gist.github.com/vermilion1/5600032
Demo : http://jsfiddle.net/vpetrychuk/3NVG9/1
Code preview :
// -- BASE VIEW ------------------
var BaseView = Backbone.View.extend({
constructor : function (options) {
Backbone.View.prototype.constructor.apply(this, arguments);
this._eventAggregator = options && options.eventAggregator || this;
this._parseTriggers();
},
_parseTriggers : function () {
_.each(this.triggers || {}, function (fn, event) {
var handler = this[fn];
if (_.isFunction(handler)) {
this.listenTo(this._eventAggregator, event, handler);
}
},
this);
}
});
// -- TEST ------------------
var View = BaseView.extend({
triggers : {
'hello' : 'helloHandler',
'bye' : 'byeHandler'
},
helloHandler : function (name) {
console.log('hello, ' + name);
},
byeHandler : function (name) {
console.log('bye, ' + name);
}
});
var view1 = new View();
view1.trigger('hello', 'dude 1'); // -> hello, dude 1
view1.trigger('bye', 'dude 1'); // -> bye, dude 1
var vent = _.extend({}, Backbone.Events);
var view2 = new View({eventAggregator:vent});
vent.trigger('hello', 'dude 2'); // -> hello, dude 2
vent.trigger('bye', 'dude 2'); // -> bye, dude 2
I'm currently working with backbone.js and I have a strange problem :
I have a class :
var AppView = Backbone.View.extend({
...
events: {
// Score bar
"mouseover #jauge-bottom": "showToolTip",
"mouseleave #jauge-bottom": "hideToolTip",
// Form events and inputs
"click a.social": "socialAction",
"click a#sound": "switchMute",
"submit form#form-intro": "introForm",
"click .choisir-perso a.btn-jouer": "sexSet",
"mouseover .overlay": "sexHover",
"click #male": "sexClick",
"mouseover #male": "sexHover",
"click #female": "sexClick",
"mouseover #female": "sexHover",
"mouseleave #male": "sexHover",
"mouseleave #female": "sexHover",
"click input.field": "inputFocus",
"blur input.field": "inputFocus"
},
initialize: function() {
this.user.bind('change', this.setScore, this);
_.bindAll(this, 'render', 'setScore');
},
And a subclass :
var Level1 = AppView.extend({
events: _.extend({
}, appMainView.prototype.events), // set the inheritance events
render: function(){
this.bla();
},
bla: function(){
console.log('bla');
}
});
I obtain this error in my console :
this.bla is not a function
My question is why and how can I fix it ?
You're receiving the error because the render function context is not set to the view.
Add an initialize function to bind the render function to the view.
var Level1 = AppView.extend({
initialize: function() {
_.bindAll(this, 'render');
},
events: _.extend({
}, appMainView.prototype.events), // set the inheritance events
render: function(){
this.bla();
},
bla: function(){
console.log('bla');
}
});