diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts
index 89d54b6cc5..7345a25952 100644
--- a/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts
+++ b/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts
@@ -178,6 +178,59 @@ describe('compiler compliance: template', () => {
     expectEmit(result.source, template, 'Incorrect template');
   });
 
+  it('should correctly bind to implicit receiver in template', () => {
+    const files = {
+      app: {
+        'spec.ts': `
+          import {Component, NgModule} from '@angular/core';
+
+          @Component({
+            selector: 'my-component',
+            template: \`
+              
+              
+            \`
+          })
+          export class MyComponent {
+            greet(val: any) {} 
+          }
+
+          @NgModule({declarations: [MyComponent]})
+          export class MyModule {}
+        `
+      }
+    };
+
+    const template = `
+      function MyComponent_div_0_Template(rf, ctx) {
+        if (rf & 1) {
+          const $_r2$ = i0.ɵɵgetCurrentView();
+          $r3$.ɵɵelementStart(0, "div", $_c1$);
+          $r3$.ɵɵlistener("click", function MyComponent_div_0_Template_div_click_0_listener($event) {
+            i0.ɵɵrestoreView($_r2$);
+            const $ctx_r1$ = i0.ɵɵnextContext();
+            return $ctx_r1$.greet($ctx_r1$);
+          });
+          $r3$.ɵɵelementEnd();
+        }
+      }
+      // ...
+      function MyComponent_div_1_Template(rf, ctx) {
+        if (rf & 1) {
+          $r3$.ɵɵelement(0, "div", $_c3$);
+        } if (rf & 2) {
+          const $ctx_0$ = i0.ɵɵnextContext();
+          $r3$.ɵɵselect(0);
+          $r3$.ɵɵproperty("id", $ctx_0$);
+        }
+      }
+    `;
+
+    const result = compile(files, angularFiles);
+
+    expectEmit(result.source, template, 'Incorrect template');
+  });
+
   it('should support ngFor context variables', () => {
     const files = {
       app: {
diff --git a/packages/compiler/src/compiler_util/expression_converter.ts b/packages/compiler/src/compiler_util/expression_converter.ts
index 67cc3ceeb8..971aec3223 100644
--- a/packages/compiler/src/compiler_util/expression_converter.ts
+++ b/packages/compiler/src/compiler_util/expression_converter.ts
@@ -13,7 +13,10 @@ import {ParseSourceSpan} from '../parse_util';
 
 export class EventHandlerVars { static event = o.variable('$event'); }
 
-export interface LocalResolver { getLocal(name: string): o.Expression|null; }
+export interface LocalResolver {
+  getLocal(name: string): o.Expression|null;
+  notifyImplicitReceiverUse(): void;
+}
 
 export class ConvertActionBindingResult {
   /**
@@ -99,6 +102,11 @@ export function convertActionBinding(
   const actionStmts: o.Statement[] = [];
   flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts);
   prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
+
+  if (visitor.usesImplicitReceiver) {
+    localResolver.notifyImplicitReceiverUse();
+  }
+
   const lastIndex = actionStmts.length - 1;
   let preventDefaultVar: o.ReadVarExpr = null !;
   if (lastIndex >= 0) {
@@ -160,6 +168,10 @@ export function convertPropertyBinding(
   const outputExpr: o.Expression = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
   const stmts: o.Statement[] = getStatementsFromVisitor(visitor, bindingId);
 
+  if (visitor.usesImplicitReceiver) {
+    localResolver.notifyImplicitReceiverUse();
+  }
+
   if (visitor.temporaryCount === 0 && form == BindingForm.TrySimple) {
     return new ConvertPropertyBindingResult([], outputExpr);
   }
@@ -192,6 +204,10 @@ export function convertUpdateArguments(
   const outputExpr: o.InvokeFunctionExpr =
       expressionWithArgumentsToExtract.visit(visitor, _Mode.Expression);
 
+  if (visitor.usesImplicitReceiver) {
+    localResolver.notifyImplicitReceiverUse();
+  }
+
   const stmts = getStatementsFromVisitor(visitor, bindingId);
 
   // Removing the first argument, because it was a length for ViewEngine, not Ivy.
@@ -290,6 +306,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
   private _resultMap = new Map();
   private _currentTemporary: number = 0;
   public temporaryCount: number = 0;
+  public usesImplicitReceiver: boolean = false;
 
   constructor(
       private _localResolver: LocalResolver, private _implicitReceiver: o.Expression,
@@ -387,6 +404,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
 
   visitImplicitReceiver(ast: cdAst.ImplicitReceiver, mode: _Mode): any {
     ensureExpressionMode(mode, ast);
+    this.usesImplicitReceiver = true;
     return this._implicitReceiver;
   }
 
@@ -462,11 +480,15 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
       return this.convertSafeAccess(ast, leftMostSafe, mode);
     } else {
       const args = this.visitAll(ast.args, _Mode.Expression);
+      const prevUsesImplicitReceiver = this.usesImplicitReceiver;
       let result: any = null;
       const receiver = this._visit(ast.receiver, _Mode.Expression);
       if (receiver === this._implicitReceiver) {
         const varExpr = this._getLocal(ast.name);
         if (varExpr) {
+          // Restore the previous "usesImplicitReceiver" state since the implicit
+          // receiver has been replaced with a resolved local expression.
+          this.usesImplicitReceiver = prevUsesImplicitReceiver;
           result = varExpr.callFn(args);
         }
       }
@@ -492,9 +514,15 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
       return this.convertSafeAccess(ast, leftMostSafe, mode);
     } else {
       let result: any = null;
+      const prevUsesImplicitReceiver = this.usesImplicitReceiver;
       const receiver = this._visit(ast.receiver, _Mode.Expression);
       if (receiver === this._implicitReceiver) {
         result = this._getLocal(ast.name);
+        if (result) {
+          // Restore the previous "usesImplicitReceiver" state since the implicit
+          // receiver has been replaced with a resolved local expression.
+          this.usesImplicitReceiver = prevUsesImplicitReceiver;
+        }
       }
       if (result == null) {
         result = receiver.prop(ast.name);
@@ -505,6 +533,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
 
   visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
     const receiver: o.Expression = this._visit(ast.receiver, _Mode.Expression);
+    const prevUsesImplicitReceiver = this.usesImplicitReceiver;
 
     let varExpr: o.ReadPropExpr|null = null;
     if (receiver === this._implicitReceiver) {
@@ -515,6 +544,9 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
           // to a 'context.property' value and will be used as the target of the
           // write expression.
           varExpr = localExpr;
+          // Restore the previous "usesImplicitReceiver" state since the implicit
+          // receiver has been replaced with a resolved local expression.
+          this.usesImplicitReceiver = prevUsesImplicitReceiver;
         } else {
           // Otherwise it's an error.
           throw new Error('Cannot assign to a reference or variable!');
@@ -753,6 +785,7 @@ function flattenStatements(arg: any, output: o.Statement[]) {
 }
 
 class DefaultLocalResolver implements LocalResolver {
+  notifyImplicitReceiverUse(): void {}
   getLocal(name: string): o.Expression|null {
     if (name === EventHandlerVars.event.name) {
       return EventHandlerVars.event;
diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts
index 5d6378730d..9ec1f5a2b4 100644
--- a/packages/compiler/src/render3/view/compiler.ts
+++ b/packages/compiler/src/render3/view/compiler.ts
@@ -632,7 +632,7 @@ function createHostBindingsFunction(
   const eventBindings =
       bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
   if (eventBindings && eventBindings.length) {
-    const listeners = createHostListeners(bindingContext, eventBindings, name);
+    const listeners = createHostListeners(eventBindings, name);
     createStatements.push(...listeners);
   }
 
@@ -781,16 +781,14 @@ function getBindingNameAndInstruction(binding: ParsedProperty):
   return {bindingName, instruction, isAttribute: !!attrMatches};
 }
 
-function createHostListeners(
-    bindingContext: o.Expression, eventBindings: ParsedEvent[], name?: string): o.Statement[] {
+function createHostListeners(eventBindings: ParsedEvent[], name?: string): o.Statement[] {
   return eventBindings.map(binding => {
     let bindingName = binding.name && sanitizeIdentifier(binding.name);
     const bindingFnName = binding.type === ParsedEventType.Animation ?
         prepareSyntheticListenerFunctionName(bindingName, binding.targetOrPhase) :
         bindingName;
     const handlerName = name && bindingName ? `${name}_${bindingFnName}_HostBindingHandler` : null;
-    const params = prepareEventListenerParameters(
-        BoundEvent.fromParsedEvent(binding), bindingContext, handlerName);
+    const params = prepareEventListenerParameters(BoundEvent.fromParsedEvent(binding), handlerName);
     const instruction =
         binding.type == ParsedEventType.Animation ? R3.componentHostSyntheticListener : R3.listener;
     return o.importExpr(instruction).callFn(params).toStmt();
diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts
index 61477223e5..7c219556d1 100644
--- a/packages/compiler/src/render3/view/template.ts
+++ b/packages/compiler/src/render3/view/template.ts
@@ -59,7 +59,7 @@ export function renderFlagCheckIfStmt(
 }
 
 export function prepareEventListenerParameters(
-    eventAst: t.BoundEvent, bindingContext: o.Expression, handlerName: string | null = null,
+    eventAst: t.BoundEvent, handlerName: string | null = null,
     scope: BindingScope | null = null): o.Expression[] {
   const {type, name, target, phase, handler} = eventAst;
   if (target && !GLOBAL_TARGET_RESOLVERS.has(target)) {
@@ -67,8 +67,11 @@ export function prepareEventListenerParameters(
         Supported list of global targets: ${Array.from(GLOBAL_TARGET_RESOLVERS.keys())}.`);
   }
 
+  const implicitReceiverExpr = (scope === null || scope.bindingLevel === 0) ?
+      o.variable(CONTEXT_NAME) :
+      scope.getOrCreateSharedContextVar(0);
   const bindingExpr = convertActionBinding(
-      scope, bindingContext, handler, 'b', () => error('Unexpected interpolation'),
+      scope, implicitReceiverExpr, handler, 'b', () => error('Unexpected interpolation'),
       eventAst.handlerSpan);
 
   const statements = [];
@@ -152,6 +155,10 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
   // track it to properly adjust projection slot index in the `projection` instruction.
   private _ngContentSelectorsOffset = 0;
 
+  // Expression that should be used as implicit receiver when converting template
+  // expressions to output AST.
+  private _implicitReceiverExpr: o.ReadVarExpr|null = null;
+
   constructor(
       private constantPool: ConstantPool, parentBindingScope: BindingScope, private level = 0,
       private contextName: string|null, private i18nContext: I18nContext|null,
@@ -306,6 +313,9 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
   // LocalResolver
   getLocal(name: string): o.Expression|null { return this._bindingScope.get(name); }
 
+  // LocalResolver
+  notifyImplicitReceiverUse(): void { this._bindingScope.notifyImplicitReceiverUse(); }
+
   i18nTranslate(
       message: i18n.Message, params: {[name: string]: o.Expression} = {}, ref?: o.ReadVarExpr,
       transformFn?: (raw: o.ReadVarExpr) => o.Expression): o.ReadVarExpr {
@@ -448,8 +458,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
     if (bindings.size) {
       bindings.forEach(binding => {
         this.updateInstruction(
-            index, span, R3.i18nExp,
-            () => [this.convertPropertyBinding(o.variable(CONTEXT_NAME), binding)]);
+            index, span, R3.i18nExp, () => [this.convertPropertyBinding(binding)]);
       });
       this.updateInstruction(index, span, R3.i18nApply, [o.literal(index)]);
     }
@@ -598,8 +607,6 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
       this.addNamespaceInstruction(currentNamespace, element);
     }
 
-    const implicit = o.variable(CONTEXT_NAME);
-
     if (this.i18n) {
       this.i18n.appendElement(element.i18n !, elementIndex);
     }
@@ -649,7 +656,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
                 hasBindings = true;
                 this.updateInstruction(
                     elementIndex, element.sourceSpan, R3.i18nExp,
-                    () => [this.convertExpressionBinding(implicit, expression)]);
+                    () => [this.convertExpressionBinding(expression)]);
               });
             }
           }
@@ -671,7 +678,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
       // (things like `styleProp`, `classProp`, etc..) are applied later on in this
       // file
       this.processStylingInstruction(
-          elementIndex, implicit,
+          elementIndex,
           stylingBuilder.buildStylingInstruction(element.sourceSpan, this.constantPool), true);
 
       // Generate Listeners (outputs)
@@ -697,7 +704,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
     for (let i = 0; i <= limit; i++) {
       const instruction = stylingInstructions[i];
       this._bindingSlots += instruction.allocateBindingSlots;
-      this.processStylingInstruction(elementIndex, implicit, instruction, false);
+      this.processStylingInstruction(elementIndex, instruction, false);
     }
 
     // the reason why `undefined` is used is because the renderer understands this as a
@@ -726,7 +733,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
         this.updateInstruction(elementIndex, input.sourceSpan, R3.property, () => {
           return [
             o.literal(bindingName),
-            (hasValue ? this.convertPropertyBinding(implicit, value, /* skipBindFn */ true) :
+            (hasValue ? this.convertPropertyBinding(value, /* skipBindFn */ true) :
                         emptyValueBindInstruction),
           ];
         });
@@ -764,7 +771,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
             } else {
               // [prop]="value"
               this.boundUpdateInstruction(
-                  R3.property, elementIndex, attrName, input, implicit, value, params);
+                  R3.property, elementIndex, attrName, input, value, params);
             }
           } else if (inputType === BindingType.Attribute) {
             if (value instanceof Interpolation && getInterpolationArgsLength(value) > 1) {
@@ -776,14 +783,14 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
               const boundValue = value instanceof Interpolation ? value.expressions[0] : value;
               // [attr.name]="value" or attr.name="{{value}}"
               this.boundUpdateInstruction(
-                  R3.attribute, elementIndex, attrName, input, implicit, boundValue, params);
+                  R3.attribute, elementIndex, attrName, input, boundValue, params);
             }
           } else {
             // class prop
             this.updateInstruction(elementIndex, input.sourceSpan, R3.classProp, () => {
               return [
-                o.literal(elementIndex), o.literal(attrName),
-                this.convertPropertyBinding(implicit, value), ...params
+                o.literal(elementIndex), o.literal(attrName), this.convertPropertyBinding(value),
+                ...params
               ];
             });
           }
@@ -817,9 +824,9 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
    */
   boundUpdateInstruction(
       instruction: o.ExternalReference, elementIndex: number, attrName: string,
-      input: t.BoundAttribute, implicit: o.ReadVarExpr, value: any, params: any[]) {
+      input: t.BoundAttribute, value: any, params: any[]) {
     this.updateInstruction(elementIndex, input.sourceSpan, instruction, () => {
-      return [o.literal(attrName), this.convertPropertyBinding(implicit, value, true), ...params];
+      return [o.literal(attrName), this.convertPropertyBinding(value, true), ...params];
     });
   }
 
@@ -832,9 +839,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
       input: t.BoundAttribute, value: any, params: any[]) {
     this.updateInstruction(
         elementIndex, input.sourceSpan, instruction,
-        () =>
-            [o.literal(attrName),
-             ...this.getUpdateInstructionArguments(o.variable(CONTEXT_NAME), value), ...params]);
+        () => [o.literal(attrName), ...this.getUpdateInstructionArguments(value), ...params]);
   }
 
   visitTemplate(template: t.Template) {
@@ -904,13 +909,12 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
     });
 
     // handle property bindings e.g. ɵɵproperty('ngForOf', ctx.items), et al;
-    const context = o.variable(CONTEXT_NAME);
-    this.templatePropertyBindings(template, templateIndex, context, template.templateAttrs);
+    this.templatePropertyBindings(template, templateIndex, template.templateAttrs);
 
     // Only add normal input/output binding instructions on explicit ng-template elements.
     if (template.tagName === NG_TEMPLATE_TAG_NAME) {
       // Add the input bindings
-      this.templatePropertyBindings(template, templateIndex, context, template.inputs);
+      this.templatePropertyBindings(template, templateIndex, template.inputs);
       // Generate listeners for directive output
       template.outputs.forEach((outputAst: t.BoundEvent) => {
         this.creationInstruction(
@@ -948,12 +952,11 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
     if (value instanceof Interpolation) {
       this.updateInstruction(
           nodeIndex, text.sourceSpan, getTextInterpolationExpression(value),
-          () => this.getUpdateInstructionArguments(o.variable(CONTEXT_NAME), value));
+          () => this.getUpdateInstructionArguments(value));
     } else {
       this.updateInstruction(
           nodeIndex, text.sourceSpan, R3.textBinding,
-          () =>
-              [o.literal(nodeIndex), this.convertPropertyBinding(o.variable(CONTEXT_NAME), value)]);
+          () => [o.literal(nodeIndex), this.convertPropertyBinding(value)]);
     }
   }
 
@@ -1019,8 +1022,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
   private bindingContext() { return `${this._bindingContext++}`; }
 
   private templatePropertyBindings(
-      template: t.Template, templateIndex: number, context: o.ReadVarExpr,
-      attrs: (t.BoundAttribute|t.TextAttribute)[]) {
+      template: t.Template, templateIndex: number, attrs: (t.BoundAttribute|t.TextAttribute)[]) {
     attrs.forEach(input => {
       if (input instanceof t.BoundAttribute) {
         const value = input.value.visit(this._valueConverter);
@@ -1029,7 +1031,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
           this.allocateBindingSlots(value);
           this.updateInstruction(
               templateIndex, template.sourceSpan, R3.property,
-              () => [o.literal(input.name), this.convertPropertyBinding(context, value, true)]);
+              () => [o.literal(input.name), this.convertPropertyBinding(value, true)]);
         }
       }
     });
@@ -1049,10 +1051,10 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
   }
 
   private processStylingInstruction(
-      elementIndex: number, implicit: any, instruction: Instruction|null, createMode: boolean) {
+      elementIndex: number, instruction: Instruction|null, createMode: boolean) {
     if (instruction) {
       const paramsFn = () =>
-          instruction.buildParams(value => this.convertPropertyBinding(implicit, value, true));
+          instruction.buildParams(value => this.convertPropertyBinding(value, true));
       if (createMode) {
         this.creationInstruction(instruction.sourceSpan, instruction.reference, paramsFn);
       } else {
@@ -1090,23 +1092,38 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
     this._bindingSlots += value instanceof Interpolation ? value.expressions.length : 1;
   }
 
-  private convertExpressionBinding(implicit: o.Expression, value: AST): o.Expression {
-    const convertedPropertyBinding =
-        convertPropertyBinding(this, implicit, value, this.bindingContext(), BindingForm.TrySimple);
+  /**
+   * Gets an expression that refers to the implicit receiver. The implicit
+   * receiver is always the root level context.
+   */
+  private getImplicitReceiverExpr(): o.ReadVarExpr {
+    if (this._implicitReceiverExpr) {
+      return this._implicitReceiverExpr;
+    }
+
+    return this._implicitReceiverExpr = this.level === 0 ?
+        o.variable(CONTEXT_NAME) :
+        this._bindingScope.getOrCreateSharedContextVar(0);
+  }
+
+  private convertExpressionBinding(value: AST): o.Expression {
+    const convertedPropertyBinding = convertPropertyBinding(
+        this, this.getImplicitReceiverExpr(), value, this.bindingContext(), BindingForm.TrySimple);
     const valExpr = convertedPropertyBinding.currValExpr;
+
     return o.importExpr(R3.bind).callFn([valExpr]);
   }
 
-  private convertPropertyBinding(implicit: o.Expression, value: AST, skipBindFn?: boolean):
-      o.Expression {
+  private convertPropertyBinding(value: AST, skipBindFn?: boolean): o.Expression {
     const interpolationFn =
         value instanceof Interpolation ? interpolate : () => error('Unexpected interpolation');
 
     const convertedPropertyBinding = convertPropertyBinding(
-        this, implicit, value, this.bindingContext(), BindingForm.TrySimple, interpolationFn);
+        this, this.getImplicitReceiverExpr(), value, this.bindingContext(), BindingForm.TrySimple,
+        interpolationFn);
+    const valExpr = convertedPropertyBinding.currValExpr;
     this._tempVariables.push(...convertedPropertyBinding.stmts);
 
-    const valExpr = convertedPropertyBinding.currValExpr;
     return value instanceof Interpolation || skipBindFn ? valExpr :
                                                           o.importExpr(R3.bind).callFn([valExpr]);
   }
@@ -1115,13 +1132,12 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
    * Gets a list of argument expressions to pass to an update instruction expression. Also updates
    * the temp variables state with temp variables that were identified as needing to be created
    * while visiting the arguments.
-   * @param contextExpression The expression for the context variable used to create arguments
    * @param value The original expression we will be resolving an arguments list from.
    */
-  private getUpdateInstructionArguments(contextExpression: o.Expression, value: AST):
-      o.Expression[] {
+  private getUpdateInstructionArguments(value: AST): o.Expression[] {
     const {args, stmts} =
-        convertUpdateArguments(this, contextExpression, value, this.bindingContext());
+        convertUpdateArguments(this, this.getImplicitReceiverExpr(), value, this.bindingContext());
+
     this._tempVariables.push(...stmts);
     return args;
   }
@@ -1265,8 +1281,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
           sanitizeIdentifier(eventName);
       const handlerName = `${this.templateName}_${tagName}_${bindingFnName}_${index}_listener`;
       const scope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel);
-      const context = o.variable(CONTEXT_NAME);
-      return prepareEventListenerParameters(outputAst, context, handlerName, scope);
+      return prepareEventListenerParameters(outputAst, handlerName, scope);
     };
   }
 }
@@ -1544,14 +1559,38 @@ export class BindingScope implements LocalResolver {
     return this;
   }
 
+  // Implemented as part of LocalResolver.
   getLocal(name: string): (o.Expression|null) { return this.get(name); }
 
+  // Implemented as part of LocalResolver.
+  notifyImplicitReceiverUse(): void {
+    if (this.bindingLevel !== 0) {
+      // Since the implicit receiver is accessed in an embedded view, we need to
+      // ensure that we declare a shared context variable for the current template
+      // in the update variables.
+      this.map.get(SHARED_CONTEXT_KEY + 0) !.declare = true;
+    }
+  }
+
   nestedScope(level: number): BindingScope {
     const newScope = new BindingScope(level, this);
     if (level > 0) newScope.generateSharedContextVar(0);
     return newScope;
   }
 
+  /**
+   * Gets or creates a shared context variable and returns its expression. Note that
+   * this does not mean that the shared variable will be declared. Variables in the
+   * binding scope will be only declared if they are used.
+   */
+  getOrCreateSharedContextVar(retrievalLevel: number): o.ReadVarExpr {
+    const bindingKey = SHARED_CONTEXT_KEY + retrievalLevel;
+    if (!this.map.has(bindingKey)) {
+      this.generateSharedContextVar(retrievalLevel);
+    }
+    return this.map.get(bindingKey) !.lhs;
+  }
+
   getSharedContextName(retrievalLevel: number): o.ReadVarExpr|null {
     const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + retrievalLevel);
     return sharedCtxObj && sharedCtxObj.declare ? sharedCtxObj.lhs : null;
diff --git a/packages/compiler/src/view_compiler/type_check_compiler.ts b/packages/compiler/src/view_compiler/type_check_compiler.ts
index 8d5bfeb6a7..f617c47e51 100644
--- a/packages/compiler/src/view_compiler/type_check_compiler.ts
+++ b/packages/compiler/src/view_compiler/type_check_compiler.ts
@@ -79,6 +79,7 @@ interface Expression {
 const DYNAMIC_VAR_NAME = '_any';
 
 class TypeCheckLocalResolver implements LocalResolver {
+  notifyImplicitReceiverUse(): void {}
   getLocal(name: string): o.Expression|null {
     if (name === EventHandlerVars.event.name) {
       // References to the event should not be type-checked.
@@ -284,6 +285,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
     }
   }
 
+  notifyImplicitReceiverUse(): void {}
   getLocal(name: string): o.Expression|null {
     if (name == EventHandlerVars.event.name) {
       return o.variable(this.getOutputVar(o.BuiltinTypeName.Dynamic));
diff --git a/packages/compiler/src/view_compiler/view_compiler.ts b/packages/compiler/src/view_compiler/view_compiler.ts
index e97e3291f6..8310f7e104 100644
--- a/packages/compiler/src/view_compiler/view_compiler.ts
+++ b/packages/compiler/src/view_compiler/view_compiler.ts
@@ -687,6 +687,12 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
     return null;
   }
 
+  notifyImplicitReceiverUse(): void {
+    // Not needed in View Engine as View Engine walks through the generated
+    // expressions to figure out if the implicit receiver is used and needs
+    // to be generated as part of the pre-update statements.
+  }
+
   private _createLiteralArrayConverter(sourceSpan: ParseSourceSpan, argCount: number):
       BuiltinConverter {
     if (argCount === 0) {
diff --git a/packages/core/test/acceptance/embedded_views_spec.ts b/packages/core/test/acceptance/embedded_views_spec.ts
new file mode 100644
index 0000000000..a4fd8ba31b
--- /dev/null
+++ b/packages/core/test/acceptance/embedded_views_spec.ts
@@ -0,0 +1,75 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {Component, Input} from '@angular/core';
+import {TestBed} from '@angular/core/testing';
+
+describe('embedded views', () => {
+
+  it('should correctly resolve the implicit receiver in expressions', () => {
+    const items: string[] = [];
+
+    @Component({
+      selector: 'child-cmp',
+      template: 'Child',
+    })
+    class ChildCmp {
+      @Input() addItemFn: Function|undefined;
+    }
+
+    @Component({
+      template: ``,
+    })
+    class TestCmp {
+      item: string = 'CmpItem';
+      addItem() { items.push(this.item); }
+    }
+
+    TestBed.configureTestingModule({declarations: [ChildCmp, TestCmp]});
+    const fixture = TestBed.createComponent(TestCmp);
+    fixture.detectChanges();
+
+    const childCmp: ChildCmp = fixture.debugElement.children[0].componentInstance;
+
+    childCmp.addItemFn !();
+    childCmp.addItemFn !();
+
+    expect(items).toEqual(['CmpItem', 'CmpItem']);
+  });
+
+  it('should resolve template input variables through the implicit receiver', () => {
+    @Component({template: `{{this.a}}`})
+    class TestCmp {
+    }
+
+    TestBed.configureTestingModule({declarations: [TestCmp]});
+    const fixture = TestBed.createComponent(TestCmp);
+    fixture.detectChanges();
+
+    expect(fixture.nativeElement.textContent).toBe('true');
+  });
+
+  it('should component instance variables through the implicit receiver', () => {
+    @Component({
+      template: `
+        
+          {{this.myProp}}{{myProp}}
+        `
+    })
+    class TestCmp {
+      myProp = 'Hello';
+    }
+
+    TestBed.configureTestingModule({declarations: [TestCmp]});
+    const fixture = TestBed.createComponent(TestCmp);
+    fixture.detectChanges();
+
+    expect(fixture.nativeElement.textContent).toBe('HelloHello');
+  });
+
+});