Coder Social home page Coder Social logo

ngvue's Introduction

ngVue

⚠️ For Vue 3 support use ngVue3 ⚠️

Github | npm

Build status JavaScript Style Guide npm version Monthly npm downloads code style: prettier

VueJS is a library to build web interfaces with composable view components and reactive data binding. ngVue, inspired by ngReact, is an Angular module that allows you to develop/use Vue components in AngularJS applications. ngVue can be used in the existing Angular applications and helps migrate the view parts of the application from Angular 1.x to Vue 2.

The motivation for this is similar to ngReact's:

  • The AngularJS application suffers from a performance bottleneck due to a huge amount of scope watchers on the page, but VueJS offers an amazing reactive data-binding mechanism and other optimizations
  • Instead of two-way data flow between controllers and views, VueJS defaults to a one-way, parent-to-child data flow between components which makes the application more predictable
  • VueJS offers a much easier way to compose the web interfaces, and you can take advantage of the functional reactive programming in VueJS 2. Angular directives introduce a high learning barrier, such as the compile and link function, and the directives are prone to get confused with the components
  • The VueJS community offers a component or a UI framework that you would like to try out
  • Too deep into an AngularJS application to move it away from the code but you would like to experiment with VueJS

Table of Contents

Install

via npm:

npm install ngVue

Usage

ngVue is a UMD module (known as Universal Module Definition), so it's CommonJS and AMD compatible, as well as supporting browser global variable definition.

First of all, remember to load AngularJS 1.x, VueJS and ngVue:

// load on the page with the `script` tag
<script src="./node_modules/angular/angular.js"></script>
<script src="./node_modules/vue/dist/vue.js"></script>
<script src="./node_modules/ngVue/build/index.js"></script>

or ...

// the CommonJS style
const ng = require('angular')
const vue = require('vue')
require('ngVue')

// the AMD style
require(['angular', 'vue', 'ngVue'], function(ng, Vue) {
})

// the ECMAScript module style
import angular from 'angular'
import vue from 'vue'
import 'ngVue'

and then require ngVue as a dependency for the Angular app:

angular.module('yourApp', ['ngVue'])

Features

ngVue is composed of a directive vue-component, a factory createVueComponent and a directive helper v-directives. It also provides some plugins for enhancement.

  • vue-component is a directive that delegates data to a Vue component so VueJS can compile it with the corresponding nodes
  • createVueComponent is a factory that converts a Vue component into a vue-component directive

ngVue does support VueJS directives but currently they only work with a Vue component in AngularJS templates.

  • v-directives is a directive to apply the vue directives to the vue components
<!-- This won't work -->
<div v-directives="hello"></div>

<!-- But this will work ... -->
<vue-component name="HelloComponent" v-directives="hello"></vue-component>
<!-- Or ... -->
<hello-component v-directives="hello"></hello-component>

The vue-component directive

The vue-component directive wraps the vue component into an angular directive so that the vue component can be created and initialized while the angular is compiling the templates.

At first an Angular controller needs creating to declare the view data like this:

const app = angular.module('vue.components', ['ngVue'])
  .controller('MainController', function () {
    this.person = {
      firstName: 'The',
      lastName: 'World'
    }
  })

Then declare a Vue component like this:

const VComponent = Vue.component('hello-component', {
  props: {
    firstName: String,
    lastName: String
  },
  render (h) {
    return (
      <span>Hi, { this.firstName } { this.lastName }</span>
    )
  }
})

In the end, register the Vue component to the Angular module with value method like this:

app.value('HelloComponent', VComponent);

Now you can use hello-component in Angular templates:

<body ng-app="vue.components">
  <div class="hello-card"
       ng-controller="MainController as ctrl">
    <vue-component name="HelloComponent"
                   v-props="ctrl.person"
                   watch-depth="value" />
  </div>
</body>

The vue-component directive provides three main attributes:

  • name attribute checks for Angular injectable of that name

  • v-props attribute is a string expression evaluated to an object as the data exposed to Vue component

  • v-props-* attribute allows you to name the partial data extracted from the angular scope. vue-component wraps them into a new object and pass it to the Vue component. For example props-first-name and props-last-name will create two properties firstName and lastName in a new object as the component data

<vue-component v-props="ctrl.person" />
<!-- equals to -->
<vue-component v-props-first-name="ctrl.person.firstName" v-props-last-name="ctrl.person.lastName" />
  • watch-depth attribute indicates which watch strategy AngularJS will use to detect the changes on the scope objects. The possible values as follows:
value description
reference (default) watches the object reference
collection (rarely used) same as angular $watchCollection, shallow watches the properties of the object: for arrays, it watches the array items; for object maps, it watches the properties
value (rarely used) deep watches every property inside the object

NOTES

  • watch-depth cannot propagate all the changes on the scope objects to VueJS due to the limitation of the reactivity system, but you can read about several solutions in Caveats.
  • The collection strategy and the value strategy are rarely used. The scope object will be converted into a reactive object by VueJS and so any changes on the reactive scope object will trigger the view updates.
  • The value strategy is not recommended because it causes a heavy computation. To detect the change, Angular copies the entire object and traverses every property insides in each digest cycle.

Handling events

Events can be handled from Vue to AngularJS components by binding functions references as v-on-*:

app.controller('MainController', function ($scope) {
  this.handleHelloEvent = function (greetings) {
    console.log(greetings); // "Hello, World!"
  }
})
<vue-component v-on-hello-world="ctrl.handleHelloEvent"></vue-component>
const VComponent = Vue.component('hello-component', {
  methods: {
    helloFromVue() {
      this.$emit('hello-world', 'Hello, World!');
    }
  },
  render (h) {
    return (
      <button onClick={this.helloFromVue}></button>
    )
  }
})

Events name in $emit function should be using kebab-case syntax.

Handling HTML attributes

Just like regular Vue components, you can pass HTML attributes from the parent Angular component to your Vue component. The parent's class and style attributes will be merged with the corresponding Vue component attributes, while others will be passed down unless they conflict with attributes in the Vue component's own template. Keep in mind that when you pass down literal strings for anything other than class and style attributes, they must be surrounded by quotes, e.g. data-value="'enabled'".

angular.module("app")
  .directive("myCustomButton", createVueComponent => {
    return createVueComponent(Vue.component("MyCustomButton", MyCustomButton))
  })
<my-custom-button
  disabled="ctrl.isDisabled"
  class="excellent"
  tabindex="3"
  type="'submit'"
  v-props-button-text="'Click me'" />
<template>
<!-- tabindex, type, class, and disabled will appear on the button element -->
<button>
  {{ buttonText }}
</button>
</template>

<script>
export default {
  name: "my-custom-button",
  props: ["buttonText"],
}
</script>

Note that using inheritAttrs: false and binding $attrs to another element is also supported:

<template>
<div>
  <!-- tabindex and type should appear on the button element instead of the parent div -->
  <button v-bind="$attrs">
    {{ buttonText }}
  </button>
  <span>Other elements</span>
</div>
</template>

<script>
export default {
  inheritAttrs: false,
  name: "my-custom-button",
  props: ["buttonText"],
}
</script>

The createVueComponent factory

The createVueComponent factory creates a reusable Angular directive which is bound to a specific Vue component.

For example, with the vue-component directive as mentioned above, you have to register each Vue component with a string name and then write <vue-component name="TheRegisteredName"></vue-component> repeatedly. With this createVueComponent factory, however, you can create <name-of-vue-component></name-of-vue-component> directives and simply apply the exported Vue components to them.

The factory returns a plain $compile options object which describes how to compile the Angular directive with VueJS, and it takes the Vue component as the first argument:

app.directive('helloComponent', function (createVueComponent) {
  return createVueComponent(VComponent)
})

Alternatively, the name of the Vue component registered by angular.value can also be provided to the factory:

app.directive('helloComponent', function (createVueComponent) {
  return createVueComponent('HelloComponent')
})

Composition API

The Vue 2 Composition API plugin is supported with the usual limitations, additionally the component MUST include a setup function, there isn't a good way to detect it otherwise.

Uses Composition API

export default Vue.component('my-component', {
  setup() {},
  render(h) {
    return (<div></div>)
  }
})

Fall back to Options API

export default Vue.component('my-component', {
  render() {
    return (<div></div>);
  }
});

You don't necessarily need to use Vue.component when defining Composition API components but without a setup method ngVue will not be able to link it.

This works

export default {
  setup() {},
  render(h) {
    return (<div></div>)
  }
}

This will break

export default {
  render(h) {
    return (<div></div>)
  }
}

Caveats

ngVue brings the reactivity system to the dirty checking mechanism and you should therefore understand how they work together and how to avoid some common gotchas. Please read more in the Caveats.

Plugins

ngVue is simple but flexible for enhancement. Please read more in the Plugins.

TODO

  • vue components
  • vue directives
  • unit tests
  • docs + simple examples
  • ng filters in VueJS
  • supports vuex
  • performance optimization

ngvue's People

Contributors

chearon avatar dependabot[bot] avatar dorayx avatar erickwilder avatar greenkeeper[bot] avatar jaredmcateer avatar minigod avatar nicolaspayot avatar pcable avatar pkaminski avatar qingwei-li avatar scottmmjackson avatar sinh117801 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  avatar

ngvue's Issues

Crash when Vue component element is not empty

If you try to put any content into a Vue component's element in your Angular template, ngVue crashes here because it can't find the __slot__ reference.

I'm guessing that element content is supposed to be compiled by Angular and passed as a slot value down to the Vue component, but this doesn't appear to be documented and isn't working for me.

v-directive not updating value

I'm working on a global vue directive and would like to update it's value from an angular property but it doesn't seem like ngVue is watching the value and always sets the value to whatever the initial value was when the vue component was created. I've worked out a pretty minimal test case overwriting the create-vue-component example to show what I am trying to do.

In hello-component I try to set the value using v-directives and in hello-component2 I set it using v-disable just to demonstrate what the desired effect I am trying to achieve.

index.js

import angular from 'angular'
import Vue from 'vue'
import '../../src/index.js'

function componentDisabled(el, binding) {
  if (binding.oldValue === binding.value) return;

  if (binding.value === true) {
    el.querySelectorAll('input, button').forEach(inputEl => {
      inputEl.setAttribute('disabled', 'disable');
      inputEl.classList.add('component-disabled');
    });
  } else if (binding.value === false) {
    el.querySelectorAll('input, button').forEach(inputEl => {
      inputEl.removeAttribute('disabled');
      inputEl.classList.remove('component-disabled');
    });
  }
}

Vue.directive('disabled', {
  inserted: componentDisabled,
  componentUpdated: componentDisabled,
});

const VueComponent = Vue.component('hello-component', {
  props: {
    firstName: String,
    lastName: String,
  },
  render () {
    return (
      <div>
        <input type="text" value={this.firstName} />
        <input type="text" value={this.lastName} />
        <button>Click me!</button>
      </div>
    )
  }
})

const VueComponent2 = Vue.component('hello-component2', {
  props: {
    firstName: String,
    lastName: String,
  },
  data() { return {isDisabled: true}; },
  render () {
    return (
      <div>
        <hello-component first-name={this.firstName} last-name={this.lastName} v-disabled={this.isDisabled}></hello-component>
        <button onClick={() => this.isDisabled = !this.isDisabled}>Click to {this.isDisabled === true ? 'enable' : 'disable'}</button>
      </div>
    )
  },
})

angular
  .module('vue.components', ['ngVue'])
  .controller('MainController', function () {
    this.person = {
      firstName: 'Jake',
      lastName: 'Sully',
    }

    this.isDisabled = true;
  })
  .directive('helloComponent', function (createVueComponent) {
    return createVueComponent(VueComponent)
  })
  .directive('helloComponent2', function (createVueComponent) {
    return createVueComponent(VueComponent2)
  })
;

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Hello, World</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.7/css/materialize.min.css">
  <style>
    html, body {
      width: 100%;
      height: 100%;
    }
    body {
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .hello-card {
      width: 500px;
    }
  </style>
</head>
<body ng-app="vue.components">
  <div class="hello-card" ng-controller="MainController as ctrl">
    <h2>Angular v-directive</h2>
    <hello-component 
      v-props="ctrl.person"
      watch-depth="value"
      v-directives="{name: 'disabled', value: ctrl.isDisabled }"
    ></hello-component>
    <button ng-click="ctrl.isDisable = !ctrl.isDisabled">Click to {{ctrl.isDisabled ? 'enable' : 'disable'}}</button>
    <hr>
    <h2>Vue custom directive</h2>
    <hello-component2 v-props="ctrl.person" watch-depth="value" ></hello-component2>
  </div>
  <script src="/assets/create-vue-component.build.js"></script>
</body>
</html>

directives

Cannot render more than one vue component.

Hi guys,

I'm triyng ngVue in a simple testing app, and i'm getting this error when more than one component was added to the view.

[Vue warn]: Error in mounted hook: "TypeError: Cannot read property 'parentNode' of undefined"
vue.js:1743 TypeError: Cannot read property 'parentNode' of undefined

This is my code:

<body ng-app="webApp">
      <div ng-controller="TestCtrl" class="test-controller">
        <h2>This is Ng Controller</h2>
        <pre class="ng-data">
            <h3>Ng Scope</h3>
            <div>Counter: {{counter1}}</div>
            <div>Name: {{name}}</div>
            <div>Items: {{items}}</div>
        </pre>
        <vue-component name="HelloComponent" vprops-name="name" />
        <vue-component name="HelloComponent" vprops-name="name" />
      </div>
<body>   
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.25/angular.min.js"></script>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="./node_modules/ngVue/build/index.js"></script>

No matter if it's the same component or another one. What i'm doing wrong?

Thanks!

Issue with webpack and the render method

Hey there, I'm having an issue getting the vue component to pass in my webpack build, it is currently triggering an Unexpected token error.

Here is the component:
var VDataList = Vue.component('v-process-list', { props: { data: String }, render (h) { return ( <div>This is the VueJS Component, data received: {this.data}</div> ) } })

I think add this to the app:
.value('VDataList', VDataList);

And finally in my directive I invoke the component:
<vue-component name="VDataList" vprops="testData"></vue-component>

I'm suspicious I'm missing something in my webpack config, any help is greatly appreciated.

Vue3 support

With the Vue 3 release on the immediate horizon should probably check to see if we can support it. I'll try to carve out some time to take a look but help is greatly appreciated.

Embrace semantic release

I've been bumping versions by hand but I think this is really error-prone and at some point (we're humans) something can be bumped as a patch while it would really deserve a minor or even major version bump. This way the project can better follow semantic versioning and the maintainers don't need to release packages by hand ;)

https://semantic-release.gitbook.io/semantic-release/

Vue component not rendering on IE

I am using a Vue component. It works fine with chrome browser but on IE it is never rendered.

TypeError: Object doesn't support property or method 'assign'

An in-range update of rollup is breaking the build 🚨

Version 0.41.3 of rollup just got published.

Branch Build failing 🚨
Dependency rollup
Current Version 0.41.2
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As rollup is “only” a devDependency of this project it might not break production or downstream projects, but “only” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this 💪


Status Details
  • continuous-integration/travis-ci/push The Travis CI build failed Details
Commits

The new version differs by 3 commits .

  • 4bbf9fd -> v0.41.3
  • 603d2e7 Merge pull request #1260 from rollup/gh-1258
  • 3a8b1c1 dont treat this.foo as possible namespace - fixes #1258

See the full diff.

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Example not running

Hello,

Trying to figure out how to use ngVue. Cloned and ran npm install but says that there is no script for npm run dev-example and npm run dev... Any suggestions?

/Users/miguelbrito/.npm/_logs/2018-03-15T14_06_02_539Z-debug.log

Time to remove that deprecation message?

@jaredmcateer is it possible to suppress teh the camelCase deprecation warning message?

camelCase syntax for events name (in $emit function) will be deprecated in a future release.
Please, make sure to use kebab-case syntax when emitting events from Vue.

It's pretty annoying, it pops up in all my playwright test cases.

Angular component inside Vue component/template

Hi, and thank you for your work!
I am trying to migrate a huge angular 1.x app into vue. I was wondering: is it possible to include an angular component into vue code?
I would like to re-use an existing angular component inside code refactored through Vue/ngVue, because I cannot simply rewrite it at the moment.
Let's say I am rewriting the "routing wrapper" of my application, but still, as the component loaded for a given route, I would like to inject the old angular code.

Something like:

<old-angular-app>
    <ng-vue-component>
        <old-existing-angular-component />
    </ng-vue-component>
</old-angular-app>

But it does not work, it gives a vue warn on unregistered component, which, I guess, is the right behaviour of the framework.
I know that maybe ngVue was intended to include vue components into angular, and not the contrary.
But I was wondering if it's possible to fix that in some way.

Usefulness of $compile for slot

Hi,

I'm playing with ngVue and I am trying to understand why we need to do a $compile in the rootProps.mounted in the ngVueLinker function.

Concretely, I have a vue component MyComponent with a slot. The content of the slot contains some `ng-if``s.

  <MyComponent>
    <span>
      <div ng-if="true">TRUE</div>
      <div ng-if="false">FALSE</div>
      <div ng-if="true">TRUE</div>
      <button ng-click="vm.onClick()">Click me!</button>
    </span>
  </MyComponent>

With the $compile used the ng-ifs are not displayed (line 177, https://jsfiddle.net/6jnL9sua/2/)
Without the $compiler used the ng-ifs are displayed (line 178, https://jsfiddle.net/6jnL9sua/3/)
Same behaviour happen with ng-repeat.

If I put a angular component inside the slot, the component is rendered with and without the $compile. If the component has some ng-if or ng-repeat inside they are interpreted either way.

  <MyComponent>
    <span>
      <ng-my-awesome-component></ng-my-awesome-component>
    </span>
  </MyComponent>

So concretely, I don't understand in which case the $compile is useful.

Thank you

P.S. Thank korya for the JSFiddle :p
P.S. Don't hesitate to change the title of the issue if it is not meaningful

$compile('<my-component/>') doesn't work

If my markup contains <my-component/> the component will be rendered correctly. However if I call the angular $compile method

$compile('<my-component/>')($rootScope).html() I get an empty string.

After considerable frustration I found that wrapping it with a div works.

Raising this for the attention of others who might be so unfortunate.

vueInstance is retained in memory, causing leaks in large apps

I've been investigating memory leaks in my app, I've found that in some cases the vueInstance will be retained in memory because of the $destroy event listener. These leaks aren't explicitly the fault of ngVue, but can easily be solved by a small change. I want to set the vueInstance to null when the scope is destroyed. This will allow it to be released to memory.

I will also suggest to use angular.element().remove() to remove the $el element. Since Angular uses jQlite (or JQuery), it caches information of various elements in the DOM. That cached data only gets released if the proper jQuery remove method is called.

Omit angular from build

Due to the environment of the SPA I am developing, AngularJS is included per script tag by default.
However, I have no option of including ngVue as script tag.
Is there a way to initialize ngVue using require without also importing angular?

Console throws:

WARNING: Tried to load angular more than once.

How does event handling work

First off, thank you for this very cool piece of software!

I am kind of stuck on how I would handle a simple click event with ngVue.
I can not find anything about this in the docs.

How is the data flow from the vue component upwards to the angular controller meant to work?

Issue processing props

I ran into an interesting issue with the following code:

<my-vue-comp v-prop-states="ctrl.states"></my-vue-comp>

When trying to load the app the console outputs the error:

logger.js:108 2019-03-07T22:40:23.908Z ERROR browser-console [Vue warn]: Missing 
required prop: "states"

found in

---> <Default1> at client/ui-components/MyVueComponent.vue
       <Root>

Stepping through the code I found that the reactive data object being passed to the vue instance constructor had a props property named tates instead of states. I finally noticed my typo that the prop in v-prop-states was singular and needed to be plural, but ngVue was parsing it as vPropsTates.

Support multiple children in <slot>

For example:

<vue-component name="SomeComponent">
  <label for="name">Name</label>
  <input id="name" type="text" />
</vue-components>

does not currently work in ngVue 1.7.8

I've implemented the above multiple-child support in slots in a fork, and tested it. Will open a PR in the next few hours if all goes well! 🙂

Side note:
I saw that slots might be deprecated in #66, not sure what the status of slots is right now? I don't see anything listed in Caveats

Deprecation warning for camelCase event names cannot be suppressed

'camelCase syntax for events name (in $emit function) will be deprecated in a futur release.\nPlease, make sure to use kebab-case syntax when emitting events from Vue.'

This warning was introduced in #83 - however, it cannot be suppressed.

A common pattern is to use NodeJS-style environment variables which can be configured in webpack:

if (process && process.env && process.env.NODE_ENV === 'development'`) {
   logger.warn('Be sure to do these things before this project is shipped')
} else {
   // be silent
}

https://webpack.js.org/plugins/environment-plugin/

Reproduce:

(1) Include ngVue in a productionized project, either using a <script> tag or importing using ES6 modules.

Expected:

  • No deprecation warning occurs merely by including the code

Actual:

  • Deprecation warning occurs merely by including the code.

Plugins do not work with Webpack 5

Webpack 5 now supports the exports option in package.json. As a result, we can't import anything except:

    ".": {
      "require": "./build/index.js",
      "import": "./build/index.esm.js"
    }

This means we can't do import 'ngVue/build/plugins.js'; as Webpack gives the error:

Module not found: Error: Package path ./build/plugins.js is not exported from package /Users/silas.rioux/code/priori_legal/node_modules/ngVue (see exports field in /Users/silas.rioux/code/priori_legal/node_modules/ngVue/package.json)

Can you please add that plugin.js file as an allowable export?

Attach and render Vue component with `component` method (AngularJS 1.5+)

Currently we can use Vue components with vue-component directive and createVueComponent factory. It would be awesome if we could attach and render a Vue component with component method from AngularJS 1.5+.

A case where this would be very useful is with ui-router v1, where states can be defined this way:

const state = {
  name: 'home',
  component: HomeComponent // or 'HomeComponent' if registered
}

This isn't possible to date for HomeComponent to be registered as a Vue component.

Console warning when changing props in Angular controller

I'm trying to trigger one of my Vue component to update when something happens on the Angular side. I don't think I can reference Vue component's method in Angular controller so I'm doing a watch on one of the props to trigger the update. It is working but I'm getting a console warning about avoid mutating prop directly. Is that something expected when we modify props on Angular side?

Sorry in advance if I missed something obvious here.

Incorrect main/module/exports in `package.json` in v2.1.1

The fix of #143 brings a new bug, when I trying to build the project, the following message emerges:

[commonjs--resolver] Failed to resolve entry for package "ngVue". The package may have incorrect main/module/exports specified in its package.json: No known conditions for "." entry in "ngVue" package
error during build:
Error: Failed to resolve entry for package "ngVue". The package may have incorrect main/module/exports specified in its package.json: No known conditions for "." entry in "ngVue" package

The current package.json is incorrect as follows:

{
  "name": "ngVue",
  "author": "Doray Hong <[email protected]>",
  "version": "2.1.1",
  "description": "Use Vue Components in Angular 1.x",
  "main": "build/index.js",
  "module": "build/index.esm.js",
  "exports": {
    "*": {  // <------------------------ SHOULD BE "."
      "require": "./build/index.js",
      "import": "./build/index.esm.js"
    }
  },
  // ...
}

Change "*" to "." will fix this problem.

Current workaround is the same approach as in #143

Problems with slots in wrapped components

I have a simple Vue component with a default slot. I create an AngularJs wrapper using createVueComponent and then use the wrapper in AngularJS app. A simplified version of my setup is shown below.

const customLink = Vue.component('CustomLink', {
  template: '#custom-link',
})

angular.module('app', ['ngVue']).directive(
  'ngCustomLink',
  createVueComponent => createVueComponent(customLink),
);

angular.element(() => {
  angular.bootstrap(document.getElementById('app'), ['app']);
});
<div id="app">
  <p>
    No children (no text): <ng-custom-link></ng-custom-link>  
  </p>
  <p>
    Expected to see the default text: <i>Default Text</i>
  </p>
  <hr>

  <p>
    Text child (fails): <ng-custom-link>Raw text</ng-custom-link>
  </p>
  <p>
    Expected to see: <i>Raw text</i>
  </p>
  <hr>

  <p>
    Span child (ok): <ng-custom-link><span>span text</span></ng-custom-link>
  </p>
  <p>
    Expected to see: <i>span text</i>
  </p>
</div>

<template id="custom-link" type="x-template">
  <a href="#">
    <slot>Default Text</slot>
  </a>
</template>

The setup is available at https://jsfiddle.net/nqr876os/5/

There are 3 problems:

  1. When the AngularJS wrapper has a single child that is a text Node, an exception is thrown and the component is not rendered at all.
  2. When the AngularJS wrapper has no children, the default slot of the original Vue component is not displayed.
  3. When the AngularJS wrapper has multiple children, only the first one is displayed.
  • VueJS is v2.6.6
  • Angular is 1.6.6
  • ngVue is 1.6.0

slots are not rendered

HTML content between Vue component tags (within AngularJS template) is not rendered but only displayed as text.

For example, given a Vue component HelloComponent with a template defined as such:

<div>
  Hello, World!
  <slot></slot>
</div>

and with the following use case:

<vue-component name="HelloComponent">
  <button>Click me!</button>
</vue-component>

will be rendered and displayed as:

Hello, World!
<button>Click me!</button>

instead of the text Hello, World! and an actual Click me! button.

.sync modifier

Hi,

I would just like to ask if the .sync modifier is available or something of the equivalent?

In addition, is it possible to $emit from the Vue component to the Angular component with the name of the event containing colon : in the name?

Attributes on ngVue component don't get transferred to Vue component

It looks like ngVue currently doesn't support passing generic attributes to the Vue instance as $attrs. As far as I can tell, the code only extracts values that are of the following:

const exprKeys = {
  props: 'vProps',
  data: 'vData',
  on: 'vOn'
}

I'm trying to support this use case described in the Vue docs:

By default, parent scope attribute bindings that are not recognized as props will “fallthrough” and be applied to the root element of the child component as normal HTML attributes.
When authoring a component that wraps a target element or another component, this may not always be the desired behavior. By setting inheritAttrs to false, this default behavior can be disabled. The attributes are available via the $attrs instance property (also new in 2.4) and can be explicitly bound to a non-root element using v-bind.

Note: this option does not affect class and style bindings.

In my particular case, I'm trying to pass generic attributes like tabindex and type to the button nested in my Vue component as shown here:

<my-custom-button tabindex="3" type="submit" v-props-button-text="'Click me'" />
angular.module("app")
  .directive("myCustomButton", createVueComponent => {
    return createVueComponent(Vue.component("MyCustomButton", MyCustomButton))
  })
<template>
<div>
  <!-- tabindex and type should appear on the button -->
  <button v-bind="$attrs">
    {{ buttonText }}
  </button>
  <span>Other elements</span>
</div>
</template>

<script>
export default {
  inheritAttrs: false,
  name: "my-custom-button",
  props: ["buttonText"],
}
</script>

Links:
inheritAttrs: https://vuejs.org/v2/api/#inheritAttrs
$attrs: https://vuejs.org/v2/api/#vm-attrs

If you think this feature can be added, I should have some time soon to open a pull request. Thanks!

Parent callback called in the context of Vue child component

Description

I ran into an issue that I think should be handled by ngVue. I have a parent Angular component which passes a function, close, to a child Vue component. If the close callback is called from within Vue, i.e. from clicking the p tag, then the callback is called in the context of Vue, meaning this points to the wrong object. When close is called from Vue, this.ReportingService and this._modalInstance are both undefined. Of course, if close is called from within Angular, everything is fine.

I was able to temporarily solve this by binding the function to the Angular context with:
this.close = this.close.bind(this);

However, it seems like if you're trying to communicate back up to a parent component and calling it's callback, it should be in that parent's context. Would this be something that ngVue can take care of? For example, we can look at the props passed from Angular to Vue, and if the prop is of type Function, it should be bound to the context from where it was passed.

Relevant code

Angular Component

class ModalController {
  constructor(ReportingService) {
    this.ReportingService = ReportingService;
  }

  $onInit() {
    // Without this line, the code breaks as described above
    // Since this function is also called in the Vue context, bind to this controller
    this.close = this.close.bind(this);
  }

  close(reason) {
    this.ReportingService.report(`Closing modal for reason: ${reason}`);
    this._modalInstance.dismiss();
  }
}
modal-body(vprops-close-modal="$ctrl.close")

Vue Component

.modal-body
    //- omitted code
    p(@click="onClose('skip this step')") Skip this step
export default {
  props: {
    closeModal: Function,
  },
  methods: {
    onClose(reason) {
      this.closeModal(reason);
    },
  },
}

Support for composition api?

In preparing for Vue 3 I am trying to utilise the @vue/composition-api package but it seems ngVue doesn't recognize this api. Here's my component

<template>
  <div class="ps">
    <div class="ps__spinner" :class="{ '.ps__spinner--spin': inProgress }"></div>
  </div>
</template>

<script lang="ts">
import { createComponent, computed } from "@vue/composition-api";
import store from "@client/store";

export default createComponent({
  setup() {
    const inProgress = computed(() => store.getters.standBy.inProgress);
    return { inProgress };
  },
});
</script>

Using

import ProgressSpinner from './progress-spinner.vue';

angular
.directive('progressSpinner'. ['createVueComponent', c => c(ProgressSpinner)];

resulted in an error

logger.js:109 2020-02-05T21:03:23.027Z ERROR browser-console Error: [$injector:unpr] Unknown provider: [object Object]Provider <- [object Object]
https://errors.angularjs.org/1.7.5/$injector/unpr?p0=%5Bobject%20Object%5DProvider%20%3C-%20%5Bobject%20Object%5D
    at https://dev.arterys.com/vendors.js:15125:12
    at https://dev.arterys.com/vendors.js:19892:19
    at Object.getService [as get] (https://dev.arterys.com/vendors.js:20052:32)
    at https://dev.arterys.com/vendors.js:19897:45
    at Object.getService [as get] (https://dev.arterys.com/vendors.js:20052:32)
    at https://dev.arterys.com/vendors.js:143690:73
    at ngVueLinker (https://dev.arterys.com/vendors.js:143691:10)
    at Object.link (https://dev.arterys.com/vendors.js:143788:21)
    at https://dev.arterys.com/vendors.js:16352:18
    at invokeLinkFn (https://dev.arterys.com/vendors.js:26222:9) <progress-spinner>

I then tried

import ProgressSpinner from './progress-spinner.vue';

angular
  .value('ProgressSpinner', ProgressSpinner)
  .directive('progressSpinner'. ['createVueComponent', c => c('ProgressSpinner')];

And that at least got angular happy but vue complained about this

logger.js:109 2020-02-05T20:54:05.380Z ERROR browser-console [Vue warn]: Property
 or method "inProgress" is not defined on the instance but referenced during 
render. Make sure that this property is reactive, either in the data option, or 
for class-based components, by initializing the property. See: 
https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.

found in

---> <ProgressSpinner> at client/ui/progress-spinner.vue
       <Root>

So it seems that the setup function isn't being run or something. I would be happy to work on this with anyone willing to guide me since it seems that you've moved on from this project. I'm just hoping to get a little more out of it since the migration of angular to vue is probably going to take us right up to end of life of angular. Thanks!

`process` is not defined in all build environments

A recent change (#82) added a check on process.env to lib/logger.js, but this is not defined in all build environments and will cause a crash at runtime in that case. You probably need to check typeof process first to see if it's defined at all.

Quirk Mode doesn't work.

We are currently in the process of migrating from AngularJS to Vue 2 and we have a use case where we have to dynamically add a new property to the reactive object as mentioned in Quirk Mode in ngVue. Unfortunately this didn't help to solve the problem. Can you please check and verify if this is actually supported by ngVue?

Vue-apollo provider doesn't work

I have an issue using $ngVueProvider to register an apolloProvider instance.

It seems the provider is correctly passed as a prop to the Vue instance, but Vue is not registering it and it is not accessible from my ngVue vue components.

Any idea why it would be the case?

I tested my setup on a clean vuejs project and vue-apollo works like a charm, my best guess is that the issue is related to the ngVueLinker and the vue instance props?

Mount Vue in ShadowDOM

Hi @jaredmcateer
Great work with this plug-in!

I am already using mgVue and it works great. In my use-case, I need to mount a vue component but I don’t want the styles from angular to bleed down into vue. Rather than fix one-off I issues, I’m going for isolation of the vue component.

I’m interested in adding an option to mount the vue component in a shadowDOM root element.

I’m wondering if you thought this was possible?

I was thinking that it could just be a small tweak to ngLinker but haven’t been able to get it to work yet.

Specify `module` to `build/index.esm.js` in `package.json` to support ESM import

Currently the main script is set to UMD module build/index.js. But in some case if we need to use vite transformMixedEsModules function (vueComponent/ant-design-vue#5717) like:

export default {
  build: {
    commonjsOptions: {
      transformMixedEsModules: true,
    },
  },
}

These lines in build/index.js will cause problems: (Claiming Vue__default["default"].component is not a function)

if (isCompositionApi(component)) {
  component = Vue__default["default"].component(component.name || 'UnnamedComponent', component);
}
//            ^ Vue__default["default"].component is not a function

Current workaround is manually resolving ngVue to build/index.esm.js:

export default {
  resolve: {
    alias: {
      ngVue: resolve(__dirname, './node_modules/ngVue/build/index.esm.js'),
    },
  },
}

I'd appreciate it that module is set to build/index.esm.js in package.json

Event names cannot contain dashes

Given a vue component that emits an event like this:

this.$emit('something-happened')

I can't listen to this even with v-on-something-happened on a <vue-component /> tag and all events have to be named without any word separation inside the Vue component:

this.$emit('somethinghappened')

Expected result:

Be able to use v-on-something-happpened="fn" attribute and listen to all Events triggered by the Vue component.

Updating Angular Controller from vue component

Is it possible to update the Angular controller from the Vue component?

I have an Angular controller that receives data from several services before passing them down to the vue component as props using ngVue, I need to then pass data back to the controller after checking for the existence of data within the component. As the props are immutable, and the vue component variables are not available outside of it's template I'm looking for a way to update the scope variable attached to the v-props.

Thanks in advance for any guidance with whether this is possible.

Update to the Vuex store does not trigger update in the components in debug mode.

I am updating the vuex store from angular js and in dev developer tools I see the updated value in store but vue components does not reflect the changes in debug mode. It shows the changes after the hot reload or after state update from component property change.

Everything works fine in production mode. What could be the problem here ?

ngVueComponentsModule.config(($ngVueProvider) => {
  $ngVueProvider.activeQuirkMode()
  $ngVueProvider.setRootVueInstanceProps({ router, store })
})

Uncaught TypeError: e.warn is not a function

When minifying code with uglify-js and drop_console option, the error TypeError: e.warn is not a function is thrown, as we're trying to use a method from an object that is removed.

`npm run bundle` fails

Here's what I get when I try to run the bundle task:

> [email protected] bundle /home/npayot/dev/ngVue
> BABEL_ENV=build rollup -c

resolve failed:  { Error: Cannot find module 'caniuse-db'
    at Function.Module._resolveFilename (module.js:472:15)
    at Function.requireRelative.resolve (/home/npayot/dev/ngVue/node_modules/require-relative/index.js:30:17)
    at resolve (/home/npayot/dev/ngVue/node_modules/modify-babel-preset/lib/serialize.js:25:26)
    at findAndRemove (/home/npayot/dev/ngVue/node_modules/modify-babel-preset/lib/serialize.js:67:11)
    at /home/npayot/dev/ngVue/node_modules/modify-babel-preset/lib/serialize.js:111:13
    at Array.map (native)
    at loadPreset (/home/npayot/dev/ngVue/node_modules/modify-babel-preset/lib/serialize.js:103:29)
    at module.exports (/home/npayot/dev/ngVue/node_modules/modify-babel-preset/index.js:97:19)
    at Object.<anonymous> (/home/npayot/dev/ngVue/node_modules/babel-preset-es2015-rollup/index.js:3:18)
    at Module._compile (module.js:573:32)
    at Object.Module._extensions..js (module.js:582:10)
    at Module.load (module.js:490:32)
    at tryModuleLoad (module.js:449:12)
    at Function.Module._load (module.js:441:3)
    at Module.require (module.js:500:17)
    at require (internal/module.js:20:19)
    at /home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/options/option-manager.js:302:17
    at Array.map (native)
    at OptionManager.resolvePresets (/home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/options/option-manager.js:270:20)
    at OptionManager.mergePresets (/home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/options/option-manager.js:259:10)
    at OptionManager.mergeOptions (/home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/options/option-manager.js:244:14)
    at OptionManager.init (/home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/options/option-manager.js:374:12) code: 'MODULE_NOT_FOUND' } caniuse-db
resolve failed:  { Error: Cannot find module 'babel-runtime'
    at Function.Module._resolveFilename (module.js:472:15)
    at Function.requireRelative.resolve (/home/npayot/dev/ngVue/node_modules/require-relative/index.js:30:17)
    at resolve (/home/npayot/dev/ngVue/node_modules/modify-babel-preset/lib/serialize.js:25:26)
    at findAndRemove (/home/npayot/dev/ngVue/node_modules/modify-babel-preset/lib/serialize.js:67:11)
    at /home/npayot/dev/ngVue/node_modules/modify-babel-preset/lib/serialize.js:111:13
    at Array.map (native)
    at loadPreset (/home/npayot/dev/ngVue/node_modules/modify-babel-preset/lib/serialize.js:103:29)
    at module.exports (/home/npayot/dev/ngVue/node_modules/modify-babel-preset/index.js:97:19)
    at Object.<anonymous> (/home/npayot/dev/ngVue/node_modules/babel-preset-es2015-rollup/index.js:3:18)
    at Module._compile (module.js:573:32)
    at Object.Module._extensions..js (module.js:582:10)
    at Module.load (module.js:490:32)
    at tryModuleLoad (module.js:449:12)
    at Function.Module._load (module.js:441:3)
    at Module.require (module.js:500:17)
    at require (internal/module.js:20:19)
    at /home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/options/option-manager.js:302:17
    at Array.map (native)
    at OptionManager.resolvePresets (/home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/options/option-manager.js:270:20)
    at OptionManager.mergePresets (/home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/options/option-manager.js:259:10)
    at OptionManager.mergeOptions (/home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/options/option-manager.js:244:14)
    at OptionManager.init (/home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/options/option-manager.js:374:12)
    at File.initOptions (/home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/index.js:216:65)
    at new File (/home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/file/index.js:139:24)
    at Pipeline.transform (/home/npayot/dev/ngVue/node_modules/babel-core/lib/transformation/pipeline.js:46:16)
    at preflightCheck (/home/npayot/dev/ngVue/node_modules/rollup-plugin-babel/dist/rollup-plugin-babel.cjs.js:44:25)
    at Object.transform$1 [as transform] (/home/npayot/dev/ngVue/node_modules/rollup-plugin-babel/dist/rollup-plugin-babel.cjs.js:117:18)
    at /home/npayot/dev/ngVue/node_modules/rollup/src/utils/transform.js:19:35 code: 'MODULE_NOT_FOUND' } babel-runtime
Treating 'babel-helper-vue-jsx-merge-props' as external dependency
No name was provided for external module 'babel-helper-vue-jsx-merge-props' in options.globals – guessing '_mergeJSXProps'

I tried to install modules caniuse-db and babel-plugin-transform-runtime but nothing better came out.
I'm using node v7.0.0 and have the same output with node 6.5.0. If you have any hints, I'd be more than happy to help.

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.