GithubHelp home page GithubHelp logo

cyclic-router's Introduction

Cycle.js Community

This organization is a place for any Cycle.js project that welcomes collaboration. We welcome all types of projects, from drivers to games to documentation to art.

The official cyclejs organization contains the code for Cycle.js itself and the core libraries developers might use. (HTTP, DOM, etc). We're here for all of the cool stuff the community makes.

About our community

We are a welcoming, collaborative community. We believe that contributions should be encouraged and celebrated. We especially welcome those who feel like they might not be welcome, or able to help. Come one, come all.

We do not tolerate behaviour that makes other contributors feel harassed or unwelcome. Please see the code of conduct.

In short, don't be a dick and consider how others will perceive your actions. If you experience any negative interactions, please contact either @tylors or @widdershin and we will do our best to help. If in doubt, get in touch.

Getting involved

We welcome contributors of all levels of experience and from all different backgrounds.

We are liberal about membership. If you want to be a member, please open an issue on this repository and we will add you. πŸ˜„

As a member, you are welcome to create repositories in this community and to transfer your existing repos. The only criteria is that they're related to Cycle.js and that collaboration is welcomed.

The most straightforward way to contribute to a repository is to open a pull request. We ask that repositories in our community follow Github flow as using pull requests allows for more review, discussion and learning to take place.

When one of your pull requests is merged, you will be added as a collaborator to the repository. This allows for easier collaboration (as collaborators can work together on pull requests).

Being a collaborator means:

  • You can push to the repo, and to branches.
  • With the permission of the other collaborators, you can merge pull requests.
  • You get to have a say in the development of the project.
  • You have no obligation to contribute, but any work you do is very appreciated.
  • You still work with pull requests, but now you work on the repo itself, not your fork. This makes it easier to collaborate.

Here is an automagically generated list of open, unassigned issues with a "Help Wanted" label. If you want to contribute but you're unsure what to work on, take a look at these issues and comment on any you're interested in.

cyclic-router

cycle-canvas

README

cycle-svg-pan-and-zoom

cycle-regl

cyclic-router's People

Contributors

atomrc avatar greenkeeperio-bot avatar jabx avatar jvanbruegge avatar lius avatar maximilianmikus avatar nakaji-dayo avatar nicoespeon avatar ntilwalli avatar olpeh avatar stevealee avatar tylors avatar wmaurer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cyclic-router's Issues

Labeling edges of inserted trees with component names for use by visualization tool(s)

I have the following idea. When we open a cycle.js visual dev tool, it should be possible to collapse and expand observables' circuits of components. But for this to happen, a visual tool need to know at which point components touch.

This info can be placed by machinery that inserts/remove trees, either taking a name of a component function, that created the tree, and/or by reading annotations on tree-creating function. More so, there can info say at which route (or part of it) has a particular circuit section has been inserted.

Is such labeling done currently? If so, can you point me to code. If not, what do you think about this?

switchpath return type mismatch

switchpath returns this:

export interface SwitchPathReturn {
    path: string | null;
    value: any | null;
}

while cyclic expects this:

export interface RouteMatcherReturn {
    path: string;
    value: any;
}

I'm not deep enough in to know whether this is just a matter of adjusting the type in cycle to keep up with switchpath, or if the possible null return requires more changes.

The parameter route matches the general route.

const match$ = sources.router.define({
  '/main': MainComponent,
  '/:id': id => sources => OtherComponent({ ...sources, id: xs.of(id) })
});

It will return OtherComponent's sinks when URL is '/main'.

Support redirects?

Would be nice to support redirecting, for example my/url to my/url/specific.
Don't think it's possible to signal this with switch-path, so would probably have to be at a higher level than that, possibly an option when defining routes?

export RouteMatcherReturn interface

To fully type an application using cyclic-router, I need to refer to the RouteMatcherReturn interface.

export interface RouteMatcherReturn {
    path: string;
    value: any;
}

This currently requires ugly imports:

import { RouteMatcherReturn } from 'cyclic-router/lib/interfaces';

I think this interface should be re-export this from the facade, for simpler use from application code.

(Related: RouterSource.define currently returns type any - it's really a Stream<RouteMatcherReturn>, right?)

Optimizing component rebuilding

Hi.

Do you think would be a bad idea to place some kind of dropRepeats in routedComponent after the matching to prevent rebuild the page component if the matching return the same?

I think would be a useful in the case you want to update the url (fragment?) but you don't want the page component to be reconstructed.

Investigate isolation instead of `path`

Currently the API is a bit clunky, I always have to look up how to use the router in the README, maybe we can think of an isolation based API that feels more natural.

This issue is as reminder and as start of a discussion

HTTP request send twice if route changed back

I have a strange behavior maybe someone can help.
Currently I have a router component, that is loading all pages.
On the StartPage I have a form that can POST some entries.

Now, if I made an POST request and switched from '/start' to '/feed' and back to '/start' the POST request is send a second time.

const routedComponent = (sources) => ({path, value}) => value({...sources, router: sources.router.path(path)});

const routes = {
    '/start': StartPage,
    '/feed': UnderConstructionPage,
    '*': NotFoundPage,
};

export function Router(sources: AppSources): AppSinks {
 const routes$: Stream<SwitchPathReturn> = sources.router.define(routes);
    const page$ = routes$.map(routedComponent(sources));

    const redirectStartpage$ = sources.router.history$
        .filter(loc => loc.pathname === '/')
        .mapTo('/start');

    return {
        DOM: page$.map(c => c.DOM || xs.empty()).flatten(),
        HTTP: page$.map(c => c.HTTP || xs.empty()).flatten(),
        router: xs.merge(page$.map(c => c.router || xs.empty()).flatten(), redirectStartpage$),
        onion: page$.map(c => c.onion || xs.empty()).flatten(),
    };
}

On the StartPage I have a intent that is listen to a HTTP stream.
And if the response is successfull I show a message.

export function intent(sources: AppSources) {

    const {DOM,HTTP} = sources;

    return {
        newSetClick$: DOM.select(ID_NEW_SET_BTN).events('click').debug('CLICK'),
        refreshSetList$: xs.of(GetSetsApi.buildRequest()),
        newSetResponse$: HTTP.select(PostSetApi.ID).flatten()
    };

}

function reducer(action: any) {

    const initReducer$ = xs.of(function initReducer(state) {
        return {
            showNewCardMessage: false,
            newCardMessage: {}
        }
    });

    const newCardMessageReducer$ = action.newSetResponse$
        .filter(response => response.ok)
        .map(res => ({
            id: res.body._id,
            title: res.body.title
        }))
        .map(res => function cardMessageReducer(state) {
            return {
                ...state,
                showNewCardMessage: true,
                newCardMessage: {
                    id: res.id,
                    title: res.title
                }
            }
        });


    return xs.merge(initReducer$, newCardMessageReducer$);
}

The request is send by Startpage itself but only if the submit button was clicked.
Now if I remove the "Intent" the behavior disapears.

Cycle Unified update

I have a fork which updates to Unified. It requires a PR (cyclejs/cyclejs#552)
to be merged into cyclejs/history, but other than that it works. I'll post the PR when (assuming) that history PR gets merged. You can view/test the changes here: https://github.com/ntilwalli/cyclic-router

The above linked repo includes a cyclejs submodule that needs to be pulled in and switched to the cyclic-history branch for the 'npm install' to succeed.

The update introduces a breaking change in that the create function no longer takes options. You'll have to instantiate your history object externally (where you can apply your options).

Reviews/critiques are welcome.

createHref doesn't behave as expected

I'm posting this as a note of something to possibly update in the future...

The code for path never allows an empty string to be added to the _namespace meaning when trying to construct a path using join there is never a scenario where a '/' is the first character so all constructed paths are relative. In my app I want all the paths to be absolute from root so the createHref functionality feels broken. I'm doing [""].concat(Router._namespace).join('/') directly in my code to create the current relative path. I feel likecreateHref should be changed to use that approach.

TypeScript warnings

When using Typescript with webpack, I get a several warnings from cyclic-router like this:

WARNING in ./~/cyclic-router/lib/index.js
Cannot find source file '../src/index.ts': Error: Cannot resolve 'file' or 'directory' ../src/index.ts in /web-cycle/node_modules/cyclic-router/lib

Can you include the typescript files in the npm package?

Router renders components twice with hash history

With HTML5 History API, the user agent fires a popstate event when the user navigates through their history, whether backwards or forwards, provided it isn’t taking the user away from the current page:
http://html5doctor.com/history-api/#historical-events

As far as I understand it, this means that when using hash history - createHashHistory(), both a PUSH and a POP emitted.

@cycle/history also emits both of these events. You can see this by adding a debug to history$ in RouterSource, e.g. on line 39 of RouterSource.ts (when using the hash history).

However I believe it should be up to cyclic-router to filter out the POPs. The unfortunate consequence of getting both events is that components are rendered twice on every route change (when using hash history).

What do you think @TylorS?

Uncaught TypeError: runSA.remember is not a function

While trying open an application I get following error message in browser console:

makeHistoryDriver.js:42 Uncaught TypeError: runSA.remember is not a function
historyDriver @ makeHistoryDriver.js:42
routerDriver @ makeRouterDriver.js:24
callDrivers @ index.js:21
Cycle @ index.js:73
run @ index.js:61
(anonymous function) @ app.js:33
(anonymous function) @ bundle.js:1042
webpack_require @ bundle.js:556
fn @ bundle.js:87
(anonymous function) @ multi_main:3
(anonymous function) @ bundle.js:586
webpack_require @ bundle.js:556
(anonymous function) @ bundle.js:579
(anonymous function) @ bundle.js:582

running on Windows. Any suggestions what this might be?

Add usage of createHref in README

As a beginner, it is really confusing how to construct links in the DOM. The example in the README is incomplete without a working example of links. I have tried using createHref after doing a lot of searching, but it is not working and I am getting an error like this:

TypeError: Cannot read property '_namespace' of undefined
    at RouterSource.createHref (VM986 bundle.js:3568)
    at HomeComponent (VM986 bundle.js:3432)
    at MapOp.f (VM986 bundle.js:3562)
    at _try (VM986 bundle.js:17778)

My example source code:

import xs from 'xstream';
import { run } from '@cycle/run';
import { makeDOMDriver, div, p, a } from '@cycle/dom';
import { routerify } from 'cyclic-router';
import { makeHistoryDriver } from '@cycle/history';
import switchPath from 'switch-path';

function HomeComponent(sources) {
  const { router: { createHref }} = sources;
  const aboutLink = createHref('/about');
  const vdom$ = xs.of(false).map(() => div([
    p('Home page'),
    a({ attrs: { href: aboutLink }}, 'About')
  ]));

  return {
    DOM: vdom$
  }
}

function AboutComponent(sources) {
  const vdom$ = xs.of(false).map(() => div([
    p('About page')
  ]));

  return {
    DOM: vdom$
  }
}

function main(sources) {
  const pageSink$ = sources.router.routedComponent({
    '/': HomeComponent,
    '/about': AboutComponent
  })(sources);

  return {
    DOM: pageSink$.map(c => c.DOM).flatten(),
    router: xs.of('/')
  };
}

const mainWithRouting = routerify(main, switchPath);

run(mainWithRouting, {
  DOM: makeDOMDriver('#app'),
  history: makeHistoryDriver()
});

Can't match path correctly !

I have a router defined as

const match$ = sources.router.define({
  '/a': AComponent,
  '/aa': AAComponent,
  '/ca': CAComponent
}

This stream will be {path: null, value: null} when path is /aa. But it works when path is /ca.

Problem running example code

I am trying to use cyclic-router and it seems that when a new route event occurs its followed by the default one

function main(sources) {
  const match$ = sources.router.define({
    '/': HomeComponent,
    '/other': OtherComponent
  });

  const page$ = match$.debug().map(({path, value}) => {
    return value(Object.assign({}, sources, {
      router: sources.router.path(path)
    }));
  });

  return {
    DOM: page$.map(c => c.DOM).flatten(),
    router: xs.of('/'),
  };
}

Running this example when I enter the url /other the match$ receives 2 events: The first one is a POP event with the path /other and then a "/" event follows. Am I missing something here?

the object in the first event is a {location: {action: "POP",...}, path: "/other"} then the second event object is {location: {action: "PUSH"}, path: "/"}

Add prettier to repo

Makes formatting easier, as everyone uses a different style.
Simply run npm run format and the code is formatted correctly

I can set that up if I know what your preferences are

Multiple redirects failing in routercomponent

I am currently trying to create a routing component based on the example code and the xstream-boilerplate routing component. I am getting stuck on a particular issue for a redirect function.

export function createRedirect (path) {
  const redirect = (sources) => {
    const vTree = xs.of(div('.redirect', 'Redirecting...'));
    const redirect$ = xs.of(sources.router.createHref(path));
    return {
      DOM: vTree,
      router: redirect$
    }
  }
  return sources => isolate(redirect)(sources);
}

I am using this function to let the '/' state redirect to a substate, so that there is always some url qualified substate for the host page component. So for example '/' redirects to '/qualifiedstate'. And this seems to work pretty well, the main component is always redirecting. However if I want to continue the redirect stream by redirecting '/' in qualifiedstate to 'somesubstate', thus: '/' -> '/qualifiedstate' -> 'qualifiedstate/somesubstate', the redirect seems to stop on the vtree created by the redirect component. The redirect then does work for hrefs aimed at '/qualifiedstate', it's only the initial flow from '/' -> '/qualifiedstate' -> '/qualifiedstate/somesubstate' that seems to fail.

I've added my current implementation to a webpackbin: http://www.webpackbin.com/EJYlChBIb, it seems however that the history package will not load there so I can't guarantee it works.

I am not exactly sure where the issue lies, I find it odd that the DOM sink seems to be returned properly but the router sink doesn't, considering they both use the same stream operator. Any insight into the matter would be greatly appreciated.

Provide a param observable instead of value

An example from readme:

const routes = {
  '/:id': id => sources => YourComponent({props$: Observable.of({id}), ...sources})
}

Here, whenever the id is changed, a new YourComponent is created. It would be nicer to have something like

const routes = {
  '/:id': param$ => sources => YourComponent({param$, ...sources})
}

Where param$ emits values of form {id}

Component runs twice, isolated child components lose scope/namespace

I have an app with a bunch of nested components and have found that when I use cyclic-router, the component that's being loaded (Welcome) gets called twice, causing isolated child components with implicit scopes (e.g. export default sources => isolate(UserProfile)(sources)) lose track of their scope because it seems the scopes are recalculated when the component is reran.

If you git clone https://github.com/wyqydsyq/hapi-cycle, run npm install && npm run launch and then visit http://localhost:1337/ you can see the UI in this case works fine, but if you then swap the part of https://github.com/wyqydsyq/hapi-cycle/blob/master/src/ui/app.js that returns just the component with the commented out lines to make it use cyclic-router instead of calling the component directly, subcomponents will no longer emit/recieve events from/to themselves.

Using explicit scopes seems to remedy this, as each isolated child component would be using the same scope string in between calling the main component, but that's more of a band-aid fix than a solution.

So the working/not working cases for me are:

  • βœ… Router with explicit isolation scope on child components
  • βœ… No router (calling component directly) with implied isolation scope on child components
  • ❎ Router with implied isolation scope on child components

How to create a nested route?

const match$ = sources.router
    .define({
      '/': Home,
      '/login': Login,
      '/products': ProductsIndex,
      '/products/:productId': id => s => ProductDetails({ ... }),
    });

Question: its possible to make a Link component?

I'm wondering if with cyclejs is possible to craft a generic Link component that accept the route from the props, and manage the click event handler internally.

I saw the dynamic change route section on the readme, but it seems ugly to me as you need to create a class for each link, and map them all on the router: xs.merge ... thing.

CaptureClicks() doesn't work

It says the history driver is proxied, and yet it's not behaving as it should. Can this be fixed or should I change to using dynamic routing?

After a while the router becomes too slow - rxjs

Hi folks!
I'm using cyclic-router on a cycle/rxjs app. The router is working, but, after some clicks (navigation) this becomes too slow, I mean, im not doing anything else but clicking the a tags i've added.

I created this sample repo to repro the issue: clone, npm install -> npm start -> navigate between the two pages -> checkout how this become "slow"

am i missing something?

Router outlet component?

Consuming cyclic-router is a relatively manual process, as shown in the README example. It provides mostly a driver. What are the thoughts on the idea of providing a higher level component, a "router outlet"? I have the following relatively simple implementation, which could be enhanced with something about parameters then land in a PR, if it seems likely to be considered and merged. Thoughts?

// Cycle cyclic-router RouterOutlet component This implements a relatively naive
// router outlet component, which pre-creates instances of all the destination
// route components, and supports only simple non-parameterized routing so far.

// Requires that incoming sources include a cyclic-router called router.
// Requires that application will consume a VDOM sink called DOM.

import xs, { Stream } from 'xstream';
import { VNode } from '@cycle/dom';
import isolate from '@cycle/isolate';
import { RouterSource, RouteMatcherReturn } from 'cyclic-router';

export interface RouteDef {
  urlPath: string;
  label: string;
  cssClass: string;
  componentFn: any;
}

interface Sources {
  router: RouterSource;
}

interface Sinks {
  DOM: Stream<VNode>;
}

export function RouterOutlet<S extends Sinks>(sources: Sources, defs: RouteDef[], sinkNames: string[]): S {

  const defsWithComps = defs.map(def => {
    const comp = isolate(def.componentFn)({
      ...sources,
      router: sources.router.path(def.urlPath) // scoped router
    });
    return { ...def, comp };
  });

  // Pass through all the non-DOM outputs

  const passThroughSinks = sinkNames
    .map(n => ({
      [n]: xs.merge(...defsWithComps
        .map(def => def.comp[n] || xs.never()))
    }))
    .reduce(Object.assign, {});

  // Switch between component DOM outputs:

  const match$ = sources.router.define(
    defsWithComps.reduce((result, item) => ({
      ...result,
      [item.urlPath]: item.comp
    }), {}));

  const dom$ = match$.map((m: RouteMatcherReturn) => m.value)
    .map((c: Sinks) => c.DOM).flatten();

  return {
    ...passThroughSinks,
    DOM: dom$
  };
}

I can't run example in readme

I'm a beginner in cyclejs( in nodejs too). And i can't run example in readme. I installed all nedded packages, but i see an error "makeHistoryDriver throws error for a proper history object...". Can you correct example in readme and explain me why this error causes? Sorry for maybe dumb question.

Document Isomorphic/Universal usage

I'm still new to Cycle.js so I apologize for my noobiness but how would one go about rendering a Cycle app with cyclic-router by using the req.url (in an express app, for instance)? I can't figure out where to pass the req.url string into the makeRouterDriver function in order to make Cycle render the correct page of my app. Thanks!

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.