2016-06-09 8 views
7

(내 질문 제목이 좋지 않으면 죄송합니다. 더 나은 옵션을 제안 할 수는 없습니다.)규칙에 따라 객체의 임의로 딥 속성에 바인딩

저는 오브젝트를 그리드에 바인딩 할 수 있지만 오브젝트의 프리젠 테이션을 어느 정도 사용자 정의 할 수있는 방식으로 재사용 가능한 "특성 격자"를 각도로 작성하려고합니다.

처럼 지시어 템플릿 모습입니다합니다 ( form-element 내 질문에 중요하지 않다, 그래서 나는 그것을 밖으로 떠날거야) : 지시어 코드

<div ng-repeat="prop in propertyData({object: propertyObject})"> 
    <div ng-switch on="prop.type"> 
     <div ng-switch-when="text"> 
      <form-element type="text" 
          label-translation-key="{{prop.key}}" 
          label="{{prop.key}}" 
          name="{{prop.key}}" 
          model="propertyObject[prop.key]" 
          focus-events-enabled="false"> 
      </form-element> 
     </div> 
    </div> 
</div> 

과 :

angular.module("app.shared").directive('propertyGrid', ['$log', function($log) { 
    return { 
     restrict: 'E', 
     scope: { 
      propertyObject: '=', 
      propertyData: '&' 
     } 
     templateUrl: 'views/propertyGrid.html' 
    }; 
}]); 

<property-grid edit-mode="true" 
       property-object="selectedSite" 
       property-data="getSitePropertyData(object)"> 
</property-grid> 

그리고 getSitePropertyData() 기능 그쪽 :

다음은 사용 예입니다 t는 그것으로 간다 :

var lastSite; 
var lastSitePropertyData; 
$scope.getSitePropertyData = function (site) { 
    if (site == undefined) return null; 

    if (site == lastSite) 
     return lastSitePropertyData; 

    lastSite = site; 
    lastSitePropertyData = [ 
     {key:"SiteName", value:site.SiteName, editable: true, type:"text"}, 
     //{key:"Company.CompanyName", value:site.Company.CompanyName, editable: false, type:"text"}, 
     {key:"Address1", value:site.Address1, editable: true, type:"text"}, 
     {key:"Address2", value:site.Address2, editable: true, type:"text"}, 
     {key:"PostalCode", value:site.PostalCode, editable: true, type:"text"}, 
     {key:"City", value:site.City, editable: true, type:"text"}, 
     {key:"Country", value:site.Country, editable: true, type:"text"}, 
     {key:"ContactName", value:site.ContactName, editable: true, type:"text"}, 
     {key: "ContactEmail", value: site.ContactEmail, editable: true, type:"email"}, 
     {key: "ContactPhone", value: site.ContactPhone, editable: true, type:"text"}, 
     {key: "Info", value: site.Info, editable: true, type:"text"} 
    ]; 
    return lastSitePropertyData; 
}; 
나는 그런 "속성 데이터"기능을 통해가는 단지 객체의 속성에 직접 바인딩 아니에요 이유는 나뿐만 아니라, 속성의 순서를 제어 할 필요가있다

심지어는 사용자에게 표시할지 여부와 프레젠테이션을 위해 어떤 종류의 속성 (텍스트, 전자 메일, 번호, 날짜 등)인지에 따라 다릅니다.

먼저 getSitePropertyData() 함수에서 나머지 숫자 인 value에서 알 수 있듯이이 함수에서 직접 값을 제공하려고 시도했지만 객체에 바인딩되지 않으므로 개체 또는 양식이 변경됩니다 속성 표가 앞뒤로 동기화되지 않았습니다. 다음으로는 key 아이디어를 사용하고 있습니다. 바로 이것을 수행 할 수 있습니다 : propertyObject[prop.key] - 직접 속성에 적합하지만 볼 수있는 것처럼 속성의 속성이기 때문에 "Company"필드를 주석 처리해야했습니다. , propertyObject["a.b"]이 작동하지 않습니다.

나는 여기서 무엇을해야할지 고민 중입니다. 바인딩이 필요합니다. 바인딩에 임의로 깊은 속성을 사용할 수 있어야합니다. 나는 이런 종류의 것이 이론적으로 가능하다는 것을 안다; 예를 들어 UI Grid에서 완료 한 것을 보았습니다. 그러나 그러한 프로젝트에는 너무 많은 코드가있어서 내가 어떻게 작업하는지 찾는 데 많은 시간을 할애 할 것입니다.

내가 가까워지고 있습니까, 아니면이 모든 일이 잘못 될까요?

답변

2

개체에서 임의의 각도 식을 실행하고 싶습니다. 이는 정확히 $parse (ref)의 목적입니다. 이 서비스는 ...Angular 표현식을 파싱하고 getter와 setter를 반환합니다.

<form-element type="text" 
    label-translation-key="{{prop.key}}" 
    label="{{prop.key}}" 
    name="{{prop.key}}" 
    root-obj="propertyObject" 
    path="{{prop.key}}" 
    focus-events-enabled="false"> 

: 희망 의미를 변경하지 않고

app.directive('formElement', ['$parse', function($parse) { 
    return { 
    restrict: 'E', 
    scope: { 
     label: '@', 
     name: '@', 
     rootObj: '=', 
     path: '@' 
    }, 
    template: 
     '<label>{{ label }}</label>' + 
     '<input type="text" ng-model="data.model" />', 
    link: function(scope) { 
     var getModel = $parse(scope.path); 
     var setModel = getModel.assign; 
     scope.data = {}; 
     Object.defineProperty(scope.data, 'model', { 
     get: function() { 
      return getModel(scope.rootObj); 
     }, 
     set: function(value) { 
      setModel(scope.rootObj, value); 
     } 
     }); 
    } 
    }; 
}]); 

나는 약간 지시어가 사용되는 방법을 변경 한 다음 예는 $parse의 사용을 보여주는 당신의 formElement 지침의 과도하게 단순화 구현 root-obj이 모델의 맨 위에 있고 path은 실제 데이터에 도달하는 표현식입니다.

보시다시피, 은 루트 개체에 대해 지정된 식에 대해 getter 및 setter 함수를 만듭니다. model.data 속성에서 $parse으로 만든 접근 자 함수를 루트 개체에 적용합니다. Object.defineProperty 구조 전체가 시계로 대체 될 수 있지만 이는 다이제스트주기에만 오버 헤드를 추가합니다. 여기

이 작업 바이올린입니다 : 그런데 https://jsfiddle.net/zb6cfk6y/


, 가져 오기/세트를 작성하는 또 다른 (간결하고 관용적 인 이상) 방법은 다음과 같습니다

Object.defineProperty(scope.data, 'model', { 
    get: getModel.bind(null, scope.rootObj), 
    set: setModel.bind(null, scope.rootObj) 
    }); 
0

당신은 당신이 그것을

function createObject(){ 
    for(var key in site){ 
     lastSitePropertyData.push({key:key, value:site[key], editable: true, type:"text"}); 
} } 

그리고 나중에 사용할 수 있도록 기능이 그런 식으로 사용 될 수있는이

function getKey(prop){ 
     if(typeof prop.value === 'object'){ 
     return prop.value.key; //can run loop create a go deep reccursive method - thats upto u 
    } 
    else return prop.key; 
} 
function getValue(prop){ 
     if(typeof prop === 'object'){ 
     return prop.value.value; //have tp run loop get value from deep reccursive method - thats upto u 
    } 
    else return prop.value; 
} 

등의 데이터 뭔가를 얻기 위해 하드 코딩하지 이런 식으로 객체를 생성 할 수 lastSitePropertyData을 만들 때 html로 {{getKey(prop)}}{{getValue(prop}} 작업 데모를 보시려면이 링크를 보시기 바랍니다. - https://jsfiddle.net/718px9c2/4/

참고 : 더 좋은 방법으로 json 데이터에 액세스하기위한 아이디어입니다. 데모에서는 각도를 사용하지 않습니다.

+0

나는 당신의 대답 미스를 생각한다 내 질문에서 두 가지 가장 중요한 점. 첫째, "나는"속성 데이터 "기능을 통해 가고 개체의 속성에 직접 바인딩하는 것이 아니라 속성의 순서를 제어해야한다는 이유가 있습니다." for 루프를 사용하면 이러한 제어가 허용되지 않습니다. 둘째, "이 함수에서 직접 값을 제공하려고 시도했지만 개체에 바인딩되지 않으므로 개체 또는 양식에서 변경 내용이 앞뒤로 동기화되지 않았습니다." 솔루션에서이 양방향 바인딩을 요구하지 않습니다. – Alex

0

또 다른 아이디어는 이와 같이 수행하는 것입니다. object.proto를 더티하게 만드는 것을 피하려면 (이 방법은 항상 좋은 생각입니다)이 기능을 다른 모듈로 옮기십시오.

(function() { 

    'use strict'; 
    if (Object.hasOwnProperty('getDeep')) { 
     console.error('object prototype already has prop function'); 
     return false; 
    } 

    function getDeep(propPath) { 
     if (!propPath || typeof propPath === 'function') { 
      return this; 
     } 

     var props = propPath.split('.'); 

     var result = this; 
     props.forEach(function queryProp(propName) { 
      result = result[propName]; 
     }); 

     return result; 
    } 

    Object.defineProperty(Object.prototype, 'getDeep', { 
     value: getDeep, 
     writable: true, 
     configurable: true, 
     enumerable: false 
    }); 


}()); 
+0

그러나 이것은 객체를 각도 모델에 바인딩하지 않습니다. 따라서 양식의 값을 편집 할 수 없습니다. –

0

나는 그리드에 데이터를 표시하는 데 사용 비슷한을 가지고, 그 그리드는 아직 같은 열을 같은 객체를 표시하지 않을 수 있습니다. 그러나 나는 그것을 한 번에 다룰 수 없다.

  1. 나는 내 유형을 선언 유형의 서비스 및 일부 기본 구성
  2. 내가 지정한 내용에 따라 그리드 정의 옵션을 생성하는 그리드 서비스가 있습니다.
  3. 컨트롤러에서 그리드 서비스를 사용하여 그리드를 인스턴스화하고 열의 순서와 기본 구성을 재정의하는 특정 구성을 지정합니다. 그리드 서비스 자체는 필터링을위한 적절한 구성을 생성하고 필드의 유형 정의를 사용하여 정렬합니다.
1

lodash을 사용하는 경우 _.get 함수를 사용하여이를 수행 할 수 있습니다.

당신은 당신의 property-grid의 컨트롤러에 _.get을 저장하고 다음 템플릿에

model="get(propertyObject,prop.key)" 

를 사용합니다. 이 기능을 응용 프로그램의 여러 위치 (및 property-grid이 아닌)에 사용해야하는 경우 filter을 쓸 수 있습니다.


이 문제는이 방법으로 모델을 바인딩 할 수 없기 때문에 값을 편집 할 수 없다는 것입니다. _.set 함수와 getter 및 setter가있는 객체를 사용하여이 작업을 수행 할 수 있습니다.

vm.modelize = function(obj, path) { 
    return { 
     get value(){return _.get(obj, path)}, 
     set value(v){_.set(obj, path,v)} 
    }; 
} 

그런 다음 템플릿의 기능을 사용할 수 있습니다 :

<div ng-repeat="prop in propertyData({object: propertyObject})"> 
    <input type="text" 
      ng-model="ctrl.modelize(propertyObject,prop.key).value" 
      ng-model-options="{ getterSetter: true }"></input> 
</div> 

은 감소 예를 들어이 Plunker를 참조하십시오. 당신이 lodash를 사용하지 않는 경우


당신은 내가 lodash에서 추출한 _.get 기능의 단순화 된 버전을 사용할 수 있습니다.

function getPath(object, path) { 
    path = path.split('.') 

    var index = 0 
    var length = path.length; 

    while (object != null && index < length) { 
    object = object[path[index++]]; 
    } 
    return (index && index == length) ? object : undefined; 
} 

이 기능을 사용하면 Cannot read property 'foo' of undefined 오류가 발생하지 않습니다. 이것은 특히 정의되지 않은 값이있을 수있는 속성의 긴 체인이있는 경우 유용합니다. 더 많은 고급 경로 (예 : foo.bar[0])를 사용하려면 _.get 기능을 lodash에서 사용해야합니다. 이러한 추출 기능 lodash 처리하는 일부 가장자리의 경우를 무시하는 것이

function setPath(object, path, value) { 
    path = path.split(".") 

    var index = -1, 
     length = path.length, 
     lastIndex = length - 1, 
     nested = object; 

    while (nested != null && ++index < length) { 
     var key = path[index] 
     if (typeof nested === 'object') { 
      var newValue = value; 
      if (index != lastIndex) { 
       var objValue = nested[key]; 
       newValue = objValue == null ? 
        ((typeof path[index + 1] === 'number') ? [] : {}) : 
        objValue; 
      } 


      if (!(hasOwnProperty.call(nested, key) && (nested[key] === value)) || 
       (value === undefined && !(key in nested))) { 
       nested[key] = newValue; 
      } 


     } 
     nested = nested[key]; 
    } 
    return object; 
} 

가 유의 사항 : 여기

그리고

_.set 또한 추출 된 형태 lodash의 단순화 된 버전입니다. 그러나 그들은 대부분의 경우에 작동해야합니다.

관련 문제