Implementing a master view with multiple collections. Backbone.js - javascript

EDIT my humble mockup of what I want to implement
I have defined such a view:
define(['jquery', 'underscore', 'backbone', 'text!_templates/gv_container.html', 'bootstrap/bootstrap-tab'],
function($, _, Backbone, htmlTemplate, tab) {
var GridViewContainer = Backbone.View.extend({
id: 'tab-panel',
template: _.template(htmlTemplate),
events: { 'click ul.nav-tabs a': 'tabClicked' },
tabClicked: function(e) {
e.preventDefault();
$(e.target).tab('show');
},
render: function() {
this.$el.append(this.template);
return this;
}
});
return GridViewContainer;
}); // define(...)
The view's template looks like this:
<ul class="nav nav-tabs">
<li class="active">Products</li>
<!-- other tabs -->
</ul>
<div class="tab-content">
<div class="tab-pane active" id="products">
<!-- table with rows like { title, few more properties, edit|remove links } -->
</div>
<!-- other panes ... -->
</div>
Initially I thought I might use it (as common template for all collections at once).
Goal
I have three collections: products, categories, orders. And I want to insert each of them as tables on separate tab-panes. As for passing models to GridViewContainer I think I can wrap them in a composite model and simply pass the latter one as a part of options object to GridViewContainer view. What's next?
Product, Order, Category models have different properties, have different edit forms and probably event handling. This brings specificity. Should I use [EntityName]ListView per collection and then append it to GridViewContainer view?
ASIDE
I use jquery.js, underscore.js, backbone.js, require.js, and ASP.NET MVC 4 on server side.
I don't use Marionette.js

I would recommend to create a view for each model.
You may create a abstract wich holds the shared stuff like table creation.
Each view extends your abstract instead of Backbone.View.
var myAbstract = Backbone.View.extend({...})
var productsView = myAbstract.extend({...})
Then create a view, handling the wrapper (Tabs).
Example: http://jsfiddle.net/DC7rN/

Related

Ember.js Render multiple views inside static HTML

I'm new to Ember and I'm stuck trying to render views.
The application in context is not a single page application but so far Ember has been playing nice until I started dealing with views.
I'm trying to render more than one view on a single page (which is like a dashboard and has tons of static HTML and a few containers, one for each view).
Sample HTML:
Let's say I would like to render two lists, one inside the "left-container" div and the other inside the right container.
<div class="static-header></div>
<!-- more static content goes here -->
<div id="left-container"></div>
<!-- more static content goes here -->
<div id="right-container"></div>
...
I've tried creating different views and inserting them using the appendTo method (which is described in the Defining a View section of Ember guides) but it throws the error:
Container was not found when looking up a views template. This is most likely due to manually instantiating an Ember.View. See: http://git.io/EKPpnA
and I couldn't find my way using the link that it points to.
Ember code:
var App = Ember.Application.create({});
App.HomeViewLeft = Ember.View.extend({
templateName: 'home-left',
});
var view = App.HomeViewLeft.create();
view.appendTo('#left-container');
I have also tried using a ContainerView as described in Ember api docs:
App.HomeViewLeft = Ember.View.extend({
templateName: 'home-left',
});
var containerView = Ember.ContainerView.create({
classNames: ['container-view'],
});
containerView.pushObject(App.HomeViewLeft.create());
containerView.appendTo('left-container');
But I get the same error.
How should I render each view inside the #left-container and #right-container respectively?
Thanks in advance.
Template:
<div id="here"></div>
<script type="text/x-handlebars" data-template-name="components/my-foo">
{{text}}
</script>
JS:
App = Ember.Application.create({});
Ember.Application.initializer({
name: 'stand-alone-components',
initialize: function(container, application) {
App.MyFooComponent.create({text: 'Hello World', container: container}).appendTo('#here');
}
});
App.MyFooComponent = Ember.Component.extend({
init: function() {
this._super();
this.set('layout', Ember.TEMPLATES['components/my-foo']);
}
});
http://emberjs.jsbin.com/nekowidera/1/edit?html,js,output
It looks like you are trying to make views behave like partials
Move anything static into a partial and include it in your main template like so:
{{partial "header"}}
The lists you want to render can be turned into a component (if the only thing that changes in them is the data).
So you end up with a single view that contains partials and components:
<div class="static-header>{{partial "header"}}</div>
{{partial "static-content"}}
<div id="left-container">{{list-component data=firstList}}</div>
{{partial "static-content"}}
<div id="right-container">{{list-component data=secondList}}</div>
...

Rendering a nested model handlebars?

I have a data structure like this.
var myModel {
_id: 798698,
username: "John",
message: {
message1: "Some cool messsage",
message2: "I'm mad Ohio State lost"
}
}
The message object is a little tricky. Inside my handlebars template.
{{#each message}}
<div class="message">
{{../username}}: {{this}}
</div>
{{/each}}
Then when that renders the output is something like this.
<div class="NCAA">
<div class="NBA">
<div class="message">steve: What?</div>
<div class="message">steve: Okay?</div>
<div class="message">steve: Hey</div>
</div>
<div class="NBA"></div>
<div class="NBA">
<div class="message">
Iowastate: Hey nikeman
</div>
</div>
</div>
The class names come from a backbone view, but the problem is the template is being wrapped by a div and I am not sure how to prevent that so that its just a list of .message.. Also I cant identify why there is an empty div, I have suspicions but cant point my finger to it..
Here is the backbone view code, just to show you how things are rendered.
var Marionette = require('backbone.marionette');
var ChatView = Marionette.ItemView.extend({
className: 'NBA',
template: require('../../templates/message.hbs')
});
module.exports = CollectionView = Marionette.CollectionView.extend({
className: 'NCAA',
initialize: function() {
this.listenTo(this.collection, 'change', this.render);
},
itemView: ChatView
});
In Backbone, every view is associated with an el. At the time of instantiation, if you do not provide an el for the view, backbone uses an empty DIV. I guess this is what is happening in your case.
So at the time of your view instantiation, just provide an el element like this:
var myView = new ChartView({ el: $('.myElementForView') })
Read this text from Backbone View el documentation
All views have a DOM element at all times (the el property), whether they've already been inserted into the page or not. In this fashion, views can be rendered at any time, and inserted into the DOM all at once, in order to get high-performance UI rendering with as few reflows and repaints as possible. this.el is created from the view's tagName, className, id and attributes properties, if specified. If not, el is an empty div.

knockout js jquery Load

Hi I am trying to load knockout js html pages dynamically from jQuery Load but getting
You cannot apply bindings multiple times to the same element.
How do I load knockout js pages with jqyuery load.
What I am trying is to create a navigation framework by loading url page (Views/home.html or Views/login.html)into when ever user click on link.
So I can't load all knocokout viewmodel on fist load
I am trying to create Navigation model to load/refresh only body of the page not the full page.
For example
<ul class="nav navbar-nav" id="nav">
<li>
Home
</li>
<li>
Login
</li>
<li>
Contactus
</li>
</ul>
<div id="body"></div>
if Home click
$("#body").load("Views/home.html");
if Login click
$("#body").load("Views/login.html");
Home.html
var homeViewModel= function() {
this.firstName = ko.observable();
this.lastName = ko.observable();
};
ko.applyBindings(new homeViewModel());
Login.html
var loginViewModel= function () {
this.username = ko.observable("test");
};
ko.applyBindings(new loginViewModel())
I have attached and removed node but the dependency is always name of viewmodel must be viewModel
ko.cleanNode($("#body")[0]);
$("#body").load(url, function() {
ko.applyBindings(new viewModel(), $("#body")[0]);
});
You may want to look at Pager.js, which extends knockout.js for page history and partial loading.
Load all your javascript in your main page.
Keep the partials (Views/home.html, Views/login.html) containing only html with binding declarations.
You don't need to explicitly call applyBindings when switching partial, Pager.js does that for you (with proper cleanup for all bindings on previous partial).

Angular.js - Two Concurrent But Exclusive Routes ($routeProvider)

<div id="header">
Header
</div>
<div id="wrapper">
<div id="slider" ng-view>
Slider
</div>
<div id="mainWindow" ng-view>
Main window
</div>
</div>
http://jsfiddle.net/abehnaz/k9Y4f/
I am working with a webapp that has two major views. The first is a main window (standard stuff), and the second is a "slider". The slider is more than a slide out menu (sometimes called a "shelf"). It has routing and secondary information associated with it.
In what seems to be the normal Angular paradigm, I would use a single ng-view in the main window, and that would populate the main window with the results of the $routeProvider's view template.
However, here I want to be able to ALSO have the slider have its own route. Angular does not seem to support this. I was thinking that I could potentially hack the functionality in by making a client side URL of the form:
www.app.com/MAINWINDOW/someView1/someView2/SLIDER/someViewa
Am I on the right track with this, or is there another functionality of Angular that I can use?
I would not recommend two routeParameters. Make your sidebar a service and put a controller on your wrapper, slider, and mainWindow. By having the ng-controller on #wrapper as well as the children, you can share data between the controllers (and in turn, inject the service into those controllers. I have done this and it was very successful. See an example below. In my code, my ng-view and routing returned everything within the <div ng-app="myApp">. You could easily put the ng-view in the MainCtrl and trigger routes from the sidebar.
In your template:
<div ng-app="myApp">
<div ng-view>
//Produced by routing
<div id="wrapper" ng-controller="RootCtrl">
<div id="Sidebar" ng-controller="SidebarCtrl">
...
<div ng-click="setUnit(mod.number, unit.number)">Unit 1</div>
...
</div>
<div id="mainWindow" ng-controller="MainCtrl">
</div>
</div>
</div>
</div>
In your javascript:
A service:
myApp.service('questionsSvc', function(Modules) {
var QuestionsSrc = {};
QuestionsSrc.questions = [];
var getQuestionsForModAndUnitFn = function(mod, unit) {
...bunch of code to have it populate QuestionSrc with questions
};
return {
getQuestionsForModAndUnit: getQuestionsForModAndUnitFn,
Questions: QuestionsSrc
};
});
For Controllers:
function RootCtrl($scope) {
$scope.data = {};
}
function SidebarCtrl($scope, Modules, questionsSvc) {
$scope.setUnit = function (mod, unit) {
questionsSvc.getQuestionsForModAndUnit(mod, unit);
$scope.data.questions = questionsSvc.Questions.questions;
//$scope.data.questions was used in the MainCtrl window to do an ng-repeat on the json array of questions bound to this model.
}
};
function MainCtrl($scope){
...whatever you need to manipulate the code...
}
With this example, I am sharing information across Controllers. The magic is with the $scope.data = {}; from the RootCtrl. It allows me to attach questions to the $scope based on an action in the Sidebar (clicking a label) and use that same $scope in the MainCtrl to display the questions in a pretty format. With what I just showed, I did not have to use $routeParameters to pass variables to go to another page (such as module and quiz) but I could have done so as you asked but would have had the sidebar ng-click change routes.
Hope this helps.
One solution is to use Angular UI router:
AngularUI Router is a routing framework for AngularJS, which allows
you to organize the parts of your interface into a state machine.
Unlike the $route service in Angular core, which is organized around
URL routes, UI-Router is organized around states, which may optionally
have routes, as well as other behavior, attached.
States are bound to named, nested and parallel views, allowing you to
powerfully manage your application's interface.
Just to give a flavor for how you could use AngularUI Router- It supports multiple named views (which you can read more about under "Multiple & Named Views" in their docs). So for instance you can use ui-view with names:
<div id="wrapper">
<div id="slider" ui-view="slider">
Slider
</div>
<div id="mainWindow" ui-view="main">
Main window
</div>
</div>
Then within config you can attach states to various routes and specify what each view should display for that state.
myapp.config(function($stateProvider, $urlRouterProvider){
$stateProvider
.state('index', {
url: "/index",
views: {
"Slider": {
templateUrl:"route1.viewA.html"
},
"Main": {
templateUrl:"main1.viewA.html"
}
}
})
.state('slider2', {
url: "/slider2",
views: {
"Slider": {
templateUrl:"route2.viewA.html"
},
"Main": {
templateUrl:"main1.viewA.html"
}
}
})
There's a variety of other ways you could use AngularUI Router though. For instance, you may be able to get away with just using nested routes- which they recommend trying first.
Here's a couple more good reference material you might check out:
http://joelhooks.com/blog/2013/07/22/the-basics-of-using-ui-router-with-angularjs/
http://www.ng-newsletter.com/posts/angular-ui-router.html

Ember.js problems binding content of array controller to content of a different array controller

We're having trouble linking the content of one array controller with the content of another array controller in Ember.js. We've scoured Stackoverflow, but no solution seems to work for us.
The scenario is simple. We have a model called "Campaign". The model is pretty basic and looks like this...
Campaign Model:
App.Campaign = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
opts: DS.attr('string'),
starred: DS.attr('boolean'),
keywords: DS.hasMany('App.Keyword')
});
// Create dummy campaign array
App.Campaign.FIXTURES = new Array();
// Populate dummy campaign array
...
The Campaign controller is also basic. It's a simple ArrayController with nothing going on inside it ...
Campaign Index Controller:
App.CampaignsIndexController = Em.ArrayController.extend({
});
For the sake of this question, we've stripped our application template down to the essentials. As you can see below, the application template has a sidebar. In the sidebar are two views. One view is for the site's navigation, the other view is for "Starred Campaigns". This view will contain a subset of the overall collection of campaigns.
Application Template:
<!-- ================================================== -->
<!-- ======================= APP ====================== -->
<!-- ================================================== -->
<div id="app" class="">
<!-- ================================================== -->
<!-- ==================== SIDEBAR ===================== -->
<!-- ================================================== -->
<div id="sidebar">
{{ view App.NavigationView }}
{{ view App.StarredCampaignsView controller="App.StarredCampaignsController" }}
</div>
<!-- ================================================== -->
<!-- ====================== PAGE ====================== -->
<!-- ================================================== -->
<div id="page" class="">
{{ outlet page }}
</div>
</div>
We have a controller for Starred Campaigns. This is where things start to get confusing. In our controller, we bind the content of the ArrayController to the content of the CampaignsIndexController, which holds the array of all Campaign models.
Starred Campaigns Controller:
App.StarredCampaignsController = Em.ArrayController.extend({
content: [],
contentBinding: Ember.Binding.oneWay('App.CampaignsIndexController.content')
});
The view of for Starred Campaigns is also very simple...
Starred Campaigns View:
App.StarredCampaignsView = Em.View.extend({
templateName: 'templates/layout/starred-campaigns',
});
And this is what the template for Starred Campaigns looks like. It is attempting to loop over the items in the controller.content and pass them off to another view.
Starred Campaigns Template:
<h5 class="sidebar-title">Starred Campaigns</h5>
<ul class="starred-campaigns clearfix">
{{# each item in controller.content }}
{{ view App.StarredCampaignItemView itemBinding="item" }}
{{/ each }}
</ul>
However, despite all of this, the content in StarredCampaignsController remains empty, even when we populate the App.Campaign array that is represent by App.CampaignsIndexController. See any problems with our approach? I feel like we're missing something really simple here.
In Ember, for controllers to require other controllers, you need to use the needs property. This is always changing in Ember: in previous versions it was the responsibility of the router with this.controllerFor, and with even older versions, the approach was more akin to the approach you appear to be attempting to take. However, the final approach has long since been deprecated, and instances of controllers/models/views are no longer held on App directly, only their object representations.
To dive straight in with the needs, you need to tell your App.StarredCampaignsController controller that it requires the App.CampaignsIndexController like so:
App.StarredCampaignsController = Em.ArrayController.extend({
content: [],
needs: ['campaigns_index']
});
You now have access to the App.CampaignsIndexController controller in your starred campaigns view: {{controllers.campaigns_index}}.
See simple JSFiddle: http://jsfiddle.net/AGJfB/
The reason why yours isn't working is because you've reference the object of the controller, as opposed to its instance:
Ember.Binding.oneWay('App.CampaignsIndexController.content')
Whilst you have an App.CampaignsIndexController on App, that's its object. Ember will create the instance for you when you access it through any of the Ember ways (this.controllerFor, needs, navigating to it via the URL, etc...).
In days gone by we could use:
Ember.Binding.oneWay('App.campaignsIndexController.content')
However, that's such a bad way to do things, because you're referencing an absolute path to the controller. Ever since Ember pre 1, I've never had to use Ember.Binding.oneWay, but there still will be cases where you need it. When you find yourself using it though, it's probably wise to ask yourself if it's necessary, and whether there's a better way to do it.
I hope I've explained myself well. Please feel free to ask any questions!

Categories

Resources