Coder Social home page Coder Social logo

astoilkov / jsblocks Goto Github PK

View Code? Open in Web Editor NEW
2.8K 113.0 103.0 4.32 MB

2012 UI framework (I was 20 years old, React didn't exist, inspired by Knockout)

Home Page: http://jsblocks.com

License: Other

JavaScript 98.84% HTML 1.05% CSS 0.11%
mvc framework javascript

jsblocks's Issues

chaining with each is not skipping the function passed to each

Compiling a chain into a raw callback composition is a very good idea. As of now, there are some bugs with it.

An exemple is better than long explanations. The following code:

blocks([0,1,2])
.each(function(v) { console.log('first each', v); })
.each(function(v) { console.log('second each', v); })
.map(function(v) { console.log('first map', v); return v; })
.map(function(v) { console.log('second map', v); return v; })
.value();

is logging this:

first each 0
first each 1
first each 2
first each 0
second each 0
first each 1
second each 1
first each 2
second each 2
first each 0
second each 0
first map 0
second map 0
first each 1
second each 1
first map 1
second map 1
first each 2
second each 2
first map 2
second map 2

We see 2 things:

  • the second "each" is recalling the first one
  • the 2 cleverly composed "map" are recalling both "each"

As would the use of templates recursively

As would the use of templates recursively, see example:

JS:

var Menus = blocks.observable([{label: 'test1', menu: [{label: 'sub_test1', menu:[]}]}, {label: 'test2', menu: []}, {label: 'test3', menu: []}]);

HTML:

<script id="item_menu" type="text/blocks-template">
    <div data-query="each($this)">
        <h3>{{label}}</h3>
        <p>{{menu.length}}</p>
        <div data-query="visible(menu.length > 0).template('item_menu', menu)"></div>
    </div>
</script>
<div data-query="template('item_menu', Menus)"></div>

RESULT:

test1
1
{{label}}
{{menu.length}}
test2
0
test3
0

Specify a default route to handle unmatched routes

There is currently no way to specify what happens on an unmatched route.

I propose a user should be able to set this as part of view.options as follows:

App.View('404', {
    options: {
        defaultRoute: true
    }
}

This will allow a application to specify it's 404 view, or pass a bad request to the home page for example.

Nested views with lazy loading not working correctly

I tried to reproduce the same behavior as in my previous ticket (#7) and I think I know what's going on. It seems that nested views with lazy loaded templates are not working correctly.

For example, if I take the example from the Nested views page and I "convert" the Navigation/Article view with a lazy loaded template, it still works fine. However, when I also replace the Blog view with a lazy loaded template, then that view is still rendered correctly, but the child views no longer load.

Here's a small Plunker code example showing the issue: http://plnkr.co/edit/OJDNpDdo8XDDFw37MFbs?p=preview

from / to JS

Is it possible to convert a JS object to an observable / observableArray? Each object should, of course, an observable object.

e.g.
var array = [{foo:'bar},{bar:'foo'}];
var observableArrayWithObservables = blocks.toObservable(array);

Dependency Observables fail in Collections

So, I'm not sure if this is actually a problem or if I've missed an important detail here, but I can't seem to find a way to solve it. I created a model following the example of dependency observables and model introduction and it worked just fine.

However, if I do that and push multiple of these models into a collection, the observable is not evaluated correctly anymore.

I'm using the following code:

        var App = blocks.Application();

        var Cake = App.Model({
            cakeType: blocks.observable(),
            cakeSize: blocks.observable(),

            fullCake: blocks.observable(function() {
                return this.cakeType() + 'cake (' + this.cakeSize() + ')';
            })
        })

        var Cakes = App.Collection(Cake);

        App.View('Bakery', {
            cakes: Cakes([
                { cakeType: 'strawberry', cakeSize: 'small' },
                { cakeType: 'chocolate', cakeSize: 'large' }
            ])
        });
    <div data-query="view(Bakery)">
        <div data-query="each(cakes)">
            <h2>Let's eat some, {{fullCake}}!</h2>
            Type: <input data-query="val(cakeType)" />
            <br />
            Size: <input data-query="val(cakeSize)" />
        </div>
    </div>

You would expect this to display Let's eat some, strawberrycake (small) and Let's eat some, chocolatecake (large). However, while the input values are displayed correctly, the dependency obsevable returns Let's eat some, strawberrycake (small) in both entries.
I haven't found a solution for this yet, the error also occurs using {{this.fullCake}} or {{$this.fullCake}}

Can an observable respond to external script events?

I want to create a clear button for a text field that is being observed. It works, from the standpoint that the button clears the content of the field. But the two-way binding is not updated when this happens. Is there either an internal way to change the bound value, or an event I can fire that will wake up the binding to the new value?

Can't use with jQuery

Can this be used with jQuery?

I get this error when I load jQuery on a page with jsblocks.

TypeError: Cannot call method 'createElement' of undefined
at assert (eval at (c:\Users\bclarke\Work\code\factory\src\jsblocks\auth\node_modules\blocks\node\blocks-node.js:11690:23), :15378:21)
at eval (eval at (c:\Users\bclarke\Work\code\factory\src\jsblocks\auth\node_modules\blocks\node\blocks-node.js:11690:23), :17046:24)
at Config.api.me (eval at (c:\Users\bclarke\Work\code\factory\src\jsblocks\auth\node_modules\blocks\node\blocks-node.js:11690:23), :17097:3)
at arr (eval at (c:\Users\bclarke\Work\code\factory\src\jsblocks\auth\node_modules\blocks\node\blocks-node.js:11690:23), :14520:3)
at eval (eval at (c:\Users\bclarke\Work\code\factory\src\jsblocks\auth\node_modules\blocks\node\blocks-node.js:11690:23), :14524:2)
at c:\Users\bclarke\Work\code\factory\src\jsblocks\auth\node_modules\blocks\node\blocks-node.js:11693:19
at contextBubble (c:\Users\bclarke\Work\code\factory\src\jsblocks\auth\node_modules\blocks\node\blocks-node.js:11710:5)
at executeCode (c:\Users\bclarke\Work\code\factory\src\jsblocks\auth\node_modules\blocks\node\blocks-node.js:11683:5)
at executePageScripts (c:\Users\bclarke\Work\code\factory\src\jsblocks\auth\node_modules\blocks\node\blocks-node.js:11678:5)
at Object.Middleware._renderContents (c:\Users\bclarke\Work\code\factory\src\jsblocks\auth\node_modules\blocks\node\blocks-node.js:12153:9)

updates in view from other frameworks

I'm trying to combine jsblock with websockets using socket.io, however I can't figure out how to update a view from outside (from the callback of the socket) any ideas on how to get about this?

EDIT: I forgot to include to code I'm using

'use strict';
var App = blocks.Application();

var Message = App.Model({
    author: App.Property({
        defaultValue: 'anonymous'
    }),
    message: blocks.observable()
});

var Messages = App.Collection(Message, {
    even: blocks.observable(function () {
        return this().length % 2 == 0;
    })
});

App.View('Chat', {

    name: 'Wannes Gennar',
    newMessage: Message(),

    init: function () {
        this.socket = io('http://localhost:3000');
        this.messages = Messages();

        var service = this;
        console.log(service);

        this.socket.on('chat message', function (msg) {
            service.messages.push({
                message: msg
            });
        });
    },

    addMessage: function () {

        this.socket.emit('chat message', { author: this.newMessage.author, message: this.newMessage.message });
        this.newMessage.reset();
    },

    up: function(e)
    {
        if (e.which === 13)
            this.addMessage();
    }
});

With query

The API documentation of the with() query is confusing. The examples are using view(ViewName, '$context') in stead of with(). Is it meant to be used with the view() query, or are the examples wrong?

Enable CSP mode, so jblocks can be used in Chrome extensions

Chrome extensions do not allow code generation from strings. The John Doe example errs with

Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:".

Consider adding CSP mode in the future.

AngularJS performance test optimization

Hello,

If possible, please update your angular.js performance test cases with track by $index in each of the ng-repeats in order to even the playing field.

initial-load/angular.html

<tr ng-repeat="person in people track by $index">
   <td ng-repeat="column in columns track by $index">
       {{person[column]}}
   </td>
   <td ng-repeat="column in columns track by $index">
       {{person[column]}}
   </td>
</tr>

syncing-changes/angular.html

<tr ng-repeat="person in people track by $index">
    <td ng-repeat="column in columns track by $index">
        {{person[column]}}
    </td>
    <td ng-repeat="column in columns track by $index">
        {{person[column]}}
    </td>
</tr>

this.super would not work in consequent calls

You always start resolution of super from the this, so you can trap in infinite recursion.
e.g

Class1
method

Class2 extends Class1
method -> here we have this.super('method')

Class3 extends Class2
method -> here we have this.super('method')

inst = new Class3().method();

When you call this.super('method') in Class3 you will indeed call the Class2.method but there this will refer inst object. So when you try to call super again the resolution will be again Class2.method.

The best fix of this is to not use it at all.

Dynamic View Containers

Hi,
I have a index.html structure like:

<header id="header" data-query="view(HeaderView)"></header>
<section id="main">
    <aside id="sidebar" data-query="view(SideBarView)"></aside>
    <section id="content"></section>
</section>

In SideBar view, there are links for content like navigateTo(DasboardView) or navigateTo(UserProfileView).

I am loading views with requirejs like this:

define(function (require) {
   var App = blocks.Application();
   var HeaderView = require('./views/HeaderView');        
   var UserProfileView = require('./views/UserProfileView');        
   App.start();
});

What I want is:

Load that clicked link target view in content section dynamically. With 'dynamically', I mean loading of View without defining it in html, for example without defining these in content section:

<div class="container" data-query="view(DasboardView)"></div>
<div class="container" data-query="view(UserProfileView)"></div>

Is it possible with jsblocks framework without breaking route history support?

How to init jquery plugins after view is mounted to DOM?

Hi Antonio,
I have to init some jquery plugins after the view(and its template) is mounted to DOM. I tried view's routed callback but it seems this callback is too early for this task. I can only do initialization of plugins by waiting in setTimeout. Is there a better way?
Regards,

Remove the utility methods built-in jsblocks. Leave that to lodash and underscore

jsvalue - the built-in utility methods in jsblocks are having more problems than solving ones. The best option is to remove the utility methods by default and release it as a separate library. Only if it becomes stable enough it could get part of some jsblocks built. It should be people decision by default which library to use. Also jsblocks will introduce integration with lodash and underscore by default so the same experience is achieved when using those libraries.

Creating Records in Collections has no Request Payload

I have tried writing an application that is hooked up to a server, basically it was creating a record as stated in the documentation, and combined that with the Collection/List approach.

The problem is, when I run the code that is presented in the documentation, it - obviously - works. If I embed this code in something similar to the list example on the homepage, it doesn't sync correctly. So I went back to the Cake test script and tried to reproduce it using the following code:

        var App = blocks.Application();

        var Cake = App.Model({
            cakeType: blocks.observable(),
            cakeSize: blocks.observable(),

            fullCake: function(self) {
                return self.cakeType() + 'cake (' + self.cakeSize() + ')';
            },
        })

        var Cakes = App.Collection(Cake);

        App.View('Bakery', {
            newCake: Cake(),

            cakes: Cakes([
                { cakeType: 'strawberry', cakeSize: 'small' },
                { cakeType: 'chocolate', cakeSize: 'large' }
            ]),

            bakeCake: function() {
                this.cakes.add(this.newCake);

                console.log(this.newCake.cakeType()); // TEST OUTPUT
                this.newCake.sync();
                this.newCake.reset();
            }
        });
    <div data-query="view(Bakery)">
        <div>
            <h2>Let's eat some, {{newCake.fullCake(newCake)}}!</h2>
            Type: <input data-query="val(newCake.cakeType)" />
            <br />
            Size: <input data-query="val(newCake.cakeSize)" />
            <button data-query="click(bakeCake)">Bake!</button>
        </div>
    </div>

The problem is, while the request to the respective create url - which isn't given in this example but it doesn't make a difference - is successfully sent, it does not contain any request payload. So I'm basically sending an empty payload to the server.
Loggin the data via console.log at the "TEST OUTPUT" does show, that the Model actually contains data but it's not serialized and packed into the request.

Proposal: rich syntax for "each" data-query

bad: data-query="each(items.view)"
http://jsblocks.com/api/blocks-queries-each
There is no possibility to make 2 cycles with different $this

Better, but ugly syntax: data-bind="foreach: { data: categories, as: 'category' }"
http://knockoutjs.com/documentation/foreach-binding.html

Best but not compatible: ng-repeat="model in collection" https://docs.angularjs.org/api/ng/directive/ngRepeat
angular-like syntax is powerful, but this is not pure js

proposal 1:
data-query="each(collection)" data-iterator="model" (for array)
data-query="each(collection)" data-iterator-key="key" data-iterator-key="value"
it's possible to make 2 nested loops with different iterators. A little bit bloat syntax for array and really bloat syntax for objects

proposal 2:
data-query="each(collection, index, value)" (for array)
data-query="each(collection, key, value)" (for object)
better syntax, but index, key and value are not defined, so workaround will be not very simple and clear. Not extendable with new stuff

proposal 3:
data-query="each(collection).index('index_name').value('value_name')" (for array)
data-query="each(collection).key('key_name').value('key_name')" (for object)
more text, but it's javascript. There is not such big problem to extend "key", "index" and "value" methods.

proposal 4:
data-query="as({index:'index'}).each(collection)"
even better, but a little bit strange order.
There is no need to change each(), you only add "as" data-query with method "each".

proposal 4.1:
data-query="index('index').each(collection)"
proposal 3 + proposal 4 combination. Fully compatible with proposal 4

Is there a way to have 2 instances of a view with different options on a page?

Hi astoilkov,

I am having difficulty trying to make a view re-usable so that 2 copies can coexist on a page at the same time with different options.

Is there any way to pass options to a view with a data-query?

I would like to do something like this:

data-query(view(MyView, { id: 1}))

data-query(view(MyView, { id: 2}))

However, the view method does not accept options as a parameter.

I tried making a custom data-query like this but am struggling to get it to work.

data-query(portlet(MyView, { id: 1}))

blocks.queries.portlet = {
    preprocess: function (param1, param2) {
        this.html('<div id="'+param1+'" data-query="view('+param1+')"></div>');
        blocks.query(param2);
    }  
};

reset issue with collection data

<script src="http://jsblocks.com/blocks/0.3.2/blocks.js"></script>
<script type="text/javascript">
    (function (blocks, undefined) {

        var App = blocks.Application();

        function keydown(e) {
            if (e.which === 13) {
                this.folders.add(this.newFolder);
                this.newFolder.reset();
            }
        }

        var SubFolder = App.Model({
            name: App.Property(),
            editing: blocks.observable(false),
            toggleEdit: function () {
                this.editing(!this.editing());
            },
            remove: function () {
                this.destroy(true);
                console.log("remove");
            }
        });

        var Folder = App.Model({
            name: App.Property(),
            folders: App.Collection(SubFolder)(),
            editing: blocks.observable(false),
            toggleEdit: function () {
                this.editing(!this.editing());
            },
            remove: function () {
                this.destroy(true);
            },
            newFolder: SubFolder(),
            addSubfolder: keydown
        });

        var Folders = App.Collection(Folder);

        App.View("Folders", {
            newFolder: Folder(),
            folders: Folders(),
            keydown: keydown
        });

    })(window.blocks);
</script>

<div id="folders" data-query="view(Folders)">
    <input type="text" id="addFolderInput" data-query="val(newFolder.name).keydown(keydown)" placeholder="Add new folder and press enter" />
    <div data-query="each(folders)">
        <div class="folder">
            <span data-query="visible(!editing())" class="folderName">
                {{name}}
            </span>
            <span data-query="visible(editing)">
                <input type="text" data-query="val(name)" />
            </span>
            <span class="buttons">
                <span data-query="click(toggleEdit).setClass('icon-checkmark', editing).setClass('icon-pencil', !editing())"></span>
                <span data-query="click(remove)" class="icon-bin"></span>
            </span>
            <div id="newSubfolder">
                <input type="text" id="addSubfolder" data-query="val(newFolder.name).keydown(addSubfolder)" placeholder="Add new subfolder and press enter" />
            </div>
            <div id="subfolders" data-query="each(folders)">
                <div class="folder">
                    <span data-query="visible(!editing())" class="folderName">
                        {{name}}
                    </span>
                    <span data-query="visible(editing)">
                        <input type="text" data-query="val(name)" />
                    </span>
                    <span class="buttons">
                        <span data-query="click(toggleEdit).setClass('icon-checkmark', editing).setClass('icon-pencil', !editing())"></span>
                        <span data-query="click(remove)" class="icon-bin"></span>
                    </span>
                </div>
            </div>
        </div>
    </div>
</div>

results in any subfolder created for any folder to populate a new folder.

ZURB Foundation compatibility issue

Hey guys, I'm trying to use Zurb Foundation with my jsblocks app but it doesn't seem to be working.

I added some console.logs() to Foundation's init function and confirmed that it is being called.

Npm install empty

If I npm install jsblocks I get a folder with blocks.js but the file is empty.

RxJS

Hello @astoilkov. Can I use RxJS or another similar library with jsblock observable together?

Example view query API docs

In the API docs for the view() query you have the following piece of code as example:

<!-- looping through the View users collection -->
<ul data-query="view(users)">

Shouldn't that be the each() query?

jsblocks.com is down

Hi Antonio
I get ERR_CONNECTION_REFUSED error while connecting to jsblocks.com.

jsblocks and hybrid mobile app

Hi,

I am exploring possibility to use jsblocks and materializecss in an Android app (using Crosswalk to boost performance). Is there any limitation that I need to know? I understand right now that jsblocks can be used for both web and hybrid mobile app (as in Ionic or OnSen UI). However I don't know if it works well with Cordova/Phonegap or not. My feeling is that I can integrate 2 JS framework together without any issue because there is no conflict in architecture.

Thanks

Dinh

Nested views

I'm quite confused about nested views. According to the documentation you should add the name of the parent view, for example:

App.View('Parent', {});
App.View('Parent', 'Child', {});

In the API docs on the other hand, there is no reference of this parent/child declaration.


I'm having quite a lot of problems with this setup, I'm always getting the same error in my log:

Uncaught ReferenceError: Child is not defined

And I'm pretty sure that part of the script is executed. Also, when you switch the order, for example:

App.View('Parent', 'Child', {});
App.View('Parent', {});

I'm noticing that there's a high CPU usage and the page freezes.


Also, this doesn't really makes sense to me. I'm trying to make an individual component (for example a user profile component), but this can obviously be used in multiple parents. If I leave the parent viewname, then the component works individually, but it does not work in a nested situation.

Possible error in React perf test

Hi, I noticed this code in syncing-changes/react.html

var data = generateData(500);
var columns = getColumns();
React.render(
        React.createElement(Table, {people: data, columns: columns}),
        document.getElementById('example')
);

And for syncing-changes/blocks.html

var items = blocks.observable(generateData(100));
blocks.query({
    items: items,
    columns: getColumns()
});

Is this a typo? Seems a bit unfair. After correcting first example to 100, results became indistinguishable for me.

I can't change `<title>` element.

I can't change <title> element and everything in head element.

I propose to make middleware after tryServerPage, not just before it.

blocks.server({
    preMiddleware: [compression()],
    postMiddleware: [changeTitle()]
});

view inside each

Trying to add a view with a separate HTML file and it comes back with the error..

Critical: view(Row) - exception thrown while executing query parameter   <li data-query="view(Row)" data-id="20">

Is this not possible?

{{expressions syntax}} does not work with the style attribute.

The values doesn't seem to be interpolated in the style attribute. However, it works on other attributes (even with a misspelled stye attribute).

<!doctype html>
<html>
  <head>
    <script src="http://jsblocks.com/jsblocks/blocks.js"></script>
    <script>
      window.box = {};
      box.height = blocks.observable("250");
      box.width = blocks.observable("250");
      box.backgroundColor = blocks.observable("#fff");
      blocks.query(box);
    </script>
  </head>
  <body>
    Enter Height: <input type="text" data-query="val(height)" />
    Enter Width: <input type="text" data-query="val(width)" />
    Enter Background Color: <input type="text" data-query="val(backgroundColor)" />
    <h2>Height: {{height}}</h2>
    <h2>Width: {{width}}</h2>
    <h2>Background Color: {{backgroundColor}}</h2>
    <div style="background-color: {{backgroundColor}}; height: {{height}}px; width: {{width}}px;"></div>
  </body>
</html>

Anyway to avoid #hash scrolling to top?

Running the sample shopping application and navigating between boys, girls, babies etc while being lower on the page causes the page to scroll to top.

Is there anyway to prevent this?

Setting up an observable with an initial null value throws an error

  function Model(){
    this.user_input = blocks.observable();
    this.headline = 'Howdy from the application';
  }
  blocks.query(new Model());
  <h1>{{headline}}</h1>
  <input data-query="val(user_input)" placeholder="Enter some text…" size="40"/> <button class="clear">&times;</button>
  <p>{{user_input}}</p>

This works, but it throws an error unless I declare a global variable named user_input. If I put an initial value in like blocks.observable('foo'), then I don't get the error, but I also can't use a placeholder as I would like. I would like the variable to initialize to an empty string if nothing is passed into it by observable.

i18n

Hi Antonio, thanks for this elegant framework. Can you show me a quick way to add

  • i18next
  • auth checks

centrally without breaking antyhing?

Regards,
E.D.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.