Not Perform Search Again on Back Angularjs

For the second entry in the "Beyond Umbraco" series, Arlanet CTO and our MVP Lennard Fonteijn is back to evidence us how to hook an existing AngularJS and extending information technology with your own logic 🦯 Simply another way this innovative community can implement custom functionality in the friendliest, most bendiest CMS there is 😉

In my last blog post, I showed you how to detect and claw a directive in the Umbraco Backoffice in club to dispense the template information technology renders.

While writing that post, I already had a 2nd case in mind! I'm happy to go along with you onto the second entry in the "Beyond Umbraco" serial.

In this blog post, I want to show you lot how to hook an existing AngularJS controller and extend it with your own logic. While I call it a technique, that description doesn't practice it complete justice. It's actually a bunch of tricks combined that make this work. Each trick on its own can have multiple applications in general. In the end, these tricks solve the same problem I introduced in the last post: Prevent consummate duplication of existing features while assuasive you to add your own tweaks.

Mini-Umbraco Case Written report: Custom Tag Editor

For ane of my clients, I built an extension into the Media department to allow free categorization and filtering through 50GB of media based on tags. This extension heavily relies on a custom Media Type with a bunch of Tags belongings editors. Overall, the customer was really pleased with the implementation, but mentioned that the Tags belongings editor didn't work as expected when copy/pasting texts containing spaces.

The example to outline the technique is as follows:

  • Umbraco has a Tags property editor. It works fine, merely you want to be able to copy/paste a line of text and parse the spaces as separate tags.

To keep some steps short and to the point, I'll assume you've read the previous post. If non, become ahead and do that now for more background!

Figure1

Figure ane. Animation of the electric current Tags editor in action

Let'south have a look…

I opened upwardly ane of the media items, used Chrome Inspector on i of the Tag editors and institute the ng-controller attribute of the property editor I was looking for: Umbraco.PropertyEditors.TagsController

I then searched for this controller in the Umbraco GitHub repository, but couldn't get whatever results related to it. Ugh! Unfortunately, I've seen this happen occasionally. I know it exists, yet, GitHub for some reason hasn't properly figured that out. You are now left with a few options:

  • Adjust the search term to be slightly less specific.
  • Click through the repository files until you lot find what you lot are looking for; there is logic behind how it's structured.
  • Clone the repository, open up the solution and apply a solution-wide search to find what y'all need.

I chose the starting time option and looked for just "TagsController" instead, which gave me the results I was looking for:

Embed ane. Snippet of tags.html found in Umbraco viii.xi.1

This taught me the Tag holding editor is actually implemented as a directive with the proper noun "umb-tags-editor". Going past AngularJS naming conventions, I would now have to look for "umbTagsEditor" to find the controller behind it, which brought me to this file:

Embed two. Snippet of umbtagseditor.directive.js plant in Umbraco 8.11.ane

And besides the related view:

Embed iii. Snippet of umb-tags-editor.html institute in Umbraco 8.xi.1

In the view, I found the textfield that handles the input, which had a few important pieces of information: ng-model, ng-keydown and ng-blur.

Based on this, I can make the following assumptions:

  • ng-model: All input is stored into the variable "vm.tagToAdd".
  • ng-keydown: "vm.addTagOnEnter" is called every key press.
  • ng-blur: If you click outside the input and finer unfocus the textfield, so "vm.addTag" is chosen.

I made a note of this, dove into the controller code and quickly plant all the pieces of the puzzle:

Embed 4. Another snippet of umbtagseditor.directive.js establish in Umbraco 8.11.one

I can see every time addTagOnEnter is chosen by the keydown-event, it volition check if the keycode of the pressed key is equal to thirteen, which is the number for the Enter key (see https://developer.mozilla.org/en-U.s./docs/Web/API/KeyboardEvent/keyCode).

If Enter is pressed, the addTag role will exist chosen, which will then call addTagInternal with vm.tagToAdd as an argument. I as well know that addTag is chosen when the textfield is unfocused, so this means the addTag role is the way in to add support for spaces!

Hooking the controller

Now that I know what I want to alter in the original code, I tin make a plan for it. Because this is a directive, I can use the same technique I described in the previous web log post. To briefly reiterate the steps:

  • Make an App_Plugin to load a JavaScript file.
  • Claw into the configuration-phase of the "umbraco.directices"-module.
  • Add a decorator to directive, which is "umbTagsEditorDirective" in this case. (Don't forget to suspend "Directive" behind it!)

This results in the following code:

Unlike the previous weblog mail service, I am not going to manipulate the template-property this time around. Instead, I'm going to manipulate the controller-belongings.

At that place are a few things to consider before doing and then:

  • Function versus Object
  • Scoping/Closures

Function versus Object

In this instance, the controller-property is a reference to the controller function I found earlier, which in turn contains the addTag function. During the configuration phase of a directive, an example (object) of the controller does non be all the same. This means I tin can't just practise something like "directive.controller.addTag" to become a reference to the addTag-function.

Scoping/Closures

Next up is the problem of scoping. I have to "extend" the original function (much like a form in Object-Oriented Programming). Both JavaScript itself and AngularJS have multiple ways of doing this:

  • call() or apply()
  • angular.extend() in combination with either of the following AngularJS services:
    • $controller
    • $componentController

…And variations thereof. In the end, all of these approaches take pros and cons in their usage and effectiveness. For case, the $componentController is just a wrapper around the $controller, which is part of the ngMock framework meant for unit testing.

You lot should consider if y'all desire to rely on that in production code. On the other hand, you lot also have to consider if you want to rely on template or controller manipulation for production lawmaking. Whatsoever works and results in the least amount of work, right?

Initially, I used the angular.extend() in combination with the $controller, doing similar things as the $componentController does. While this seemed to work at a first glance, it failed to properly respect the directive bindings causing all sorts of weird problems. Then I went with apply() instead.

At the TODO in the lawmaking above, add together the post-obit:

This takes the references to the original controller function, overrides it with our own version and inside information technology calls the original controller function using apply on the special "this" variable. Whenever AngularJS wants to instantiate a controller for this directive, it volition offset call our office, after which I phone call the original part.

By passing "this" equally the first argument, I am effectively extending the current object with whatever the original controller code is doing. The "arguments" simply pass all incoming arguments equally an array to the original controller lawmaking.

Keep in mind this has to match the original controller lawmaking exactly! If you intend on injecting boosted AngularJS services for apply in your own logic after, information technology'south ameliorate to use call() instead and pass all required arguments manually.

If you were to do a console.log(this) after calling the apply(), it would look exactly like the original and also contains the addTag function I desire to override.

Hooking the function

It's time to add my own logic! Simply like C#, JavaScript has the concept of public- and individual-modifiers when extending classes, even though it is not as apparent.

When I extend the original controller, I but get public admission to the properties the original code explicitly added to "this"-variable (or by proxy using the vm-variable). Considering the original code does "vm.addTag = addTag", "this" at present contains a public belongings which is a reference to the private part addTag. Anything not added to "this" is private and tin can never be accessed, no matter how hard you try.

Luckily, the vm.addTag is exactly what I demand, so let'south claw that too using the same fox every bit we did with the controller:

  • Store the original reference in a variable
  • Replace the holding with a new implementation
  • Telephone call the original office when needed

This pull a fast one on is similar to i in the Reverse Engineering world called a "trampoline hook." You redirect a role call to another function and then back to the original. In a Reverse Engineering situation you would, nevertheless, replace the actual function in memory, whereas I am "just" replacing a property referencing the actual role. This is going to pose a problem later on on, but I'll go back to that!

Below the apply-call in your code, paste the following:

Inside the addTag role I store a reference to the electric current "this" as "vm." I do this because "this" changes depending on which scope y'all are in. Next, I split the tagToAdd variable past a space character and iterate over the resulting array using forEach. Each iteration calls addSingleTag and passes a tag equally an argument. Inside addSingleTag I ready the tagToAdd to the passed value and telephone call the original function. I need to use the "vm" from the outer scope here instead of "this," because "this" has a different value as part of the Each-call.

When I go to my Tags holding in Umbraco, type "hello umbraco" and unfocus the textfield. It adds "howdy" and "umbraco" as 2 carve up tags. Hurray, I'g done!

Well… not quite. When I remove the tags, type "hullo umbraco" again and press Enter, I only become 1 tag with a space. Shoot! What is going on hither? Isn't addTagOnEnter supposed to call addTag?

I've got 99 problems, merely Umbraco ain't one.

Let's take a brief look at the addTagOnEnter code again:

Embed 5. Another snippet of umbtagseditor.directive.js found in Umbraco viii.eleven.one

At a first glance, this seems okay. When Enter is pressed, information technology calls addTag. Recall my story about "trampoline hooks" and how I ominously predicted problems? What I did when I hooked the vm.addToTag part was replace the reference to the private addToTag method. But different a real trampoline part, I didn't actually replace the function itself! Mostly, considering I can't… private means it's really private! This means that when the original code calls a private method directly, and not through the vm.addToTag property, it merely skips our hook.

To fix this, I could open an Issue on the Umbraco Issue Tracker and complain that my hacks are not working considering this controller was never made with extensibility in mind. And I might, some day. Simply both of us are brusque on time and this blog post is already long plenty, so let's just set it ourselves!

Unfortunately, I have to duplicate a flake of code now…

Make a copy of the lawmaking from umbtagseditor.directive.js to a higher place. Add information technology to your lawmaking below the utilize() and replace the addTag call with vm.addTag instead, like so:

If you end up having to exercise this, information technology'south good practice to add together a comment above the code, stating that y'all literally copied information technology from somewhere else. While this might feel silly, you are now also in control of the primal-down event, which opens up some additional possibilities for extension, but I'll leave that to your imagination.

If you do the same examination as before, typing "hello umbraco" followed past pressing Enter, information technology now correctly creates two tags. Finally!

Figure2

Effigy 2. Animation of the extended Tags editor in activity

Wrapping upward

With a lot of words, I showed you a bunch of things:

  • Multiple ways of inheriting code equally if they are classes in Object-Oriented Programming.
  • "Overriding" existing public functions, while still being able to call the original one.
  • That scoping can be annoying, only that a little responsible re-create/pasting goes a long mode.

Regardless of whether or not you are extending Umbraco functionality: if you use AngularJS in your daily evolution, you at present at least know how to extend (your own) controllers and how to open them up for inheritance.

Sources

GitHub repository: https://github.com/LennardF1989/BeyondUmbraco

Gists: https://gist.github.com/LennardF1989/d37f0a6e3159ba3581999117ad717c70

MVP Lennard Fonteijn

Arlanet CTO Lennard Fonteijn

About the Author

Lennard is the CTO of Arlanet, role of 4NG. He is a passionate programmer with over a decade of experience in edifice websites, games and other software. He has been working with Umbraco since version 4.0, is an Umbraco MVP, an Ucommerce MVP and teaches students how to be a Software Engineer at the Amsterdam Academy of Applied Sciences. In his spare time, what'southward left of it besides his numerous personal projects at least, he enjoys to play a game or 2, maybe three.

We're so excited to offer our MVPs the run a risk to showcase their piece of work on our blog. Want to see your name here adjacent?

BECOME AN UMBRACO MVP

kornwhaver.blogspot.com

Source: https://umbraco.com/blog/mvp-blog-beyond-umbraco-2-hooking-angularjs-again/

0 Response to "Not Perform Search Again on Back Angularjs"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel