From 71b339376d9c9d2bce0053e85b67117820dd9f92 Mon Sep 17 00:00:00 2001 From: Chris Thielen Date: Thu, 12 May 2016 18:28:51 -0500 Subject: [PATCH] fix(ng1.component): Allow route-to-component "@" and optional bindings Closes #2708 --- src/common/strings.ts | 6 ++++- src/ng1/viewsBuilder.ts | 31 +++++++++++------------ test/ng1/viewDirectiveSpec.js | 46 +++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/src/common/strings.ts b/src/common/strings.ts index 31f35402c..98641a430 100644 --- a/src/common/strings.ts +++ b/src/common/strings.ts @@ -35,7 +35,11 @@ export function padString(length: number, str: string) { return str; } -export const kebobString = (camelCase: string) => camelCase.replace(/([A-Z])/g, $1 => "-"+$1.toLowerCase()); +export function kebobString(camelCase: string) { + return camelCase + .replace(/^([A-Z])/, $1 => $1.toLowerCase()) // replace first char + .replace(/([A-Z])/g, $1 => "-" + $1.toLowerCase()); // replace rest +} function _toJson(obj) { return JSON.stringify(obj); diff --git a/src/ng1/viewsBuilder.ts b/src/ng1/viewsBuilder.ts index d8da74f6f..6a7a5e2f2 100644 --- a/src/ng1/viewsBuilder.ts +++ b/src/ng1/viewsBuilder.ts @@ -45,12 +45,19 @@ export function ng1ViewsBuilder(state: State) { throw new Error(`Cannot combine: ${compKeys.join("|")} with: ${nonCompKeys.join("|")} in stateview: 'name@${state.name}'`); } - // Dynamically build a template like "" + // Dynamically build a template like "" config.templateProvider = ['$injector', function($injector) { const resolveFor = key => config.bindings && config.bindings[key] || key; const prefix = angular.version.minor >= 3 ? "::" : ""; - let attrs = getComponentInputs($injector, config.component) - .map(key => `${kebobString(key)}='${prefix}$resolve.${resolveFor(key)}'`).join(" "); + const attributeTpl = input => { + var attrName = kebobString(input.name); + var resolveName = resolveFor(input.name); + if (input.type === '@') + return `${attrName}='{{${prefix}$resolve.${resolveName}}}'`; + return `${attrName}='${prefix}$resolve.${resolveName}'`; + }; + + let attrs = getComponentInputs($injector, config.component).map(attributeTpl).join(" "); let kebobName = kebobString(config.component); return `<${kebobName} ${attrs}>`; }]; @@ -70,27 +77,21 @@ export function ng1ViewsBuilder(state: State) { return views; } -// for ng 1.2 style, process the scope: { input: "=foo" } object +// for ng 1.2 style, process the scope: { input: "=foo" } +// for ng 1.3 through ng 1.5, process the component's bindToController: { input: "=foo" } object const scopeBindings = bindingsObj => Object.keys(bindingsObj || {}) - .map(key => [key, /^[=<](.*)/.exec(bindingsObj[key])]) - .filter(tuple => isDefined(tuple[1])) - .map(tuple => tuple[1][1] || tuple[0]); - -// for ng 1.3+ bindToController or 1.5 component style, process a $$bindings object -const bindToCtrlBindings = bindingsObj => Object.keys(bindingsObj || {}) - .filter(key => !!/[=<]/.exec(bindingsObj[key].mode)) - .map(key => bindingsObj[key].attrName); + .map(key => [key, /^([=<@])[?]?(.*)/.exec(bindingsObj[key])]) // [ 'input', [ '=foo', '=', 'foo' ] ] + .filter(tuple => isDefined(tuple) && isDefined(tuple[1])) // skip malformed values + .map(tuple => ({ name: tuple[1][2] || tuple[0], type: tuple[1][1] }));// { name: ('foo' || 'input'), type: '=' } // Given a directive definition, find its object input attributes // Use different properties, depending on the type of directive (component, bindToController, normal) const getBindings = def => { if (isObject(def.bindToController)) return scopeBindings(def.bindToController); - if (def.$$bindings && def.$$bindings.bindToController) return bindToCtrlBindings(def.$$bindings.bindToController); - if (def.$$isolateBindings) return bindToCtrlBindings(def.$$isolateBindings); return scopeBindings(def.scope); }; -// Gets all the directive(s)' inputs ('=' and '<') +// Gets all the directive(s)' inputs ('@', '=', and '<') function getComponentInputs($injector, name) { let cmpDefs = $injector.get(name + "Directive"); // could be multiple if (!cmpDefs || !cmpDefs.length) throw new Error(`Unable to find component named '${name}'`); diff --git a/test/ng1/viewDirectiveSpec.js b/test/ng1/viewDirectiveSpec.js index caade8f78..5c48badec 100644 --- a/test/ng1/viewDirectiveSpec.js +++ b/test/ng1/viewDirectiveSpec.js @@ -804,6 +804,16 @@ describe('angular 1.5+ style .component()', function() { bindings: { status: '<' }, template: '#{{ $ctrl.status }}#' }); + + app.component('bindingTypes', { + bindings: { oneway: '