Using params on template helpers from Iron Router in Meteor - javascript

I'm using a router with this specific route:
Router.route('/organizations/:id', function () {
this.render('organizationDetail', {id: this.params.id});
});
This renders 'organizationDetail' template.
I have this helpers for that template:
if (Meteor.isClient) {
Template.organizationDetail.helpers({
organization: function() {
this.params.id????
return FindById(id);
}
});
}
How can I access id variable to ask for the correct object?

You need to put that parameter into your data context:
Router.route('/organizations/:id', function () {
this.render('organizationDetail', {
data: function() {
return {id: this.params.id};
}
});
});
Then you can use it straight from the this object, which holds the data context in helpers:
if (Meteor.isClient) {
Template.organizationDetail.helpers({
organization: function() {
return FindById(this.id);
}
});
}

Related

Uncaught Type Error: View is not a constructor

I have Uncaught Type Error : UserRegisterView is not a constructor.I dont understand this error.I looked all code but i dont find it.
Sorry of my bad english.Please help me
Thanks for answer
UPDATED
UserRegisterView is here
var UserRegisterView = Backbone.View.extend({
model: User,
el: '#form',
events: {
'click input[id="infoWeek"]': 'infoWeek',
'click input[id="infoMonth"]': 'infoMonth'
},
infoWeek: function() {
this.$el.find("#dayOfMonth").hide();
this.render();
},
infoMonth: function() {
this.$el.find("#dayOfWeek").hide();
this.render();
}
});
var AddUserView = Backbone.View.extend({
el: $(".page"),
events: {
'click #saveUser': 'saveUser'
},
saveUser: function() {
var user = new User();
user.set({
username: $("#username").val(),
lastName: $("#lastName").val(),
regNumber: $("#regNumber").val(),
password: $("#password").val(),
departmentName: $("#departmentName").val(),
email: $("#email").val(),
role: $("#role").val()
});
user.save();
if (document.getElementById('isOpen').checked) {
user.set("isOpen", $("#isOpen").val("1"));
user.save();
} else {
user.set("isOpen", $("#isOpen").val("0"));
user.save();
}
if (document.getElementById('dayOfWeek').checked) {
user.set("dayOfWeek", $("#dayOfWeek").val());
user.save();
} else if (document.getElementById('dayOfMonth').checked) {
user.set("dayOfMonth", $("#dayOfMonth").val());
user.save();
}
$("#username").val("");
$("#firstName").val("");
$("#lastName").val("");
$("#regNumber").val("");
$("#password").val("");
$("#deparmentName").val("");
$("#email").val("");
$("#isOpen").val("");
$("#dayOfWeek").val("");
$("#dayOfMonth").val("");
},
render: function() {
var that = this;
var template = Handlebars.compile(UserRegister);
var myHtml = template(that.model.toJSON());
that.$el.html(myHtml);
return this;
}
});
return {
AddUserView: AddUserView,
UserRegisterView: UserRegisterView
};
});
router user func.
define([
'jquery',
'underscore',
'backbone',
'handlebars',
'spin',
'app/models/LoginModel',
'app/views/LoginView',
'app/views/UserRegisterView'
], function($,
_,
Backbone,
Handlebars,
Spinner,
Login,
LoginView,
UserRegisterView
) {
var Router = Backbone.Router.extend({
routes: {
'search': 'search',
'login': 'login',
'travels': 'travels',
'user': 'user',
'menu': 'menu',
'': 'home'
},
user: function() {
disposeView(new UserRegisterView().render());
}
dispose.view on util.js
function disposeView(view) {
Backbone.View.prototype.close = function() {
this.unbind();
this.undelegateEvents();
};
/* Şu anki viewi yok et */
if (this.currentView !== undefined) {
this.currentView.close();
}
/* Yeni view oluştur. */
this.currentView = view;
this.currentView.delegateEvents();
return this.currentView;
}
What's happening
Your UserRegisterView module returns an object which contains two constructors.
return {
AddUserView: AddUserView,
UserRegisterView: UserRegisterView
};
When using this module, what you're getting is the object above.
define([
// ...
'app/views/UserRegisterView'
], function(
// ...
UserRegisterView // value of the return in the module
) {
So you're kind of misleading yourself by calling it UserRegisterView as it's not the constructor, but the object containing the constructor.
To get a new UserRegisterView view instance with the current way your module is setup, you'd need to call it like so:
var userView = new UserRegisterView.UserRegisterView();
Or to create a AddUserView instance:
var addView = new UserRegisterView.AddUserView();
Solutions
Split up the module, one for each view constructor.
Change the name so at least it's not misleading (like UserViewsModule)
Other improvements
That being said, there are other improvements that could be made to your Backbone code.
var UserRegisterView = Backbone.View.extend({
// that's useless (if not used) and not a view property.
// model: User,
// don't use `el` like that, especially when using the view as a shared Constructor
el: '#form',
events: {
'click input[id="infoWeek"]': 'onInfoWeekClick',
'click input[id="infoMonth"]': 'onInfoMonthClick'
},
initialize: function() {
// Cache jQuery object of the view's element
this.$dayOfMonth = this.$("#dayOfMonth");
this.$dayOfMonth = this.$("#dayOfMonth");
// also use the shortcut function instead of `this.$el.find()`
}
onInfoWeekClick: function(e) {
this.$dayOfMonth.hide();
// calling render here is useless unless your using it as a parent
// view, where the child view overrides the render function.
},
onInfoMonthClick: function(e) {
this.$dayOfMonth.hide();
}
});
The disposeView function could be simplified:
function disposeView(view) {
var current = this.currentView;
if (current) current.close();
current = this.currentView = view;
current.delegateEvents();
return current;
}
Don't change the default Backbone view prototype each time the function is called. Instead, add the function once.
_.extend(Backbone.View.prototype, {
close: function() {
this.unbind();
this.undelegateEvents();
},
// any other function you want to add can go here.
});
In another answer, I go into details on how to extend Backbone's core classes with requirejs transparently.
You're already using jQuery, so don't use JavaScript DOM API document.getElementById('isOpen') interspersed with jQuery selectors $('#isOpen').
I made some improvements to the following view. Take the time to create yourself some utility functions (like reset and getValues) to simplify the flow of the code and encapsulate the complexity.
var AddUserView = Backbone.View.extend({
el: $(".page"),
events: {
'click #saveUser': 'saveUser'
},
// compile the template once while creating the view class
template: Handlebars.compile(UserRegister),
// get the selector string out of the code and place them in one place
// easy to change and maintain.
fields: {
username: "#username",
firstName: "#firstName",
lastName: "#lastName",
regNumber: "#regNumber",
password: "#password",
deparmentName: "#deparmentName",
email: "#email",
isOpen: "#isOpen",
dayOfWeek: "#dayOfWeek",
dayOfMonth: "#dayOfMonth",
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
// cache jQuery object of every field once after a render
this.field = _.reduce(this.fields, function(fields, selector, key) {
fields['$' + key] = this.$(selector);
return fields;
}, {}, this);
return this;
},
reset: function() {
// reset all the fields once without repeating code.
_.each(this.field, function($field) {
$field.val("");
});
return this;
},
getValues: function(keys) {
// get the value of multiple fields returned in a nice object
// ready to be sent to a Backbone model.
return _.reduce(keys, function(data, key) {
data[key] = this.field[key].val();
return data;
}, {}, this);
},
saveUser: function() {
var field = this.field,
user = new User(this.getValues([
'username',
'lastName',
'regNumber',
'password',
'departmentName',
'email',
'role',
]));
user.set({ isOpen: field.$isOpen.is(':checked') });
if (field.$dayOfWeek.is(':checked')) {
user.set("dayOfWeek", field.$dayOfWeek.val());
} else if (field.$dayOfMonth.is(':checked')) {
user.set("dayOfMonth", field.$dayOfMonth.val());
}
user.save();
this.reset();
},
});
In the following snippet, you're putting the context (this) into a local variable. I see that a lot and I could say that 90% of the times I see it on Stack Overflow questions, it makes no sense. It clearly screams copy-pasted.
render: function() {
var that = this;
// ...
that.$el.html(myHtml);
return this;
}
Please tell me you see that you're putting this into that, then using that throughout the function, then you still return this?!
Putting the context into a local variable is useful when the object is needed in a dynamically created callback.
render: function() {
var that = this; // this is available here
setTimeout(function() {
// here this is not available.
that.handleCallback();
}, 10);
// here we are in the same context as the first line.
return this;
}

What is the best way to add model in a collection from view?

I have a Backbone Marionette app with Router and a Controller. In my app you can view a collection of texts (index route with collection fetching from server), can view existing collection of texts (indexPage route without fetching from server) and can create a new text (form route). Views of list texts and create form are different from each other and changes in region.
I want to add a successully saved model to a collection and then redirect to indexPage route, but what is the best way to get a texts collection from _FormView success callback? Or how to restruct an app to do it simple?
I can send event to a controller with Backbone.Radio but want to deal without it.
Routes
router.processAppRoutes(controller, {
'': 'index',
'index': 'indexPage',
'create': 'form'
});
Controller
_Controller = Marionette.Controller.extend({
initialize: function () {
this.list = new _MainTexts();
},
index: function () {
if (!_.size(this.list)) {
var
self = this;
this.list.fetch({
success: function (collection, response, options) {
self.indexPage();
return;
}
});
}
this.indexPage();
},
indexPage: function () {
var
textsView = new _TextsView({
collection: this.list
});
application.getRegion('contentRegion').show(textsView);
},
form: function () {
var
formView = new _FormView({
model: new _MainText()
});
application.getRegion('contentRegion').show(formView);
}
});
Views
_TextView = Marionette.ItemView.extend({
className: 'item text',
template: function (serialized_model) {
return _.template('<p><%= texts[0].text %></p>')(serialized_model);
}
});
_TextsView = Marionette.CollectionView.extend({
className: 'clearfix',
childView: _TextView
});
Form view
_FormView = Marionette.ItemView.extend({
template: '#form-template',
ui: {
text: 'textarea[name="text"]',
submit: 'button[type="submit"]'
},
events: {
'click #ui.submit': 'submitForm'
},
submitForm: function (event) {
event.preventDefault();
this.model.set({
text: this.ui.text.val()
});
this.model.save({}, {
success: function (model, response, options) {
???
}
});
}
});
Ok, my problem solution is here. In controller action "form" I create event listener
var
formView = new _FormView({
model: model
});
formView.on('formSave', function (model) {
if (id == null) {
self.list.add(model);
}
...
});
Then in form view I trigger event
this.model.save({}, {
success: function (model, response, options) {
if (response.state.success) {
self.trigger('formSave', model);
}
}
});
That's all:)

Meteor Router.go not passing params

Snippet of code client-side:
Template.projectEditButton.events({
"click .edit": function() {
Router.go('projectForm', {prjId: this._id});
}
});
At this point, this._id is correct. In router file:
Router.route('/projects/form', {
name: 'projectForm',
data: function() {
return Projects.findOne(this.params.prjId);
}
});
this.params is empty. What am I missing?
You need to add the parameter to your route definition in order for the mapping to work:
Router.route('/projects/form/:prjId', {
name: 'projectForm',
data: function() {
return Projects.findOne(this.params.prjId);
}
});

Submit url parameter with form to meteor method

I am using aldeed:autoform in order to render a form and run its result through a Meteor.method(). My form looks as follows:
SelectPlanTemplates = new SimpleSchema({
templates: {
type: [String],
autoform: {
options: function() {
return PlanTemplates.find().map(function(doc) {
return { label: doc.title, value: doc._id };
});
},
noselect: true
}
},
userId: {
type: String,
allowedValues: function() {
return Meteor.users.find().map(function(doc) {
return doc._id;
});
},
autoform: {
omit: true
}
}
});
On my template, I just do the following.
+ionContent
+quickForm(schema="SelectPlanTemplates" id="SelectPlanTemplatesForm" type="method" meteormethod="createPlanFromTemplates")
My url is constructed like /plan/from_templates/{:userId}. I tried creating a hook to add the user id before submitting it.
AutoForm.hooks({
SelectPlanTemplatesForm: {
before: {
method: function(doc) {
doc.userId = Router.current().params.userId;
return doc;
}
}
}
});
However, it never seems to get to this hook.
How would I take a route parameter and pass it with my form to a meteor method with auto form?
I think I figured out a little bit of a weird way of do it.
In the router:
this.route('selectPlans', {
waitOn: function() {
return Meteor.subscribe('plan_templates');
},
path: '/select/plan_templates/:_id',
template: 'selectTemplates',
data: function() {
return new selectPlanTemplates({ userId: this.params._id });
}
});
Then I added doc=this to my template

Refresh view on Ember Data update

I’m doing a very basic application with Ember and Ember Data.
For some reason I always have the same problem. My application renders and displays the data correctly, but if I remove and search, it doesn't update the view.
I’ve already asked this here—the link has more code examples—but with not much luck. Here is how I’m trying to do it:
App = Ember.Application.create({
LOG_TRANSITIONS: true, LOG_VIEW_LOOKUPS: true
});
App.ApplicationAdapter = DS.FixtureAdapter.extend();
App.Sample = DS.Model.extend({ name: DS.attr('string') });
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('sample');
}
});
App.IndexController = Ember.ArrayController.extend({
actions: {
remove: function(sample) {
sample.destroyRecord();
}
}
});
App.Sample.FIXTURES = [
{ id: 1, name: 'Learn Ember.js'},
{ id: 2, name: 'Record 2' },
{ id: 3, name: 'Test Delete' }
];
App.ApplicationRoute = Ember.Route.extend({
actions: {
showModal: function(name, content) {
this.controllerFor(name).set('content', content);
this.render(name, {
into: 'application',
outlet: 'modal'
});
},
removeModal: function() {
this.disconnectOutlet({
outlet: 'modal',
parentView: 'application'
});
}
}
});
App.MyModalComponent = Ember.Component.extend({
actions: {
ok: function() {
this.$('.modal').modal('hide');
this.sendAction('ok');
}
},
show: function() {
this.$('.modal').modal().on('hidden.bs.modal', function() {
this.sendAction('close');
}.bind(this));
}.on('didInsertElement')
});
From your code I have tried to come up with a reasonable solution for your problem
Before I get into the solution I think the controller should be IndexController rather than sampleDeleteModalController because ember expects controller to have same name as the route.
App.SampleDeleteModalController = Ember.ObjectController.extend({
actions: {
remove: function() {
// Two ways
this.get('model').destroyRecord();
this.transitionToRoute('index');
}
}
});
transitionToRoute from the same route will not refresh a view.This will work only when you want to redirect to another route.
Solution to refresh view
option 1 : you can capture the same action inside index route after removing the record you can do this.refesh() which refreshes the model.
option 2 : You have to explicitly update the binded model inside the controller.
actions: {
remove: function() {
// Two ways
var localCopy = this.get('model');
localCopy.destroyRecord();
this.set('model',localCopy);
}
}
option 3 : After you set your model your model and then do this.rerender().Which is almost equivalent to window.reload()

Categories

Resources