How to structure a single page app with knockout.js? - javascript

I am mostly wondering how to organize things like modal windows, and dynamic pages like profiles. Should the viewModel only contain one profile view or contain all profiles loaded? This here just doesnt seem very "clean".
viewModel = {
profile: ko.observableArray([
new ProfileViewModel()
//... any others loaded
])
, createPostModal: {
input: ko.observable()
, submit: //do something to submit...
}
}
<div data-bind="foreach: profile"><!-- profile html --></div>
<div data-bind="with: createPostModal"></div>
This way doesn't seem very consistent. Is there anybody who has built a single page app with knockout that can offer some advice? Code samples would be appreciated.

We are just starting down this path at work, and so are not quite sure what we're doing. But here's the idea we have.
The page should be composed of any number of "components," possibly nested. Each component has a view model and one public method, renderTo(el), which essentially does
ko.applyBindings(viewModelForThisComponent, el)
It also could have the ability to render subcomponents.
Constructing or updating a component consists of giving it a model (e.g. JSON data from the server), from which it will derive the appropriate view model.
The app is then created by nesting a bunch of components, starting with a top-level application component.
Here is an example for a "hypothetical" book-managing application. The components are LibraryUI (displays a list of all book titles) and DetailsUI (a section of the app that displays details on a book).
function libraryBookViewModel(book) {
return {
title: ko.observable(book.title),
showDetails: function () {
var detailsUI = new BookDetailsUI(book);
detailsUI.renderTo(document.getElementById("book-details"));
}
};
}
function detailsBookViewModel(book) {
return {
title: ko.observable(book.title),
author: ko.observable(book.author),
publisher: ko.observable(book.publisher)
};
}
function LibraryUI(books) {
var bookViewModels = books.map(libraryBookViewModel);
var viewModel = {
books: ko.observableArray(bookViewModels);
};
this.renderTo = function (el) {
ko.applyBindings(viewModel, el);
};
}
function BookDetailsUI(book) {
var viewModel = detailsBookViewModel(book);
this.renderTo = function (el) {
ko.applyBindings(viewModel, el);
};
}
Note how we could make the book details appear in a jQuery UI dialog, instead of in a singleton #book-details element, by changing the showDetails function to do
var dialogEl = document.createElement("div");
detailsUI.renderTo(dialogEl);
$(dialogEl).dialog();

There are 3 frameworks out there that help with creating SPAs using Knockoutjs.
Durandal
Pagerjs
KnockBack
I have used Durandal and I really like it. Easy to use and has a lot of nice configurations so you can plug-in your own implementations. Also, Durandal is created by the same creator of Caliburn which was an very popular framework for building Silverlight/WPF applications.

Now in 2014, you probably want to use Knockout's component feature and Yeoman to scaffold your initial ko project. See this video: Steve Sanderson - Architecting large Single Page Applications with Knockout.js

[update april 5, 2013] at time of writing this answer was valid. Currently I would also suggest the Durandal JS approach as the way to go. Or check John Papa's Hot Towel or Hot Towelette SPA templates if you are using ASP.NET MVC. This also uses Durandal.
Original answer below:
I would like to point out Phillipe Monnets 4 part series about Knockout.js to you. He is the first Blogger I encounterd who splits up his example project in multiple files. I really like most of his ideas. The only thing I missed, was how to handle ajax / rest retrieved collections by using some kind of Repository / Gateway pattern. It's a good read.
Link to part 1: http://blog.monnet-usa.com/?p=354
Good luck!

I just open-sourced the mini SPA framework I put together with Knockout being the major component.
knockout-spa
A mini (but full-fledged) SPA framework built on top of Knockout, Require, Director, Sugar.
https://github.com/onlyurei/knockout-spa
Live Demo:
http://knockout-spa.mybluemix.net
Features
Routing (based on Flatiron's Director): HTML5 history (pushState) or hash.
Highly composable and reusable: pick modules/components for a page in the page-specific JS and they will be auto-wired for the page's HTML template
SEO ready (prerender.io)
Fast and lightweight (85 KB of JS minified and gizpped)
Two-tier bundle build for JS for production: common module that will be used by most pages, and page-specific modules that will be lazy-loaded
Organized folder structure to help you stay sane for organizing and reusing JS, CSS, HTML
Using Knockout 3.3.0+ so ready for Knockout's flavor of web component and custom tags (http://knockoutjs.com/documentation/component-overview.html)
All documentation are in the major dependencies' own homepages, so that you don't need to completely learn a new framework
Knockout http://knockoutjs.com
Require http://requirejs.org
Director https://github.com/flatiron/director
jQuery http://jquery.com
Sugar http://sugarjs.com

Related

Vue.js : Best way to implement MPA(Multi page app) in laravel

I have been looking around for quite a time, But didn't got anything convening.
What will be the best approach and practice to implement Vue MPA architecture in laravel.
Searched for quite a bit. But there isn't anything which will give you a clear idea. Your answer will help alot, Please make it brief.
It will also be helpful to answer the point :
Is it a good idea to use just laravel as a data API, And keep Vue
separate from laravel ?
Best approach for implementing hybrid of SPA and MPA.
Some options that I've already used:
Use Laravel to render the "main view" + connect vue.js application.
Basically laravel will render the Vue application and every request goes throught an API.
Easy to setup
Authentication + user validation is easier (you can use laravel session manager for that - don't need to build/use tokens or whatever. "No need to worry about your application state".)
Easy to "disconnect" from Laravel - if you choose in the future to decouple the SPA application.
Use laravel (or lumen) only as an API, and on another server render a SPA.
This can take more time, since you'll need to setup an extra server, prepare cross-origin, etc.
Also easy to setup, but can take more time than option #1
You'll need to create something for user validation/state management, etc.
Easy to place into laravel, if you decide in the future to use "only one app".
Can be easier to maintain/scale (if you have a frontend team, they don't need to worry about laravel - same for your "laravel team", they "won't need to worry" about the frontend)
Laravel + Vue = "one application"
You can use Laravel to render all views + vuejs for components/elements in the page.
Easy to setup. You have laravel + vuejs, and they are already prepared to be used together. https://laravel.com/docs/5.5/frontend#writing-vue-components
Not so easy to decouple. In this case you'll need to create the same views for vue.js. This can take time.
This is the "traditional web development" (in my opinion). If I had to start a project like this today, I wouldn't create all pages in Vue.js + something in Laravel (Controller + new route) to render this view. If you do this (again - my opinion), it's just extra work. If you are worried about SEO, there are "fallbacks"/extra options.
--
All options are testable + scalable.
It also depends on how you start (Should I worry about how I'll decouple the app in the future? Laravel + Vue will be for good?), how your team will work (Does the frontend team really needs to setup laravel or they only need to worry about the frontend code?), etc.
Not sure if i answered your question, if not, please leave a comment.
You haven't found anything clear because there isn't really anything to talk about other than 'What feels right to your understanding and project needs'. If you found yourself very unsure, feel free to dive into doing whatever makes sense to you and then re-adjust the structure when you gain more experience.
Also, read books about system architecture, those will help a lot.
Is it a good idea to use just laravel as a data API, And keep Vue separate from Laravel?
By this, I'm assuming you mean a SPA? Honestly, if your application is small, then I see this is fine.
Larger applications tend to be difficult to maintain if they were SPA.
Read: https://medium.com/#NeotericEU/single-page-application-vs-multiple-page-application-2591588efe58
If you end up using Laravel as an API endpoint, then use the stripped down version of it, Lumen, because it comes without Blade and few other stuff. Lumen is stripped down version to act as an API-endpoint.
Best approach for implementing hybrid of SPA and MPA.
From my experience having attempted to build 4+ projects as hybrids, here's what I found the most optimal structure:
My example will be about an app that saves 'Posts'.
1. Use a repository design pattern.
This one will save you a lot of headache in maintaining your code and maintain a DRY (Don't Repeat Yourself) concept on your code.
Create a directory App\Repositories\
Make a new class PostsRepository. This one will be the one communicating with the database and contains most of the logic.
Create the directory App\Services\
Make a new class PostsService. This one will have the PostsRepository in its constructor.
The service class will be one handling taking user's input whether from the Web controller or the API controller.
<?php
namespace App\Service;
use App\Repositories\PostsRepository;
class PostsService;
{
protected $repository;
public function __construct(PostsRepository $repository)
{
$this->repository = $repository;
}
}
Make a seperation between Web and API controllers.
For web controllers, you create the controller like usual:
php artisan make:controller PostsController
For API controllers, you create the controller to be inside an Api folder.
php artisan make:controller Api\PostsController
The last command will create the directory App\Http\Controllers\Api and have the controller be placed in it.
Recapping
Now we have different controllers to return results appropriate to the startpoint (web / api).
We have services that both the (web / api) controllers send their data to be validated (and have the action taken by the repository).
Examples:
<?php
namespace App\Http\Controllers;
use App\Service\PostsService;
class PostsController extends Controller
{
protected $service;
public function __construct(PostsService $service)
{
$this->service = $service;
}
public function index()
{
/**
* Instead of returning a Blade view and
* sending the data to it like:
*
* $posts = $this->service->all();
* return views('posts.index', compact('posts'));
*
* We go ahead and just return the boilerplate of
* our posts page (Blade).
*/
return view('posts.index');
}
}
...
<?php
namespace App\Http\Controllers\Api;
use App\Service\PostsService;
class PostsController extends Controller
{
protected $service;
public function __construct(PostsService $service)
{
$this->service = $service;
}
/**
* Returns all posts.
*
* A vue component calls this action via a route.
*/
public function index()
{
$posts = $this->service->all();
return $posts;
}
/**
* Notice we don't have a store() in our
* Web controller.
*/
public function store()
{
return $this->service->store();
}
}
...
<?php
namespace App\Services;
use App\Repositories\PostsRepository;
class PostsService extends Controller
{
protected $repository;
public function __construct(PostsRepository $repository)
{
$this->repository = $repository;
}
public function all()
{
$posts = $this->repository->all();
return $posts;
}
public function store()
{
$request = request()->except('_token');
$this->validation($request)->validate();
return $this->repository->store($request);
}
public function validation(array $data)
{
return Validator::make($data, [
'content' => 'required|string|max:255',
//
]);
}
}
In our PostsRepository we actually call methods that save the data. E.g. Post::insert($request);.
2. Dedicate an API group
Route::prefix('api/v1')->middleware('auth')->group(function() {
Route::post('posts/store', 'Api\PostsController#store')->name('api.posts.store');
});
Giving API routes a ->name() helps when you make phpunit tests.
3. Blade views
Those are ought to be stripped-down simple.
views/posts/index.blade.php:
#extends('layouts.app', ['title' => trans('words.posts')])
#section('content')
<!-- Your usual grid columns and stuff -->
<div class="columns">
<div class="column is-6">
<!-- This comp. can have a modal included. -->
<new-post-button></new-post-button>
<div class="column is-6">
<posts-index-page></posts-index-page>
</div>
</div>
#endsection
4. Vue structure.
https://github.com/pablohpsilva/vuejs-component-style-guide
So those Vue components might live in resources/assets/js/components/posts/ where inside /posts/ I'd have folders titled for example IndexPage, CreateModal, EditModal with each folder having its .vue and README.md.
I'd use the <posts-index-page> in index.blade.php and drop in the <post-create-modal> and <edit-post-modal> whenever I want.
All the vue components will use the API endpoint we specified in our Routes file.

React.js Dataflow Paradigm - how are data.props, state, and JSX beneficial?

I'm using React and created a small page that has 4 components (React classes, what is the preferred term? I'll call them components in this post):
Component Breakdown
a parent "App" component that includes and manages the other components
a "Form" component that lets the user interact with the page
a "String View" component that displays the input from the form as text
a "Visual View" (I know, bad name...) component that interprets the string view and performs actions to adjust the visual.
Dataflow
The communication of these components using states and props is as follows:
The Form has onChange handlers that pass the new state to the App
The App funnels the state data to the String View
The String View updates and passes the updated state to the App
The App funnels the new state data to the Visual View
Finally, the Visual View now updates based on the new state.
Sample Code
var App = React.createClass({
handleFormChange: function(formData) {
this.setState({formData:formData});
},
handleStringChange: function(stringData) {
this.setState({stringData:stringData});
},
render: function() {
return (
<div className="app">
<FormView onFormChange={this.handleFormChange}/>
<StringView formData={this.state.formData} onStringChange={this.handleStringChange}/>
<VisualView stringData={this.state.stringData}/>
</div>
);
}
});
var FormView = React.createClass({
handleFormChange: function(e) {
this.props.onFormChange(e.target.value);
}
render: function() {
return(
<div className="formView">
<select onChange={this.handleFormChange}>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
</div>
);
}
});
var StringView = React.createClass({
componentDidUpdate: function() {
this.props.onStringChange({newString:'newStringState'});
},
render: function() {
this.props.formData;
// process formData and update state
return (
<div className="stringView">
{this.props.formData}
</div>
);
}
});
var VisualView = React.createClass({
render: function() {
var selection = this.props.stringData,
output = '';
if (selection === 1) {
output = 'Hooray, 1!';
} else {
output = 'Yes! 2!';
}
return (
<div className="stringView">
{output}
</div>
);
}
});
Questions
Is this the correct dataflow paradigm that React is trying to enforce (components only talk to parents, not siblings)?
Compared to how I would have written this in just regular JavaScript, this seems terribly constrained. Am I missing the big picture? Is this dataflow paradigm designed to prevent future problems (if so, which ones? Any that can't be solved with disciplined regular JavaScript?), or is there some other purpose that I'm missing?
I'm getting a lot of repeated function names (handleFormChange for example, it's used in App and Form View), is there a good way to make these distinguishable? Or, are repeated function names across components desirable?
When the components actually build, the JSX stuff gets transpiled down into real JavaScript. Is there an advantage to using JSX? Would writing components in the already transpiled JavaScript have an advantage?
To start, I think it is ok to call "components", and I've seen lot of people call that way. I will answer your questions below, in an order that I think is better to make my answers make sense.
When the components actually build, the JSX stuff gets transpiled down into real JavaScript. Is there an advantage to using JSX? Would writing components in the already transpiled JavaScript have an advantage?
JSX kinda mixes JavaScript and HTML, so, it makes your code "friendly". You will create your components, and just "call" them as HTML tags. Below you can see the difference between writing JSX and pure JavaScript.
return <div className="my-component"><p>Awesome</p></div>;
return ReactDOM.div({
className: 'my-component'
}, ReactDOM.p({}, "Awesome"));
I don't know you, but I would get tired to write this amount of code just to render a div with a paragraph.
You can check more benefits of using it here:
https://hchen1202.gitbooks.io/learning-react-js/content/benefits_of_jsx.html
I'm getting a lot of repeat function names (handleFormChange for example, it's used in App and Form View), is there a good way to make these distinguishable? Or, are repeated function names across components desirable?
It is not bad, also, your app is a "demo" one, if it would be a "real" one, it would have some better names for the components (i.e. <FormView> would be <ContactForm>) and maybe your method names would be different. But it is not bad at all. For example, inside <ContactForm> you may call the submit handler as onSubmit, but outside (the prop that you pass), you may call onContactFormSubmit, or, in a more semantic way, onContactFormFilled.
If your application starts to grow and you have lots of things repeated in the same component (that is the case of your <App>), you may try to split your components, therefore, each of your component will "know" about a "domain", and it would not appear to have lots of repeated stuff.
Is this the correct dataflow paradigm that React is trying to enforce (components only talk to parents, not siblings)?
First of all, React doesn't "enforce" anything, as some people say, React is the "v" in MVC, so, you have your "presentation" layer described as components, and the data may flow in the way you want.
But you got a point when you say "components only talk to parents, not siblings", because that is the way you can "communicate" between your components when you have multiple components. Since a component can't see its sibling, you need someone to orchestrate this communication, and, in this case, this is the parent's job.
There are other ways to make components "talk" to each other (i.e. using refs), but having a parent to orchestrate is, IMO, the most reliable (and better testable) one.
Compared to how I would have written this in just regular JavaScript, this seems terribly constrained. Am I missing the big picture? Is this dataflow paradigm designed to prevent future problems (if so, which ones? Any that can't be solved with disciplined regular JavaScript?), or is there some other purpose that I'm missing?
I decided to answer that as the last one, to sum up some things.
IMO, React is just great, you start to have your "logic" in the right place (a component), and you can just compose things in order to make your page work well (and by well I mean it is orchestrated correctly).
React also makes it easier to "think" about how you will build your interfaces. This Pete Hunt's blog post is amazing, and you should check it out:
https://facebook.github.io/react/docs/thinking-in-react.html
If you would be writing your code with plain JavaScript, you would have to handle DOM in some way (i.e. using a template engine) and your code would end up mixing DOM manipulation with your application logic. React just abstracts that for you. You can only care about presenting stuff. Another advantage is that, when everything is a component, you can reuse those components, it doesn't matter where they are located. If you pass the props correctly, your component will work as expected.
I know it seems exhaustive to write those components, but as you start to write more components you start to see lots of benefits. One of them is to nevermore wonder about how to present your data (no more concatenating HTML strings or calling template functions). Another one is that it is easy to "split" your interfaces, what makes your code easier to maintain (and that is not straightforward when using plain JavaScript).
To be honest, this application you wrote is really simple, and you may not see lots of advantages of using React for building it. I think you should try to create a more "complex" one, and compare it with plain JavaScript. By "complex", I mean "user interface" complex. For example, create a form that allows user to submit multiple "people". And "people" should have "name" and multiple "pet" (which also have a name). You will see how hard is it to handle "add" and "remove" operations in this case, and how easy React handle that kind of thing.
I think that is it, I hope you and React "click". It changed my mind about how to create complex user interfaces.

Communication between ViewModels in KnockoutJS

I'm trying to implement communication between my view models in a knockoutjs driven application. I scaffolded it using yeoman tool, and as you can see it uses AMD:
define(['knockout', 'signals', 'text!./nav-bar.html'], function(ko, Signal, template) {
function NavBarViewModel(params) {
this.route = params.route;
}
return { viewModel: NavBarViewModel, template: template };
});
I have to define an object that I would later use to dispatch events, right? Something like that:
var EventDispatcher = {
itemClicked: new Signal()
};
And then, whenever something happens in the NavBarViewModel I'd like to do:
EventDispatcher.itemClicked.dispatch();
The problem is - where should I put this EventDispatcher thing? It's obvious that it should be some kind of a global object, so that every VM could get a hold on it, but it would be ugly. Dependency injection came to mind, since everything else in this architecture I choose is done this way, but how to achieve that? I come from WPF, MVVM world, and so far I've used MVVMLight framework which had this great Messenger component. Is there anything like that in the JS world (and if it's js-signals lib I'm already using, then how should I use it to achieve my goal?)
I could also use the subscribable object built into the knockout fw, but the question still stands - where to put it (or how to share the instance between VMs)?
You'd quite simply inject it by including it in your define.
First, create a new file, EventDispatcher.js with your EventDispatcher code inside (and other relevant Knockout bits, like returning the view model and whatnot).
Then in your current file add it in:
define([ ... , ... , "EventDispatcher"], function( ... , ... , EventDispatcher )
Now you can simply call its methods within this file by using:
EventDispatcher.itemClicked.dispatch()
(Where EventDispatcher is what we've named it in our define parameters).
Do bear in mind though that your EventDispatcher.js file will also need the signals file passed to it through its own define wrapper.

Structuring a multi-view (multiple pages) Knockout.js app

I've been working with Knockout.js for a couple of weeks now doing examples and other tutorials, but I'm still trying to figure out how to structure everything on the app I'm working on. It's a simple school backend app that keeps a list of all the classes, grades, teachers, attendance, and students. It has multiple pages:
list of all the clases (where you can add/edit/remove classes)
each class has a list its students (where you can add/edit/remove students)
each student has a list of his/her attendance and grades
teachers page
school subjects page
and others ...
How do I go about structuring this app? I've already started on the 'classes' page by creating a Class Model:
function Class(data) {
var self = this;
self.id = data.id;
self.name = ko.observable(data.name);
self.students = data.students;
self.attendance = data.attendance;
}
... and a Class ViewModel that loads the initial content (list of classes). It also contains a function to add, edit and remove classes.
function ClassViewModel() {
var self = this;
self.classes = ko.observableArray(ko.utils.arrayMap(classArr, function(eachClass) {
return new Class(eachClass);
}));
[...]
}
So do I continue doing a separate Model and ViewModel for each major part of the app (students, teachers, subjects, etc) and bind them separately on their own pages? And if I go this route, how do I go about sharing functions between ViewModels without having the same function added to each of them?
I have created a convention over configuration library for KO called Knockout.BindingConventions.
https://github.com/AndersMalmgren/Knockout.BindingConventions/wiki
Install using nuget
Install-Package Knockout.BindingConventions
One feature of that library is its template convention, basically it understands that a viewmodel named ClassViewModel should be connected to a template (View) called ClassView
http://jsfiddle.net/xJL7u/
I've also created a template bootstrapper that utilizes above library
https://github.com/AndersMalmgren/Knockout.Bootstrap/wiki
Install using nuget
Install-Package Knockout.Bootstrap
With these two libraries combined you can structure your application like this on server (Using your ClassViewModel as example)
app
Class
ClassViewModel.js
OtherViewModel.js
Shared
DatePickerViewModel.js
views
Class
ClassView.htm
OtherView.htm
Shared
DatePickerView.htm
Fully working MVC4 demo here
https://github.com/AndersMalmgren/Knockout.Bootstrap.Demo

Backbone showing/hiding rendered views best practices

New to using Backbone and have a very simple application. Basically there are Clients and ClientItems. I have a view to show all Clients and if you click on a Client you get taken to their ClientItems. Going to this ClientItems view should just hide the Clients view and going back to Clients should hide ClientItems. Now, in my render() function for each view, it is going through the collections and dynamically adding stuff to the page. When I go back and forth between the two (using the back button) I don't really need to fully render again as all the data is there in the page, just hidden. Where should this logic go? Right now I have it in the render() function but it feels sloppy, what is the preferred way of handling this?
We are using a global variable App with several common function used across application:
var App = {
initialize : function() {
App.views = {
clientView : new ClientsView(),
clientItemView : new ClientsItemsView()
}
},
showView: function(view){
if(App.views.current != undefined){
$(App.views.current.el).hide();
}
App.views.current = view;
$(App.views.current.el).show();
},
...
}
And then I use this App from other parts of application:
App.showView(App.views.clientView);
IntoTheVoid's solution is good – it's nice to have a single place to hide/show views. But how do you activate the logic?
In my experience, routers are the best place for this. When a route changes and the appropriate function is called, you should update the active, visible view(s).
What if you need multiple views to be visible at once? If you have a primary view that always changes when the route changes, and multiple subsidiary sticky views, you need not worry. But if it's more complex than that, think of creating a ComboView that neatly packages all the relevant views into one containing el node. That way the above logic still works, and your router functions are not littered with logic for managing what views are visible at the moment.

Categories