如何在点击时调用自定义元素成员方法? [英] How to invoke custom element member method on click?

查看:98
本文介绍了如何在点击时调用自定义元素成员方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

AKA在访问自定义元素方法时,如何避免或避免在Angular表达式中引用DOM节点.

AKA How to get around or avoid Referencing DOM nodes in Angular expressions is disallowed when accessing custom element methods.

所以我正在使用David Shapiro的小型库 angular-custom-element 进行实验具有诸如自定义元素之类的功能,以便将来验证我们的组件.我走得很远,他们似乎在报到.但是,在示例中,它试图显示在指令示例的 ng-click 中调用元素的成员函数.

So I'm using David Shapiro's small library angular-custom-element to experiment with features such as custom elements to future proof our components. I've gotten pretty far, they seem to register. however, in the example, it tries to show invoking a element's member function inside ng-click in the directive example.

 template: '<div ng-click="el.elementMethod()">{{ el.propertyNameOne }} CUSTEM ELEMENT CLICK ME</div>',

下面是完整的代码和可运行的代码段.在您单击自定义元素"之前,它不会引发错误

Below is the full code and runnable snippet. It does not throw the error until you click on the 'CUSTEM ELEMENT'

我尝试在该方法中添加"return true"以避开其他帖子所建议的安全性,但无济于事.也许有替代的实现方式吗?

I tried adding a 'return true' in the method to get around the security as suggested by other posts but to no avail. Is there perhaps an alternate implementation?

angular.module('appy', []);

angular.module('appy.elementDirectives', ['customElements']);

var app = angular.module('appy', [
    'appy',
    'appy.elementDirectives'
]);



app.config( function($customElementsProvider ) {
        'use strict';           
        var definitions =  [
                {
                  name: 'shiny-button',
                  definition: {
                    parent: HTMLButtonElement,
                    properties: {
    
                        propertyNameOne: {
                            get: function () {
                                // do any value calculations
                                var valueVar = 'testFooValue';
                                return valueVar;
                            },
                            set: function (val) {
                                // do any value calculations
                                val = val + 'X';
                                return val;
                            },
                            attribute: {
                                name: 'property-one'
                            }
                        },
                        propertyNameTwo: {
    
                            attribute: {},
                            value: 'hello',
                            readOnly: true
                        },
                        booleanProperty: {
                            attribute: {
                                name: 'bool-prop',
                                // note that "true" here just signifies that the attr should
                                // treated as a boolean,
                                boolean: true // default is false
                            },
                            value: true // default is false
                        }
                    },
    
                    // In all callbacks "this" referes to the element instance
                    callbacks: {
                        created: function () {
                          console.log('created')
                          
                        },
    
                        // is called when the element is inserted into the DOM
                        attached: function () {
                            console.log('attached')
                        },
    
                        // is called when the element is removed from the DOM
                        detached: function () {
                            // include any cleanup logic
                            console.log('detached')
                        },
    
                        // called upon any attribute change including attr set programatically
                        // during element instantiation (but not if the elem already exists in markup)
                        attributeChanged: function (attr, oldVal, newVal) {
                            console.log('attributeChanged', attr, oldVal, newVal)
                        }
                    },
                    members: {
    
                        elementMethod: function (args) {
                            alert("member click");
                            console.log('clicked member');
                            return true;
                        },
    
    
                        memberNameOne: {
                            get: function (val) {
                                return val;
                            },
                            set: function (val) {
                                val = val + 'X';
                                return val;
                            },
                            value: 'blah blah',
                            readOnly: true
                      }
                    } 
                }
            }];

        console.log("Register Element");
        //$customElementsProvider.register( 'shiny-button', definitions[0].definition );
        $customElementsProvider.registerCollection(definitions);
});

 

app.controller('MainCtrl', function($scope) { 
  var that = this;
  
  $scope.showTest = true;
  
   $scope.testButtonClick = function(){
      alert("testButton click")
    }
  
});

app.directive('shinyButton', function($customElements) {
  return {
            restrict:'E',
            scope: {
                nothingHereYet: '=propertyNameOne'
            },
            replace: false,
            template: '<div ng-click="el.elementMethod()">{{ el.propertyNameOne }} CUSTEM ELEMENT CLICK ME</div>',
           
            controllerAs:'shinyCtrl',
            
            // 3.4 at a minimum inject $scope and $element into your controller
                    controller: function($scope, $element, $attrs, $document, $log){

                        // 4. This is the only line of code that is required.
                        // this command takes care of binding all custom properties
                        // to the $scope including triggering a $digest() when
                        // any custom property is changed outside of Angular
                        // After this line you can enjoy the full power of AngularJS
                        // when interacting with your Custom Element
                        $customElements.$watchElement($scope, $element);

                        $document.on('member:changed', function(evt){
                            if(evt.detail.propName == 'a protopype prop we need to watch'){
                                // i.e. $scope.el.__proto__.memberNameOne
                                $scope.$digest();
                            }
                        });

                        // 4.2
                        // bind to an event on the element
                        // since all prop changes generate a change event
                        // other frameworks in the page can import and react
                        // to the same component
                        $element.on('prop:changed', function(evt){
                            $log.log(evt.detail); 
                            $scope.$emit(evt.detail);
                        });
                        
                        
                        

                        // 4.3
                        // gets the original custom elem config obj mostly for any debug
                        var info = $customElements.info($element);

                        //$customElements.$importElement($scope, $element, ['array','of','property','names']);

                    },
                    link: function(scope, iElement, iAttrs, controller){
                        // ...
                    }
            
        };
});





/*! (C) WebReflection Mit Style License */

/*
 * AngularCustomElement
 * https://github.com/dgs700/angular-custom-element

 * Version: 0.2.0 - 2014-11-01
 * License: MIT
 */
 
 
 
 /* Requires a Custom Element polyfill for all browsers other than Chrome
 Recommended:  https://github.com/WebReflection/document-register-element */
/*! (C) WebReflection Mit Style License */
(function(e,t,n,r){"use strict";function q(e,t){for(var n=0,r=e.length;n<r;n++)J(e[n],t)}function R(e){for(var t=0,n=e.length,r;t<n;t++)r=e[t],$(r,c[z(r)])}function U(e){return function(t){g.call(L,t)&&(J(t,e),q(t.querySelectorAll(h),e))}}function z(e){var t=e.getAttribute("is");return d.call(l,t?t.toUpperCase():e.nodeName)}function W(e){var t=e.currentTarget,n=e.attrChange,r=e.prevValue,i=e.newValue;t.attributeChangedCallback&&e.attrName!=="style"&&t.attributeChangedCallback(e.attrName,n===e.ADDITION?null:r,n===e.REMOVAL?null:i)}function X(e){var t=U(e);return function(e){t(e.target)}}function V(e,t){var n=this;O.call(n,e,t),B.call(n,{target:n})}function $(e,t){N(e,t),I?I.observe(e,_):(H&&(e.setAttribute=V,e[i]=F(e),e.addEventListener(u,B)),e.addEventListener(o,W)),e.createdCallback&&(e.created=!0,e.createdCallback(),e.created=!1)}function J(e,t){var n,r=z(e),i="attached",s="detached";-1<r&&(C(e,c[r]),r=0,t===i&&!e[i]?(e[s]=!1,e[i]=!0,r=1):t===s&&!e[s]&&(e[i]=!1,e[s]=!0,r=1),r&&(n=e[t+"Callback"])&&n.call(e))}if(r in t)return;var i="__"+r+(Math.random()*1e5>>0),s="extends",o="DOMAttrModified",u="DOMSubtreeModified",a=/^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+$/,f=["ANNOTATION-XML","COLOR-PROFILE","FONT-FACE","FONT-FACE-SRC","FONT-FACE-URI","FONT-FACE-FORMAT","FONT-FACE-NAME","MISSING-GLYPH"],l=[],c=[],h="",p=t.documentElement,d=l.indexOf||function(e){for(var t=this.length;t--&&this[t]!==e;);return t},v=n.prototype,m=v.hasOwnProperty,g=v.isPrototypeOf,y=n.defineProperty,b=n.getOwnPropertyDescriptor,w=n.getOwnPropertyNames,E=n.getPrototypeOf,S=n.setPrototypeOf,x=!!n.__proto__,T=n.create||function K(e){return e?(K.prototype=e,new K):this},N=S||(x?function(e,t){return e.__proto__=t,e}:w&&b?function(){function e(e,t){for(var n,r=w(t),i=0,s=r.length;i<s;i++)n=r[i],m.call(e,n)||y(e,n,b(t,n))}return function(t,n){do e(t,n);while(n=E(n));return t}}():function(e,t){for(var n in t)e[n]=t[n];return e}),C=S||x?function(e,t){g.call(t,e)||$(e,t)}:function(e,t){e[i]||(e[i]=n(!0),$(e,t))},k=e.MutationObserver||e.WebKitMutationObserver,L=(e.HTMLElement||e.Element||e.Node).prototype,A=L.cloneNode,O=L.setAttribute,M=t.createElement,_=k&&{attributes:!0,characterData:!0,attributeOldValue:!0},D=k||function(e){H=!1,p.removeEventListener(o,D)},P=!1,H=!0,B,j,F,I;k||(p.addEventListener(o,D),p.setAttribute(i,1),p.removeAttribute(i),H&&(B=function(e){var t=this,n,r,s;if(t===e.target){n=t[i],t[i]=r=F(t);for(s in r){if(!(s in n))return j(0,t,s,n[s],r[s],"ADDITION");if(r[s]!==n[s])return j(1,t,s,n[s],r[s],"MODIFICATION")}for(s in n)if(!(s in r))return j(2,t,s,n[s],r[s],"REMOVAL")}},j=function(e,t,n,r,i,s){var o={attrChange:e,currentTarget:t,attrName:n,prevValue:r,newValue:i};o[s]=e,W(o)},F=function(e){for(var t,n,r={},i=e.attributes,s=0,o=i.length;s<o;s++)t=i[s],n=t.name,n!=="setAttribute"&&(r[n]=t.value);return r})),t[r]=function(n,r){y=n.toUpperCase(),P||(P=!0,k?(I=function(e,t){function n(e,t){for(var n=0,r=e.length;n<r;t(e[n++]));}return new k(function(r){for(var i,s,o=0,u=r.length;o<u;o++)i=r[o],i.type==="childList"?(n(i.addedNodes,e),n(i.removedNodes,t)):(s=i.target,s.attributeChangedCallback&&i.attributeName!=="style"&&s.attributeChangedCallback(i.attributeName,i.oldValue,s.getAttribute(i.attributeName)))})}(U("attached"),U("detached")),I.observe(t,{childList:!0,subtree:!0})):(t.addEventListener("DOMNodeInserted",X("attached")),t.addEventListener("DOMNodeRemoved",X("detached"))),t.addEventListener("readystatechange",function(e){q(t.querySelectorAll(h),"attached")}),t.createElement=function(e,n){var r,i=M.apply(t,arguments);return n&&i.setAttribute("is",e=n.toLowerCase()),r=d.call(l,e.toUpperCase()),-1<r&&$(i,c[r]),i},L.cloneNode=function(e){var t=A.call(this,!!e),n=z(t);return-1<n&&$(t,c[n]),e&&R(t.querySelectorAll(h)),t});if(-1<d.call(l,y))throw new Error("A "+n+" type is already registered");if(!a.test(y)||-1<d.call(f,y))throw new Error("The type "+n+" is invalid");var i=function(){return t.createElement(p,u&&y)},o=r||v,u=m.call(o,s),p=u?r[s]:y,g=l.push(y)-1,y;return h=h.concat(h.length?",":"",u?p+'[is="'+n.toLowerCase()+'"]':p),i.prototype=c[g]=m.call(o,"prototype")?o.prototype:T(L),q(t.querySelectorAll(h),"attached"),i}})(window,document,Object,"registerElement");
/*
 * AngularCustomElement
 * https://github.com/dgs700/angular-custom-element

 * Version: 0.2.0 - 2014-11-01
 * License: MIT
 */
!function(){"use strict";function a(){function a(a,b){function c(a){var b=parseFloat(a);return!isNaN(b)&&isFinite(b)?b:a}function e(a,b,c,d,e,f){if(!c)return b;c.setterCalled[e]=!0;try{e&&f?b?c.setAttribute(e,""):c.removeAttribute(e):e&&c.setAttribute(e,b),c.onPropChange(b)}catch(g){}return d=f?!!c[a]:c[a],c.propChangeNotify(c,a,b,d,e,"prop:changed"),b}if("string"!=typeof a||!/.*-.*/.test(a))return console.error("Invalid element name: ",a),this;if(a=a.toLowerCase(),d[a])return this;var f=b.parent?b.parent.prototype:b["extends"]?Object.create(document.createElement(b["extends"]).constructor).prototype:HTMLElement.prototype,g=b["extends"]||null,h=b.properties||{},i=b.members||{},j={prototype:{}},k=function(){};for(var l in i)!function(a,b){function c(a,b,c){return document.dispatchEvent(new CustomEvent("member:changed",{detail:{propName:c,newValue:a,oldValue:b||null}})),a}var d,e,f,g,h,i,l=this;"function"==typeof b[a]?j.prototype[a]={enumerable:!0,value:b[a]}:(d=b[a],i=d.readOnly||!1,g=d.value||null,e="function"==typeof d.get?function(){return g=d.get.call(l,g)}:function(){return g},f=i?k:"function"==typeof d.set?function(b){b=d.set.call(l,b),h=this[a]||null,g=c(b,h,a)}:function(b){h=this[a]||null,g=c(b,h,a)},j.prototype[a]={get:e,set:f,enumerable:!0})}(l,i);j.prototype.registerCallback={value:function(a,b){a.onPropChange=b},enumerable:!0},j.prototype.propChangeNotify={value:function(a,b,c,d,e,f){a.dispatchEvent(new CustomEvent(f,{detail:{propName:b,newValue:c,oldValue:d,attrName:e||null}}))},enumerable:!0},j.prototype.definition={value:b};var m,n=b.callbacks||{},o=n.created||k,p=n.attached||k,q=n.detached||k,r=n.attributeChanged||k,s={},t=[];for(m in h)!function(a,b){t.push(function(d){var f,g,h,i,l,m,n,o,p;o=!1,l=null,g=null,f=b[a],n=f.readOnly||!1,p=f.attribute?!0:!1,o=p&&f.attribute.boolean?!0:!1,p&&!n&&d?(g=f.attribute.name?f.attribute.name:a.toLowerCase(),s[g]={name:a,bool:o},o?l=d.hasAttribute(g)?!0:f.value?!0:!1:(l=d.hasAttribute(g)?d.getAttribute(g):f.value?f.value:null,l=c(l)),d.hasAttribute(g)||(o&&l?d.setAttribute(g,""):o?d.removeAttribute(g):d.setAttribute(g,l)),d.setterCalled[g]=!1):o&&!d?l=f.value?!0:!1:(l=f.value?f.value:null,l=c(l)),h="function"==typeof f.get?f.get:function(){return l},i=n?k:"function"==typeof f.set?function(b){b=f.set.call(d,b),b=e(a,b,d,m,g,o),l=b}:function(b){l=e(a,b,d,m,g,o)},d?Object.defineProperty(d,a,{get:h,set:i,enumerable:!0,configurable:!0}):j.prototype[a]={get:h,set:i,enumerable:!0}})}(m,h),t.forEach(function(a){a.call(this,null)});j.prototype.createdCallback={enumerable:!0,value:function(){this.setterCalled={};var a=this;t.forEach(function(b){b.call(a,a)});var b=o?o.apply(this,arguments):null;return b}},j.prototype.attributeChangedCallback={enumerable:!0,value:function(a,b,d){if(s[a]&&!this.setterCalled[a]){var e=s[a];e.bool&&""===d&&(d=!0),this.setterCalled[a]=!1,this[e.name]=e.bool?!!d:c(d)}var f=r?r.apply(this,arguments):null;return f}},j.prototype.attachedCallback={enumerable:!0,value:function(){this.classList.remove("unresolved");var a=p?p.apply(this,arguments):null;return a}},j.prototype.detachedCallback={enumerable:!0,value:function(){var a=q?q.apply(this,arguments):null;return a}};var u={prototype:Object.create(f,j.prototype)};return g&&(u["extends"]=g),d[a]=document.registerElement(a,u),this}function b(a){return Array.isArray(a)?(a.forEach(function(a){var b=a.name,c=a.definition;return"string"!=typeof b||c!==Object(c)?(console.warn("bad element definition format"),!1):void e.register(b,c)}),!0):(console.error("parameter to registerCollection must be an array"),!1)}function c(){return{info:function(a){return a[0].__proto__.definition},$watchElement:function(a,b,c){c=c||!1,a.el=b[0],c||a.el.registerCallback(a.el,function(){return setTimeout(function(){a.$digest()},0),!0})},$importElement:function(a,b,c,d){var e=null,f=null;return Array.isArray(c)&&(c.forEach(function(a,b,c){c[b]=c[b].toLowerCase()}),e=new MutationObserver(function(b){b.forEach(function(b){-1!==c.indexOf(b.attributeName)&&a.$digest()})}).observe(b[0],{attributes:!0,childList:!0,characterData:!0,attributeOldValue:!0})),d&&(f=b.addEventListener(d,function(){a.$digest()})),Array.isArray(c)&&d?{observer:e,eventBinding:f}:!1}}}var d={};window.registeredElements=this.registeredElements=d,this.register=a;var e=this;this.registerCollection=b,this.$get=c,c.$inject=["$window"]}angular.module("customElements",["ng"]).provider("$customElements",a)}(window);

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="appy">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <link data-require="bootstrap-css@*" data-semver="3.2.0" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
  <script>
    document.write('<base href="' + document.location + '" />');
  </script>
  <link rel="stylesheet" href="style.css" />
  <script data-require="angular.js@1.2.x" src="https://code.angularjs.org/1.2.6/angular.js" data-semver="1.2.25"></script>
  <script  src="angular-custom-element.js"></script>

</head>
<body ng-controller="MainCtrl as vm">

<shiny-button>this will be replaced by directive template</shiny-button>

<br/><br/>

<div ng-show="showTest" ng-click="testButtonClick()">Test Click Div</div>


</html>

推荐答案

这是因为AngularJS 1.x尚不支持Web组件.它假定如果您直接从角度宇宙中引用DOM属性,则您必须是愚蠢的,因为该代码可能是恶意的,或者仅违反了最初的观点,即角度代码应完全从外部封装并且框架可以处理所有外部事件.相互作用.尽管在大多数情况下都是如此,但这是在我们知道自定义元素将很快成为构建可重用UI Web组件的方式的核心之前发生的.

This is because AngularJS 1.x is not yet web component friendly. It assumes that you must be stupid if you reference DOM properties directly from w/in the angular universe since that code could be malicious or it just violates the original notion that angular code should be completely encapsulated from the outside world and the framework handles all outside interaction. While this is true in most cases, it came about at a time before we knew that Custom Elements would soon be core to the way we construct reusable UI web components.

在NG 2.0之前,您可能需要执行以下操作:

Until NG 2.0 you'll probably need to do something like this:

替换此:

<div ng-click="el.elementMethod()">

与此:

<div ng-click="callMember()">

$scope.callMember = function(){
    $scope.el.elementMethod();
};

使用替换代码没有错误.

With the replacement code there is no error.

但是有趣的是,通过 {{el.propertyNameOne}} 进行的相同DOM访问不会导致Angular抛出相同的错误.走吧.

What's interesting though, is that the same DOM access via {{ el.propertyNameOne }} does not cause Angular to throw the same error. Go figure.

这篇关于如何在点击时调用自定义元素成员方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆