Skip to content

Commit

Permalink
fix(routeToComponent): Bind resolves that start with data- or x-
Browse files Browse the repository at this point in the history
Allow routing to components and binding resolves with names like `xComponent` or `dataComponent` or `xResolve` or `dataResolve`

- Prefix all resolve attributes in generated template with `x-`: `<x-mycmp x-data-value="::$resolve.dataValue"></x-mycmp>`
- Prefix all component names in generated template with `x-`: `<x-mycmp></x-mycmp>`

Closes #3276
  • Loading branch information
christopherthielen committed Mar 12, 2017
1 parent 8e7386b commit 4559c32
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 119 deletions.
5 changes: 4 additions & 1 deletion src/directives/viewDirective.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,12 @@ function $ViewDirectiveFill ($compile: ICompileService, $controller: IController
if (isString(cfg.viewDecl.component)) {
let cmp = cfg.viewDecl.component;
let kebobName = kebobString(cmp);
let tagRegexp = new RegExp(`^(x-|data-)?${kebobName}$`, "i");

let getComponentController = () => {
let directiveEl = [].slice.call($element[0].children)
.filter((el: Element) => el && el.tagName && el.tagName.toLowerCase() === kebobName) ;
.filter((el: Element) => el && el.tagName && tagRegexp.exec(el.tagName)) ;

return directiveEl && angular.element(directiveEl).data(`$${cmp}Controller`);
};

Expand Down
10 changes: 5 additions & 5 deletions src/templateFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,13 @@ export class TemplateFactory implements TemplateFactoryProvider {
// then pass that attribute through to the routed component template.
// Prefer ui-view wired mappings to resolve data, unless the resolve was explicitly bound using `bindings:`
if (uiView.attr(attrName) && !bindings[name])
return `${attrName}='${uiView.attr(attrName)}'`;
return `x-${attrName}='${uiView.attr(attrName)}'`;

let resolveName = bindings[name] || name;
// Pre-evaluate the expression for "@" bindings by enclosing in {{ }}
// some-attr="{{ ::$resolve.someResolveName }}"
if (type === '@')
return `${attrName}='{{${prefix}$resolve.${resolveName}}}'`;
return `x-${attrName}='{{${prefix}$resolve.${resolveName}}}'`;

// Wire "&" callbacks to resolves that return a callback function
// Get the result of the resolve (should be a function) and annotate it to get its arguments.
Expand All @@ -165,15 +165,15 @@ export class TemplateFactory implements TemplateFactoryProvider {
let args = fn && services.$injector.annotate(fn) || [];
// account for array style injection, i.e., ['foo', function(foo) {}]
let arrayIdxStr = isArray(fn) ? `[${fn.length - 1}]` : '';
return `${attrName}='$resolve.${resolveName}${arrayIdxStr}(${args.join(",")})'`;
return `x-${attrName}='$resolve.${resolveName}${arrayIdxStr}(${args.join(",")})'`;
}

// some-attr="::$resolve.someResolveName"
return `${attrName}='${prefix}$resolve.${resolveName}'`;
return `x-${attrName}='${prefix}$resolve.${resolveName}'`;
};

let attrs = getComponentBindings(component).map(attributeTpl).join(" ");
let kebobName = kebobString(component);
let kebobName = "x-" + kebobString(component);
return `<${kebobName} ${attrs}></${kebobName}>`;
};
}
Expand Down
34 changes: 34 additions & 0 deletions test/viewDirectiveSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,15 @@ describe('angular 1.5+ style .component()', function() {
template: 'eventCmp',
});

app.component('mydataComponent', {
bindings: { dataUser: '<' },
template: '-{{ $ctrl.dataUser }}-',
});

app.component('dataComponent', {
template: 'DataComponent',
});

app.component('parentCallbackComponent', {
controller: function($rootScope) {
this.handleEvent = function(foo, bar) {
Expand Down Expand Up @@ -1164,6 +1173,31 @@ describe('angular 1.5+ style .component()', function() {
expect(el.text()).toBe('eventCmp');
});

// Test for #3276
it('should route to a component that is prefixed with "data"', function () {
$stateProvider.state('data', {
component: 'dataComponent',
});

let $state = svcs.$state, $q = svcs.$q;
$state.transitionTo('data'); $q.flush();

expect(el.text()).toBe('DataComponent');
});

// Test for #3276
it('should bind a resolve that is prefixed with "data"', function () {
$stateProvider.state('data', {
component: 'mydataComponent',
resolve: { dataUser: () => 'user' }
});

let $state = svcs.$state, $q = svcs.$q;
$state.transitionTo('data'); $q.flush();

expect(el.text()).toBe('-user-');
});

// Test for #3239
it('should pass any bindings (wired from a parent component template via the ui-view) through to the child', inject(function ($rootScope) {
let $state = svcs.$state, $q = svcs.$q;
Expand Down
Loading

0 comments on commit 4559c32

Please sign in to comment.