Disable all the checkboxes inside a list - backbone.js

I have a form as follows.
onRender: function (){
var user = new models.User({'id': this.options.user_id});
var userFetching = user.fetch({cache: false}).promise();
self = this;
$.when(userFetching).done(function(data){
var form = new Backbone.Form({
model: user //,
//fields: ['username', 'email', 'password', 'domain', 'groups', 'id']
}).render();
And I need to disable all the check boxes coming under that particular form.
How it is possible in backboneJS?All the checkboxes comming under a < ul > tag.

This should work
onRender= function() {
var user = new models.User({
'id': this.options.user_id
});
var userFetching = user.fetch({
cache: false
}).promise();
self = this;
$.when(userFetching).done(function(data) {
var form = new Backbone.Form({
model: user //,
//fields: ['username', 'email', 'password', 'domain', 'groups', 'id']
}).render();
this.$el.find("#edit-user-form").find('input:checkbox').attr('disabled', true)
})
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="edit-user-form">
<input type="checkbox" name="vehicle" value="Bike">I have a bike<br>
<input type="checkbox" name="vehicle" value="Car">I have a car
</form>

Related

Render template with fos-rest then close pop-up

I have a button on a page that when is clicked, it opens a popup that contains a form(type). I managed to get to render the form. When the form is submitted, the adding to db is done but I'm being redirected to the form template/route in a new window. What I want to do, is close the pop-up, no redirection to another page.
It starts from angular
function FeedbackController (modalService) {
var vm = this;
vm.open = open;
function open () {
modalService.openModal('add_feedback');
}
}
The route:
add_feedback:
path: /feedback
defaults:
_controller: MainBundle:Api/Feedback:addFeedback
template: MainBundle:Modals:feedback.html.twig
options:
expose: true
And the action:
/**
* #FosRest\View()
*/
public function addFeedbackAction(Request $request)
{
$view = View::create();
$feedback = new Feedback();
$feedbackService = $this->get('main.feedback.service');
$form = $this->createForm(new FeedbackType(), null, ['action' => 'feedback']);
$form->handleRequest($request);
if ($form->isValid()) {
$formData = $form->getData();
$feedbackService->create($formData, $feedback);
return null;
}
$view
->setData($form)
->setTemplateData($form)
->setTemplate('MainBundle:Modals:feedback.html.twig');
return $view;
// return $this->render('MainBundle:Modals:feedback.html.twig', array(
// 'form' => $form->createView(),
// ));
}
So you should post your form using ajax query, wait for response status and close popup or display message if smth went wrong.
Here is an example:
angular.module('myApp', [])
.controller('myCtrl', function($scope, $http) {
$scope.hello = {
name: "Boaz"
};
$scope.newName = "";
$scope.sendPost = function() {
var data = $.param({
json: JSON.stringify({
name: $scope.newName
})
});
$http.post("/echo/json/", data).success(function(data, status) {
$scope.hello = data;
})
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app='myApp'>
<div ng-controller="myCtrl">
{{hello.name}}
<form ng-submit="sendPost()">
<input ng-model="newName" />
<button type="submit">Send</button>
</form>
</div>
</body>

Angular UI Bootstrap modal result is undefined

I'm building an app that loads a modal on a click on a button in an ng-grid row. Displaying the modal and the correct data works great. Problem is with getting the data back form the form in the modal. This bit of code
modalInstance.result.then(function(selectedItem){
$scope.selected = selectedItem;
});
Returns 'undefined' for 'selectedItem'
Here's the modal.
<div ng-app="myApp">
<div ng-controller="UsersCtrl">
<script type="text/ng-template" id="editUserModal.html">
<div class="modal-header">
<h3 class="modal-title">Edit User <em>{{user.user_full_nm}} {{user.user_id}}</em></h3>
</div>
<div class="modal-body">
<p>User Name: <input type="text" name="user_full_nm" value="{{user.user_full_nm}}"></p>
<p>Email: <input type="text" name="user_nm" value="{{user.user_nm}}"></p>
<p>Active:
<select ng-model="user.deleted" ng-selected="user.deleted">
<option value="0" ng-selecte>Active</option>
<option value="1">Inactive</option>
</select>
</p>
<p>Termination Date: {{user.termination_date | date:'longDate'}}</p>
<p>Last Entry Date: {{user.last_entry | date:'longDate'}}</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</script>
<div class="gridStyle" ng-grid="gridOptions"></div>
</div>
</div>
Here's the Angular app.
var app = angular.module('myApp', ['ngGrid','ui.bootstrap']);
app.controller('UsersCtrl', function($scope, $http, $modal) {
$scope.filterOptions = {
filterText: "",
useExternalFilter: false
};
$scope.totalServerItems = 0;
$scope.pagingOptions = {
pageSizes: [20, 40, 60],
pageSize: 20,
currentPage: 1
};
$scope.setPagingData = function(data, page, pageSize){
var pagedData = data.slice((page - 1) * pageSize, page * pageSize);
$scope.userData = pagedData;
$scope.totalServerItems = data.length;
if (!$scope.$$phase) {
$scope.$apply();
}
};
$scope.getPagedDataAsync = function (pageSize, page, searchText) {
setTimeout(function () {
var data;
if (searchText) {
var ft = searchText.toLowerCase();
$http.get('getUsers').success(function (largeLoad) {
data = largeLoad.filter(function(item) {
return JSON.stringify(item).toLowerCase().indexOf(ft) != -1;
});
$scope.setPagingData(data,page,pageSize);
});
} else {
$http.get('getUsers').success(function (largeLoad) {
$scope.setPagingData(largeLoad,page,pageSize);
});
}
}, 100);
};
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
$scope.$watch('pagingOptions', function (newVal, oldVal) {
if (newVal !== oldVal && newVal.currentPage !== oldVal.currentPage) {
$scope.getPagedDataAsync($scope.pagingOptions.pageSize,$scope.pagingOptions.currentPage,$scope.filterOptions.filterText);
}
}, true);
$scope.$watch('filterOptions', function (newVal, oldVal) {
if (newVal !== oldVal) {
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage,
$scope.filterOptions.filterText);
}
}, true);
var editUserButtonTemplate = '<i class="fa fa-pencil" style="cursor:pointer;" ng-click="editUser(row.entity)"></i>';
$scope.gridOptions = {
data: 'userData',
columnDefs: [
{field: 'user_id', displayName: 'User ID', visible: false},
{field: 'user_nm', displayName: 'Email'},
{field: 'user_full_nm', displayName: 'Name'},
{field: 'deleted', displayName: 'Active', width: 60, cellFilter: 'activeFilter'},
{field: 'termination_date', displayName: 'Termination Date',cellFilter: 'date:\'longDate\''},
{field: 'last_entry', displayName: 'Last Entry Date',cellFilter: 'date:\'longDate\''},
{field: '', DisplayName: '', cellTemplate: editUserButtonTemplate, colFilterText: '', width:20}
],
enablePaging: true,
showFooter: true,
showFilter: true,
enableRowSelection: false,
filterOptions: $scope.filterOptions,
totalServerItems:'totalServerItems',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions
};
/************ open the edit user modal *********************/
$scope.editUser = function(value) {
var modalInstance = $modal.open({
templateUrl: 'editUserModal.html',
// scope: $scope,
controller: 'editUserCtrl',
resolve: {
user: function () {
return value;
}
}
});
modalInstance.result.then(function(selectedItem){
$scope.selected = selectedItem;
});
};
});
app.controller('editUserCtrl', function($scope, $http, $modalInstance, user) {
$scope.user = user;
$scope.ok = function () {
$modalInstance.close($scope.selected);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
// for the 'deleted' column, display 'Active' or 'Inactive' instead of 1 or 0
app.filter('activeFilter', function(){
var types = ['Active', 'Inactive'];
return function(type){
return types[type];
};
});
So as happens so often, as soon as I posted my question I found this one.
angular-ui-bootstrap modal not passing back result
Which led me to the problem.
$scope.ok = function () {
$modalInstance.close($scope.selected);
};
Changed $scope.selected to $scope.user and it's working as expected now.

Render object type in backbone-forms

I'm trying to render a object type with the backbone-forms tool but I can't get it to work.
I define a model:
Models.ModelType = Backbone.Model.extend({
schema: {
object1: {
type: 'Object',
subSchema: {
option: {
type: 'Checkbox'
},
description: {
type: 'Text'
}
}
},
object2: {
type: 'Checkbox'
}
}
});
And then I call it in my view:
var model = new app.Models.ModelType();
var form = new Backbone.Form({
model: model
}).render();
It doesn't render either object1 and object2. When I comment object1, it does render object2.
In my template I have:
<div data-fields="object1"></div>
<div data-fields="object2"></div>
Edit:
Here is the view:
Views.ModelView = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function () {
var model = new app.Models.ModelType();
var form = new Backbone.Form({
model: model
}).render();
$('#form').html(form.el);
}
});
And then I call it like:
var modelView = new ModelView();
In my routes file

Backbone - Nested Model with Model Binder

ModelBinder doesn't seem to work together with nested model( backbone-nested project) ..the changes from model don't get propogated to the nested elements.On changing the input value the span value doesn't change...If NestedModel is replace with DeepModel it works. Again the NestedModel also works if the person.name is removed and Model has just one level(lastName and firstName).
<script type='text/coffeescript'>
$ ->
class MyModel extends Backbone.NestedModel
defaults:
person:
name :
firstName: 'Bob'
lastName: 'Sass'
window.model = new MyModel
FormView = Backbone.View.extend
initialize: ->
#modelBinder = new Backbone.ModelBinder();
#modelBinder.bind(#model,#el)
el: '#frm'
view = new FormView model: model
</script>
<body>
<form method="post" action="/test" id='frm'>
<div id="welcome"> Welcome, <span id='person.name.firstName'></span> <span id='person.name.lastName'></span>
<br><br>
Edit your information:
<input type="text" name="person.name.firstName" value="zz"/>
<input type="text" name="person.name.lastName" value="nn"/></div>
</form>
I ran into the very same problem. I found success using Backbone.ModelBinder in conjunction with backbone-associations. It allowed me to use ModelBinder with my nested models and accomplish what you are describing.
I took the example you posted and created a fiddle Using Backbone.ModelBinder with backbone-associations using your example. Check it out and see if it answers your question.
The JavaScript ends up looking like this:
var Name = Backbone.AssociatedModel.extend({
defaults: { 'firstName': '', 'lastName': '' }
});
var Person = Backbone.AssociatedModel.extend({
defaults: { 'name': null },
// create a relation for our nested model
relations: [{
'type': Backbone.One,
'key': 'name',
'relatedModel': Name
}]
});
var MyModel = Backbone.AssociatedModel.extend({
defaults: { 'person': null },
// create a relation for our nested model
relations: [{
'type': Backbone.One,
'key': 'person',
'relatedModel': Person
}]
});
var FormView = Backbone.View.extend({
el: '#frm',
initialize: function() {
this._modelBinder = new Backbone.ModelBinder();
},
render: function() {
var bindingsHash = {
'person.name.firstName': [
{ 'selector': '#firstName' },
{ 'selector': '[name=firstName]' }
],
'person.name.lastName': [
{ 'selector': '#lastName' },
{ 'selector': '[name=lastName]' }
]
};
this._modelBinder.bind(this.model, this.el, bindingsHash);
},
close: function() {
this._modelBinder.unbind();
}
});
// create the model
var modelInfo = new MyModel({
'person': {
'name': {
'firstName': 'Bob',
'lastName': 'Sass'
}
}
});
// create the view and render
var view = new FormView({ model: modelInfo });
view.render();

Backbone Marionette Nested Composite View

So I am stuck. I got the great Backbone.Marionette to handle my nested childs/parents relationships and rendering(doing it with the bare backbone was a nightmare), but now i'm facing problems with my nested composite view,
I'm always getting a The specified itemViewContainer was not found: .tab-content from the parent composite view - CategoryCollectionView, although the itemViewContainer is available on the template, here is what I'm trying to do, I have a restaurant menu i need to present, so I have several categories and in each category I have several menu items, so my final html would be like this:
<div id="order-summary">Order Summary Goes here</div>
<div id="categories-content">
<ul class="nav nav-tabs" id="categories-tabs">
<li>Appetizers</li>
</ul>
<div class="tab-content" >
<div class="tab-pane" id="category-1">
<div class="category-title">...</div>
<div class="category-content">..the category items goes here.</div>
</div>
</div>
Here is what I have so far:
First the templates
template-skeleton
<div id="order-summary"></div>
<div id="categories-content"></div>
template-menu-core
<ul class="nav nav-tabs" id="categories-tabs"></ul>
<div class="tab-content" ></div>
template-category
<div class="category-title">
<h2><%=name%></h2>
<%=desc%>
</div>
<div class="category-content">
The menu items goes here
<ul class="menu-items"></ul>
</div>
template-menu-item
Item <%= name%>
<strong>Price is <%= price%></strong>
<input type="text" value="<%= quantity %>" />
Add
Now the script
var ItemModel = Backbone.Model.extend({
defaults: {
name: '',
price: 0,
quantity: 0
}
});
var ItemView = Backbone.Marionette.ItemView.extend({
template: '#template-menuitem',
modelEvents: {
"change": "update_quantity"
},
ui: {
"quantity" : "input"
},
events: {
"click .add": "addtoBasket"
},
addtoBasket: function (e) {
this.model.set({"quantity": this.ui.quantity.val() });
},
update_quantity: function () {
//#todo should we do a re-render here instead or is it too costy
this.ui.quantity.val(this.model.get("quantity"));
}
});
var ItemCollection = Backbone.Collection.extend({
model: ItemModel
});
var CategoryModel = Backbone.Model.extend({
defaults: {
name: ''
}
});
var CategoryView = Backbone.Marionette.CompositeView.extend({
template: '#template-category',
itemViewContainer: ".menu-items",
itemView: ItemView,
className: "tab-pane",
id: function(){
return "category-" + this.model.get("id");
},
initialize: function () {
this.collection = new ItemCollection();
var that = this;
_(this.model.get("menu_items")).each(function (menu_item) {
that.collection.add(new ItemModel({
id: menu_item.id,
name: menu_item.name,
price: menu_item.price,
desc: menu_item.desc
}));
});
}
});
var CategoryCollection = Backbone.Collection.extend({
url: '/api/categories',
model: CategoryModel
});
var CategoryCollectionView = Backbone.Marionette.CompositeView.extend({
el_tabs: '#categories-tabs',
template: '#template-menu-core',
itemViewContainer: ".tab-content", // This is where I'm getting the error
itemView: CategoryView,
onItemAdded: function (itemView) {
alert("halalouya");
//this.$el.append("<li>" + tab.get("name") + "</li>");
//$(this.el_tabs).append("<li><a href='#category-" + itemView.model.get("id") + "'>"
//+ itemView.model.get("name") + "</a></li>")
}
});
I know It's a bit hard to follow but you guys are my last resort. There is no problems with the templates and the cateogry fetching and the other stuff(it was already working before converting the CategoryCollectionView from a Marionette collection to a composite view.)
Edit 1
Added App initalizer on request:
AllegroWidget = new Backbone.Marionette.Application();
AllegroWidget.addInitializer(function (options) {
// load templates and append them as scripts
inject_template([
{ id: "template-menuitem", path: "/js/templates/ordering-widget-menuitem.html" },
{ id: "template-category", path: "/js/templates/ordering-widget-category.html" },
{ id: "template-menu-core", path: "/js/templates/ordering-widget-menu-core.html" },
{ id: "template-skeleton", path: "/js/templates/ordering-widget-skeleton.html" }
]);
// create app layout using the skeleton
var AppLayout = Backbone.Marionette.Layout.extend({
template: "#template-skeleton",
regions: {
order_summary: "#order-summary",
categories: "#categories-content"
}
});
AllegroWidget.layout = new AppLayout();
var layoutRender = AllegroWidget.layout.render();
jQuery("#allegro-ordering-widget").html(AllegroWidget.layout.el);
// Initialize the collection and views
var _category_collection = new CategoryCollection();
var _cateogories_view = new CategoryCollectionView({ api_key: window.XApiKey, collection: _category_collection });
_category_collection.fetch({
beforeSend: function (xhr) {
xhr.setRequestHeader("X-ApiKey", window.XApiKey);
},
async: false
});
//AllegroWidget.addRegions({
/// mainRegion: "#allegro-ordering-widget"
//});
AllegroWidget.layout.categories.show(_cateogories_view);
});
AllegroWidget.start({api_key: window.XApiKey});
You are adding to the collection via fetch before you call show on the region.
Marionette.CompositeView is wired by default to append ItemViews when models are added to it's collection. This is a problem as the itemViewContainer .tab-content has not been added to the dom since show has not been called on the region.
Easy to fix, rework you code as below and it should work without overloading appendHtml.
// Initialize the collection and views
var _category_collection = new CategoryCollection();
// grab a promise from fetch, async is okay
var p = _category_collection.fetch({headers: {'X-ApiKey': window.XApiKey});
// setup a callback when fetch is done
p.done(function(data) {
var _cateogories_view = new CategoryCollectionView({ api_key: window.XApiKey, collection: _category_collection });
AllegroWidget.layout.categories.show(_cateogories_view);
});
okay this is pretty weird but adding this in the CategoryCollectionView class:
appendHtml: function (collectionView, itemView, index) {
//#todo very weird stuff, assigning '.tab-content' to itemViewContainer should have been enough
collectionView.$(".tab-content").append(itemView.el);
}
solved the problem, however i have no idea why it works, asssigning '.tab-content' to the itemViewContainer should have been enough, any idea?

Resources