Redux Example



react-pxq

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider, connect } from 'react-redux'

// React component
class Counter extends Component {
  render() {
    const { value, onIncreaseClick } = this.props
    return (
      <div>
        <span>{value}</span>
        <button onClick={onIncreaseClick}>Increase</button>
      </div>
    )
  }
}

Counter.propTypes = {
  value: PropTypes.number.isRequired,
  onIncreaseClick: PropTypes.func.isRequired
}

// Action
const increaseAction = { type: 'increase' }

// Reducer
function counter(state = { count: 0 }, action) {
  const count = state.count
  switch (action.type) {
    case 'increase':
      return { count: count + 1 }
    default:
      return state
  }
}

// Store
const store = createStore(counter)

// Map Redux state to component props
function mapStateToProps(state) {
  return {
    value: state.count
  }
}

// Map Redux actions to component props
function mapDispatchToProps(dispatch) {
  return {
    onIncreaseClick: () => dispatch(increaseAction)
  }
}

// Connected Component
const App = connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

React Native Proguard Rules

# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Disabling obfuscation is useful if you collect stack traces from production crashes
# (unless you are using a system that supports de-obfuscate the stack traces).
-dontobfuscate

# React Native

# Keep our interfaces so they can be used by other ProGuard rules.
# See http://sourceforge.net/p/proguard/bugs/466/
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters

# Do not strip any method/class that is annotated with @DoNotStrip
-keep @com.facebook.proguard.annotations.DoNotStrip class *
-keepclassmembers class * {
    @com.facebook.proguard.annotations.DoNotStrip *;
}

-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
  void set*(***);
  *** get*();
}

-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
-keep class * extends com.facebook.react.bridge.NativeModule { *; }
-keepclassmembers,includedescriptorclasses class * { native <methods>; }
-keepclassmembers class *  { @com.facebook.react.uimanager.UIProp <fields>; }
-keepclassmembers class *  { @com.facebook.react.uimanager.annotations.ReactProp <methods>; }
-keepclassmembers class *  { @com.facebook.react.uimanager.annotations.ReactPropGroup <methods>; }

-dontwarn com.facebook.react.**

# okhttp

-keepattributes Signature
-keepattributes *Annotation*
-keep class com.squareup.okhttp.** { *; }
-keep interface com.squareup.okhttp.** { *; }
-dontwarn com.squareup.okhttp.**

# okio

-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**

jQuery data binding library or plugin recommendation

原文

look into rivets.js.

rivets is a Lightweight (3.4kb minified and gzipped) and powerful data binding and templating library.

Rivets.js is completely agnostic about your model / controller layer and works well with existing libraries that employ an event-driven model such as Backbone.js and Stapes.js. It ships with a built-in adapter for subscribing to plain JS objects using ES5 natives, however this can be replaced with a Watch.JS adapter or an Object.observe adapter.

Some of the features you get out-of-the-box with Rivets.js:

  • Bi-directional data binding to and from DOM nodes.
  • Computed properties through dependency mapping.
  • Formatters to allow mutating values through piping.
  • Iteration binding for binding items in an array.
  • Custom event handlers to fit your ideal workflow.
  • Uniform APIs for easily extending any of the core concepts.

Rivets uses DOM-based templating system:

Instead of parsing and compiling template strings into HTML, Rivets.js wires up your models directly to existing parts of DOM that contain binding declarations and control flow instructions directly on the DOM nodes. You just pass in your models when binding to the parent DOM node and Rivets.js takes care of the rest.

In short, for example assume you want to display the data in a product object like:

var productInfo= {
 name: "test",
 price: 1000
}

in following HTML:

<ul id="product">
  <li>Name</li>
  <li>Price</li>
</ul>

Your can bind the data using rivets like:

rivets.bind($('#product'), {
  product: productInfo // product will be the alias name inside HTML template
});

And the corresponding rivets template will be:

<ul id="product">
  <li rv-text="product.name"></li>
  <li v-text="product.price"></li>
</ul>

or more semantically:

<ul id="product">
  <li data-rv-text="product.name"></li>
  <li data-rv-text="product.price"></li>
</ul>

The rivets.bind method accepts the template element, the model data, as well as any options you wish to override from the main rivets object (optional)


Or if you are binding an array of product objects:

rivets.bind($('#product'), {
  product: ProductArray // where productArray is an array of products
});

You can use iteration bindings using rv-each like:

<ul class="products" data-rv-each-product="products">
  <li data-rv-text="product.name"></li>
  <li data-rv-text="product.price"></li>
</ul>

rivets will create n number of lists according to the items in array.

There are many more cool features which you can find in the guide.

生于忧患,死于安乐

舜发于畎亩之中,傅说举于版筑之间,胶鬲举 于鱼盐之中,管夷吾举于土,孙叔敖举于海,百里奚举于市。

故天将降大任于是人也,必先苦其心志,劳其筋骨,饿其体肤,空乏其身, 行拂乱其所为,所以动心忍性,曾益其所不能。

人恒过, 然后能改;困于心,衡于虑,而后作;征于色,发于声,而后喻。入则无法家拂士,出则无敌国外患者,国恒亡。

然后知生于忧患而死于安乐也。

How to do stateless (session-less) & cookie-less authentication?

How to do stateless (session-less) & cookie-less authentication?

I’ve seen multiple ways to do this during my stints during application assessments. One of the popular ways is the playing tennis way that you mentioned – sending the username and password in every request to authenticate the user. This, in my opinion, is unsafe, especially if the application isn’t single page. It is also not scalable, especially if you want to add authorization to your app in addition to authentication in the future (although I guess you could build something based on logins too)

One popular, although not completely stateless mechanism (assuming you have JavaScript execution) is to embed the session cookie in the JavaScript. The security guy in me is screaming at this, but it could actually work – every request has a X-Authentication-Token header or something like that, and you map that to a database, file-store in memory, etc. on the backend to validate the user. This token can have a timeout of whatever time you specified, and if it times out, the user has to log in again. It’s fairly scalable – if you store it in a database, its one SQL statement executed, and with the correct indexes, it should take very little time to execute, even with multiple simultaneous users. Load testing here would definitely help though. If I read the question correctly, this would be your encrypted token mechanism – although, I would strongly suggest that you use a cryptographically random token of say 32 characters, versus using a combination of username + password + whatever else – this way, it stays unpredictable, but you can still associate it with the user ID or some such thing.

Whichever you do end up using, ensure it is sent to you safely. HTTPS protects you across the wire, but it doesn’t protect you if you leak the session token via the URL (or worse, credentials via the URL). I would recommend using a header, or if that’s not feasible, sending the token via a POST request every time (this would mean a hidden form field on the user’s browser.) The latter approach of using a POST request should use CSRF defenses, just in case, though I suspect using the token itself might be some sort of a CSRF defense.

Last, but not the least, make sure you have some mechanism in the backend to purge expired tokens. This has been the bane of many applications in the past – a rapidly growing database of authentication tokens which never seems to go away. If you need to support multiple user logins, make sure you either limit the number, or have a shorter time limit on each token. As I said before, load testing may be the answer to this.

There are some other security concerns that I can think of, but they are too broad to be addressed at this stage – if you keep all the use (and abuse) cases in mind, you should probably be able to do a fairly good implementation of this system.

中文相似度匹配算法

基于音形码的中文字符串相似度算法

背景介绍

字符串相似度算法是指通过一定的方法,来计算两个不同字符串之间的相似程度。通常会用一个百分比来衡量字符串之间的相似程度。字符串相似度算法被应用于许多计算场景,在诸如数据清洗,用户输入纠错,推荐系统, 剽窃检测系统,自动评分系统,以及网页搜索和DNA序列匹配这些方向都有着十分广泛的应用。

常见的字符串相似度算法包括编辑距离算法(EditDistance),n-gram算法,JaroWinkler算法以及Soundex算法。本文接下来大略的介绍一下这几种算法,有兴趣的读者可以在互联网找到一些更详细的资料。

最常见的相似度算法为编辑距离算法(EditDistance),该算法将两个字符串的相似度问题,归结为将其中一个字符串转化成另一个字符串所要付出的代价。转化的代价越高,说明两个字符串的相似度越低。通常可以选择的转化方式包含插入,替换以及删除。

N-Gram算法则是基于这样的一个假设: 即在字符串中第n个词的出现只与前面n-1个词相关,而与其他任何词都不相关,整个字符串出现的概率就是各个词出现的概率的乘积。 N-gram本身也代表目标字符串中长度为n的子串,举例,“ARM”在“ARMY”中,便是一个3-gram。当两个字符串中,相同的n-gram越多时,两个字串就会被认为更加相似。

Jaro Winkler则是将n-gram算法更进了一步。将n-gram中的不匹配的部分同时进行了换位的考虑,使得能获得更准确的相似程度。JaroWinkler在比较两个较短字符串的情况下,能够取得很好的结果。

Soundex算法与前面几种都不太相同。该算法的特点是,它所关注的问题并非两个字符串文本上的相似程度,而是发音的近似。首先,该算法会将两个字符串分别通过一定的hash算法转换成一个hash值,该值由四个字符构成,第一个字符为英文字母,后面三个为数字。进行转化的hash算法并非随机选取,而是利用了该拉丁文字符串的读音近似值。

当获得了两个字符串的读音上的hash值之后,该算法再对两个hash的相似度进行计算,便可以得出输入字符串的读音相似度。

Soundex算法的另一个应用场景在于,用户进行模糊查询时,可以通过Soundex值进行过滤,以提高查询性能。

问题描述

这些常见的字符串相似度算法在处理拉丁文字的文本匹配时,都能起到非常好的效果。它们本身最初的发明者也是为了解决拉丁文字中遇到的问题。然而,对于象形文字相似度计算,比如说中文,这些算法就显得捉襟见肘了。

举例来说明:

南通市 – 难通市 – 北通市

对于编辑距离算法而言,南通市和难通市之间的相似度,与南通市和北通市的相似度,是一模一样的,因为两者都需要付出相同的代价来转换成另一个。 使用N-Gram算法,得出的也是相同的结果。然而,对于熟悉汉字的人来讲,南通市和难通市理应有着更加接近的相似度。因为两者的发音完全相同。

既然是发音的问题,那么有没有可能利用Soundex算法来解决呢? 目前看来,还是无法做到,因为Soundex算法更多的是针对拉丁文字的发音,对于中文而言,Soundex算法无能为力。

如果说这个例子仅仅说明是发音上的相同,拉丁文字也有相似的问题,那么下面这个例子则描述了只存在于象形文字中的相似度问题:

彬彬有礼 – 杉杉有礼

如果站在解决拉丁文字的相似度的角度来看,那么这两个字符串大约只有50%的相似度,因为在四个字符中,就有两个字符是完全不同的。这两个字符不仅外形不同,即使是发音,也是完全不同。

然而对于熟悉汉字的人来说,这两个输入应该有着相当高的相似度。因为第一个和第二个字符,虽然不同,却有着十分接近的字形。

这样的案例常常出现在录入手写输入时,举例来说,某个顾客填写了一张快递单:

江苏省 南通市 紫琅路 100号

当快递员签收快递时,可能需要在系统中录入该地址,又或者,这家快递公司采用的是先进的扫描仪器,可以将地址通过扫描仪扫入。假设顾客的字写的十分潦草,那么快递员粗心大意或者不够智能的扫描仪,都有可能导致下面的文字被错误的录入:

江苏省 南通市 紫娘路 100号

如何识别这样的相似中文词组,在现有的算法中,很难解决该问题。

中文的字符串相似度有着其独特的特征,不同于其他任何语言,而在现实世界中,我们又却是时常面临这样的问题,正如我们刚才看到的例子,其中最常见的场景便是中文纠错。

我们急需要需找一种新型的算法来解决该问题。

问题分析

想要解决中文字符串的相似度匹配问题,并且量化中文相似度的结果,必须首先对单个汉字的特性有一定的了解。“琅“和“狼”的相似度,跟“琅”和“娘”之间的相似度比较,究竟哪个更高一些,量化的依据是什么?“篮”和“南”呢?他们之间有相似之处么?只有把这些问题都搞清楚了,我们才能设计出优秀的算法,来计算中文字符串之间的相似度。

经过长时间的调研和准备,在工作中不断的思考总结遇到的中文相似度的问题,我们做出如下的总结,中文的相似度问题,主要归结在三个方面。

同音字

汉字中的同音字可谓是外国人学习中文的一大难题,两个截然不同的汉字,可能有着相同的发音。

当我们对两个汉字进行相似度匹配时,发音的相同或是相近,应当在考虑之列。

对于同音字,如果仅仅考虑其发音的相似程度,那么提供这样的一个相似度算法还是十分容易的,只需要现将汉字转化成其对应的拼音,再进行传统的相似度匹配算法,譬如编辑距离算法,即可达到很好的效果。

方言易混淆发音字

在中国的各个省市中,不同地区有着各自截然不同的方言。这也导致了一些口音很重的地区无法识别一些拼音之间的区别。

最常见的例子便是,许多南方人很难分别“L”和“N”,他们常常会将这两个音弄混,将“篮球”读作“南球”,而“刘德华”就变成了“牛德华”。

解决方言易混淆发音字的办法和同音字的方法很相似,只需要在将汉字转化成拼音之后,再对一些易混淆的音标再进行一次转化,然后再去识别他们的相似度即可。

当然,也可以在计算近似度的时候,给易混淆音标设置一个相对较高的比值,也可以解决该问题。

还有些常见的易混淆音标包括:

“AN” – “ANG”

“Z” – “ZH”

“C” – “CH”

“EN” – “ENG”

字形相似

最后一种相似度问题,同时也是最难解决的问题,便是汉字字形上的相似。

汉字,作为世界上仅存的几种象形文字之一,有着和世界主流使用的拉丁语系截然不同的表现形式。拉丁文字作为一种拼音文字,在于表音,即文字形态表示了它的发音。而象形文字,则是表意文字,一个汉字本身,便表达了它所隐含的意思。

在文章开篇中,我们所提到的所有相似度匹配算法,都无法恰当的区分两个不同汉字之间字形上的异同,更罔论计算他们的相似度了。

对于这个问题,一种朴素的思想,便是首先将汉字转化成一组的字母数字的序列,而这个转化所用到的hash算法必须能够将该汉字的字形特征保留下来。利用这样的转化,我们便将汉字字形的相似度问题,变成了两组字母数字序列的相似度问题。而这正是传统相似度匹配算法的强项。

这种解决方案的核心,就在于找到一个恰当的hash算法,能够将汉字进行适当的转化,并在转化结果中,保留住汉字的字形特征。

在另一篇论文中,作者提到了使用一种名为四角编码的汉字检字法来实现这样的算法。四角算法是由王云五于1925年发明,这种编码方式根据汉字所含的单笔或复笔对汉字进行编号,取汉字的左上角,右上角,左下角以及右下角四个角的笔形,将汉字转化成最多五位的阿拉伯数字。 通过将汉字转化成四角编码,再对四角编码的相似度进行计算,便可以得出两个汉字在字形上的相似程度。

关于四角号码的编码方式,并非本文的重点,感兴趣的读者,可以访问站点:http://baike.baidu.com/view/67253.htm?fr=aladdin以了解更多信息。

利用四角编码计算汉字相似度,可以在一定程度上解决形近字的问题。

然而四角编码也有其自身的问题,由于只取汉字的四角笔形,有些外形截然不同的汉字,因为四角结构相同,也拥有同样的四角编码。

举例说明:

量 –  6010

日 –  6010

即使是从未学过中文的人,也能一眼看出这两个字形上的差异,如果我们仅仅使用四角编码,则会得出这两个汉字相似度为100%的可笑结论。

综合以上关于汉字相似度的三种问题,我们发现,每种解决方案都旨在解决整个问题集的的一个子集。这种分不同场景的做法,常常会造成使用者的困扰,因此,我们在想,是否存在一种方法,能够将这三种解决方案合而为一,取其优点而去其缺点,一次性彻底解决中文的相似度问题?

这个问题引发了我的思考。

音形码(SoundShape Code,SSC)

为了解决文章上面描述的问题,我在工作中积累相关经验,并开发出了音形码这一汉字编码方式,来解决中文的相似度算法问题。

首先,什么是音形码?音形码是一种汉字的编码方式,该编码将一个汉字转化成一个十位字母数字序列,并在一定程度上保留了该汉字的发音及字形的特征。

下图阐述了音形码的序列中每一位的含义:

整个音形码共分两部分,第一部分是音码部分,主要覆盖了韵母,声母,补码以及声调的内容。

第一位,是韵母位,通过简单的替代规则,将汉字的韵母部分映射到一个字符位。汉字的拼音中一共有24种韵母,其中部分为了后期计算目的,采用相同的字符来替代,以下是一张完整的匹配表:

你会发现我们对于an和ang,所使用的是同一种转化,目的便是为了再后期计算相似度的时候,将这种差异弱化。对于没有这种需求的应用来说,完全可以自行生成映射表。

第二位是声母位,同样的,也是利用一张替换表来将声母转换成字符:

可以看到,z和zh用的也是相同的转化。

第三位则是补码,通常用于当声母和韵母之间还有一个辅音的时候,采用的是韵母表相同的替代规则。

第四位是声调位,分别用1,2,3,4来替代汉字中的四声。

第二部分是字形码。

第一位被称为结构位,根据汉字的不同结构,用一个字符来表示该汉字的结构。

接下来的四位,则依然是借用了四角编码,来描述该汉字的形态。由于四角编码表过长,在这里就不一一列举了。

最后一位,是汉字的笔画数位, 从一到九,分别代表该汉字的笔画为一到九,接下来是A代表10位,B代表11位,并依次类推。 Z代表35位,以及任何超过35位的都用z。

举例说明:汉字 “琅”,它的音形码编码是:

通过这样的方式,将汉字首先转换成了一系列的字符序列,这样我们就可以采用一定的办法,来计算他们的相似度。

单字相似度计算

对于单字的相似度匹配,我们采用了比较复杂的计算公式,以期获得一个比较好的计算结果。

P代表音码的相似度,S代表形码的相似度,两者各占整个单字相似度的50%。

单独拆解音码相似度和形码相似度:

 

这里我重新定义了 和 的含义已适应该算法,用于代表字符比较操作, 表示,若两字符相同,则返回1,不同,则返回0. 表示,若俩字符相同,则返回1, 两字符不同,则返回-1.

将两个公式进行合并,便得到最终的计算单字相似度的算法:

先看P部分,声母部分占据了整个音码相似度的60%,补码为30%, 而声调部分为10%。于此同时,韵母部分对最终的相似度起到 的调整作用。

形码部分的算法很类似,四位四角编码在形码部分算法中占据相同的比重,而整个四角编码在形码部分中则占据70%的比重,而笔画数则占据了30%的比重。最终,字形结构部分与韵母部分一致,起到了 的调整作用。

我们以“琅”,“狼”和“娘”三字举例。

“琅”字的音形码为:F70211313B

“狼”字的音型码为:F70214323A

“娘”字的音型码为:F74214343A

根据我们刚才所描述的算法,可以得出,“琅”和“狼”的相似度为88.75%。 而“琅”和“娘”的相似度为83.75%

应用场景

单字相似度的计算并非十分有用,毕竟当两个非常大的字符串进行比较时,两个字之间差异程度的细微差别,整体的相似度结果影响不是很大。

然而在某些场景下,诸如较短字符串的比较,或者是中文纠错的时候,单字相似度的算法则可以起到非常大的作用。

举例来说,用户通过搜索引擎来检索一个短语:“紫娘路”, 而在搜索引擎的词库中,并没有能够发现任何匹配的字符串,相应的,找出了两个与其类似的字符串:

“紫琅路”

“紫薇路”

此时,目前的搜索引擎系统无法区别出这两个字符串与用户输入哪个更加接近,因而无法向用户做出更好的推荐。 相应的,使用本文描述的中文相似度算法,便可以算出,“琅”和“狼”的相似度为88.75%(前文已得出)。 “娘”和“薇”(音形码: 8K0114424G)的相似度为14.3%。由此可以得出,“紫琅路”与输入数据较为接近。

另一种常见应用场景为,服务提供者拥有巨大的词库,用户输入一个错误数据之后,如何尽快的找出所有与其十分接近的词。

在绝对匹配的情况下,做法通常为,为词库中的每一个词,计算出一个hash值,再将hash-字串对插入到一张hash表中。当用户输入一个字串时,现将该字串的hash值计算出,再去表中进行匹配。

这种做法对于绝对匹配而言,效率很高,然而对于模糊查询来说,则毫无用武之地。用户只能一个字符串一个字符串的做相似度比较算法,来选出最佳的结果。该算法的时间复杂度则达到了O(n)。

为了解决这个问题,我们可以设计一种hash值的计算方法,使得相似的字符串拥有相同的hash值,这样当用户的字符串输入时,就可以轻易的找到一群与之十分相似的字符串,再对此进行一一比较,可以将性能提升到最大。只要算法选取合适,性能甚至可以达到O(1)。

而这样的方法就隐藏在音形码的编码当中。

对任意字符串,取每一位字符的音形码的第一位(韵母)和第五位(结构),拼成一个字符串,作为该字符串的hash值,通过这样的方式,我们可以以下字符串进行转化:

“紫琅路”: 41GE5E

“紫娘路”: 41GE5E

“紫薇路”: 41815E

当用户输入“紫狼路”时,将会被转化成:41GE5E,从而与“紫琅路”以及“紫娘路”的hash值一致。再通过更细节的比较,可以得出“紫琅路”为最优结果。

当然,不同的应用可以选取不同的音形码的位数,来得到对应用最合适的hash值。这完全可以根据需求来定制化。

字符串相似度计算

字串相似度的计算可以通过直接将字符串中的每个汉字转化为音形码,再将所有音形码合并起来进行EditDistance算法比较,即可获得。

因为中文的大字符串的比较算法,即使是EditDistance也可以得到较好的结果,在这里就不详细描述了,有兴趣的读者可以自行研究。

后续

整个算法源于我在开发公司的某个实体解析的产品中总结的经验,当时因为遇到这样的问题,却没有很好的解决办法。后来,在公司组织的编程马拉松竞赛中,我便选择了这样的课题来研究,并获得了很好的结果。

该算法还有着这样那样的缺陷,比如音形码过长问题,字串错位如何计算相似度等。但是我想,不能总等一切问题都解决再来做这些工作,而是一步解决一个问题的来不断前进。因此也希望借用这篇文章,给大家一个启发,为中文的相似度算法做出自己的贡献,那它的目的也就达到了。

 

原文

相关连接:

https://www.ioiogoo.cn/2017/07/10/浅谈基于模糊音的中文匹配算法/

https://github.com/iamxiatian/xsimilarity

how-do-i-in-angularjs-if-i-have-a-jquery-background

https://stackoverflow.com/questions/14994391/thinking-in-angularjs-if-i-have-a-jquery-background

http://hanzheng.github.io/tech/angularjs/2013/10/28/translate-how-do-i-in-angularjs-if-i-have-a-jquery-background.html

1. Don’t design your page, and then change it with DOM manipulations

In jQuery, you design a page, and then you make it dynamic. This is because jQuery was designed for augmentation and has grown incredibly from that simple premise.

But in AngularJS, you must start from the ground up with your architecture in mind. Instead of starting by thinking “I have this piece of the DOM and I want to make it do X”, you have to start with what you want to accomplish, then go about designing your application, and then finally go about designing your view.

2. Don’t augment jQuery with AngularJS

Similarly, don’t start with the idea that jQuery does X, Y, and Z, so I’ll just add AngularJS on top of that for models and controllers. This is really tempting when you’re just starting out, which is why I always recommend that new AngularJS developers don’t use jQuery at all, at least until they get used to doing things the “Angular Way”.

I’ve seen many developers here and on the mailing list create these elaborate solutions with jQuery plugins of 150 or 200 lines of code that they then glue into AngularJS with a collection of callbacks and $applys that are confusing and convoluted; but they eventually get it working! The problem is that in most cases that jQuery plugin could be rewritten in AngularJS in a fraction of the code, where suddenly everything becomes comprehensible and straightforward.

The bottom line is this: when solutioning, first “think in AngularJS”; if you can’t think of a solution, ask the community; if after all of that there is no easy solution, then feel free to reach for the jQuery. But don’t let jQuery become a crutch or you’ll never master AngularJS.

3. Always think in terms of architecture

First know that single-page applications are applications. They’re not webpages. So we need to think like a server-side developer in addition to thinking like a client-side developer. We have to think about how to divide our application into individual, extensible, testable components.

So then how do you do that? How do you “think in AngularJS”? Here are some general principles, contrasted with jQuery.

The view is the “official record”

In jQuery, we programmatically change the view. We could have a dropdown menu defined as a ullike so:

<ul class="main-menu">
    <li class="active">
        <a href="#/home">Home</a>
    </li>
    <li>
        <a href="#/menu1">Menu 1</a>
        <ul>
            <li><a href="#/sm1">Submenu 1</a></li>
            <li><a href="#/sm2">Submenu 2</a></li>
            <li><a href="#/sm3">Submenu 3</a></li>
        </ul>
    </li>
    <li>
        <a href="#/home">Menu 2</a>
    </li>
</ul>

In jQuery, in our application logic, we would activate it with something like:

$('.main-menu').dropdownMenu();

When we just look at the view, it’s not immediately obvious that there is any functionality here. For small applications, that’s fine. But for non-trivial applications, things quickly get confusing and hard to maintain.

In AngularJS, though, the view is the official record of view-based functionality. Our ul declaration would look like this instead:

<ul class="main-menu" dropdown-menu>
    ...
</ul>

These two do the same thing, but in the AngularJS version anyone looking at the template knows what’s supposed to happen. Whenever a new member of the development team comes on board, she can look at this and then know that there is a directive called dropdownMenu operating on it; she doesn’t need to intuit the right answer or sift through any code. The view told us what was supposed to happen. Much cleaner.

Developers new to AngularJS often ask a question like: how do I find all links of a specific kind and add a directive onto them. The developer is always flabbergasted when we reply: you don’t. But the reason you don’t do that is that this is like half-jQuery, half-AngularJS, and no good. The problem here is that the developer is trying to “do jQuery” in the context of AngularJS. That’s never going to work well. The view is the official record. Outside of a directive (more on this below), you never, ever, never change the DOM. And directives are applied in the view, so intent is clear.

Remember: don’t design, and then mark up. You must architect, and then design.

Data binding

This is by far one of the most awesome features of AngularJS and cuts out a lot of the need to do the kinds of DOM manipulations I mentioned in the previous section. AngularJS will automatically update your view so you don’t have to! In jQuery, we respond to events and then update content. Something like:

$.ajax({
  url: '/myEndpoint.json',
  success: function ( data, status ) {
    $('ul#log').append('<li>Data Received!</li>');
  }
});

For a view that looks like this:

<ul class="messages" id="log">
</ul>

Apart from mixing concerns, we also have the same problems of signifying intent that I mentioned before. But more importantly, we had to manually reference and update a DOM node. And if we want to delete a log entry, we have to code against the DOM for that too. How do we test the logic apart from the DOM? And what if we want to change the presentation?

This a little messy and a trifle frail. But in AngularJS, we can do this:

$http( '/myEndpoint.json' ).then( function ( response ) {
    $scope.log.push( { msg: 'Data Received!' } );
});

And our view can look like this:

<ul class="messages">
    <li ng-repeat="entry in log">{{ entry.msg }}</li>
</ul>

But for that matter, our view could look like this:

<div class="messages">
    <div class="alert" ng-repeat="entry in log">
        {{ entry.msg }}
    </div>
</div>

And now instead of using an unordered list, we’re using Bootstrap alert boxes. And we never had to change the controller code! But more importantly, no matter where or how the log gets updated, the view will change too. Automatically. Neat!

Though I didn’t show it here, the data binding is two-way. So those log messages could also be editable in the view just by doing this: <input ng-model="entry.msg" />. And there was much rejoicing.

Distinct model layer

In jQuery, the DOM is kind of like the model. But in AngularJS, we have a separate model layer that we can manage in any way we want, completely independently from the view. This helps for the above data binding, maintains separation of concerns, and introduces far greater testability. Other answers mentioned this point, so I’ll just leave it at that.

Separation of concerns

And all of the above tie into this over-arching theme: keep your concerns separate. Your view acts as the official record of what is supposed to happen (for the most part); your model represents your data; you have a service layer to perform reusable tasks; you do DOM manipulation and augment your view with directives; and you glue it all together with controllers. This was also mentioned in other answers, and the only thing I would add pertains to testability, which I discuss in another section below.

Dependency injection

To help us out with separation of concerns is dependency injection (DI). If you come from a server-side language (from Java to PHP) you’re probably familiar with this concept already, but if you’re a client-side guy coming from jQuery, this concept can seem anything from silly to superfluous to hipster. But it’s not. 🙂

From a broad perspective, DI means that you can declare components very freely and then from any other component, just ask for an instance of it and it will be granted. You don’t have to know about loading order, or file locations, or anything like that. The power may not immediately be visible, but I’ll provide just one (common) example: testing.

Let’s say in our application, we require a service that implements server-side storage through a REST API and, depending on application state, local storage as well. When running tests on our controllers, we don’t want to have to communicate with the server – we’re testing the controller, after all. We can just add a mock service of the same name as our original component, and the injector will ensure that our controller gets the fake one automatically – our controller doesn’t and needn’t know the difference.

Speaking of testing…

4. Test-driven development – always

This is really part of section 3 on architecture, but it’s so important that I’m putting it as its own top-level section.

Out of all of the many jQuery plugins you’ve seen, used, or written, how many of them had an accompanying test suite? Not very many because jQuery isn’t very amenable to that. But AngularJS is.

In jQuery, the only way to test is often to create the component independently with a sample/demo page against which our tests can perform DOM manipulation. So then we have to develop a component separately and then integrate it into our application. How inconvenient! So much of the time, when developing with jQuery, we opt for iterative instead of test-driven development. And who could blame us?

But because we have separation of concerns, we can do test-driven development iteratively in AngularJS! For example, let’s say we want a super-simple directive to indicate in our menu what our current route is. We can declare what we want in the view of our application:

<a href="/hello" when-active>Hello</a>

Okay, now we can write a test for the non-existent when-active directive:

it( 'should add "active" when the route changes', inject(function() {
    var elm = $compile( '<a href="/hello" when-active>Hello</a>' )( $scope );

    $location.path('/not-matching');
    expect( elm.hasClass('active') ).toBeFalsey();

    $location.path( '/hello' );
    expect( elm.hasClass('active') ).toBeTruthy();
}));

And when we run our test, we can confirm that it fails. Only now should we create our directive:

.directive( 'whenActive', function ( $location ) {
    return {
        scope: true,
        link: function ( scope, element, attrs ) {
            scope.$on( '$routeChangeSuccess', function () {
                if ( $location.path() == element.attr( 'href' ) ) {
                    element.addClass( 'active' );
                }
                else {
                    element.removeClass( 'active' );
                }
            });
        }
    };
});

Our test now passes and our menu performs as requested. Our development is both iterative and test-driven. Wicked-cool.

5. Conceptually, directives are not packaged jQuery

You’ll often hear “only do DOM manipulation in a directive”. This is a necessity. Treat it with due deference!

But let’s dive a little deeper…

Some directives just decorate what’s already in the view (think ngClass) and therefore sometimes do DOM manipulation straight away and then are basically done. But if a directive is like a “widget” and has a template, it should also respect separation of concerns. That is, the template too should remain largely independent from its implementation in the link and controller functions.

AngularJS comes with an entire set of tools to make this very easy; with ngClass we can dynamically update the class; ngModel allows two-way data binding; ngShow and ngHideprogrammatically show or hide an element; and many more – including the ones we write ourselves. In other words, we can do all kinds of awesomeness without DOM manipulation. The less DOM manipulation, the easier directives are to test, the easier they are to style, the easier they are to change in the future, and the more re-usable and distributable they are.

I see lots of developers new to AngularJS using directives as the place to throw a bunch of jQuery. In other words, they think “since I can’t do DOM manipulation in the controller, I’ll take that code put it in a directive”. While that certainly is much better, it’s often still wrong.

Think of the logger we programmed in section 3. Even if we put that in a directive, we still want to do it the “Angular Way”. It still doesn’t take any DOM manipulation! There are lots of times when DOM manipulation is necessary, but it’s a lot rarer than you think! Before doing DOM manipulation anywhere in your application, ask yourself if you really need to. There might be a better way.

Here’s a quick example that shows the pattern I see most frequently. We want a toggleable button. (Note: this example is a little contrived and a skosh verbose to represent more complicated cases that are solved in exactly the same way.)

.directive( 'myDirective', function () {
    return {
        template: '<a class="btn">Toggle me!</a>',
        link: function ( scope, element, attrs ) {
            var on = false;

            $(element).click( function () {
                on = !on;
                $(element).toggleClass('active', on);
            });
        }
    };
});

There are a few things wrong with this:

  1. First, jQuery was never necessary. There’s nothing we did here that needed jQuery at all!
  2. Second, even if we already have jQuery on our page, there’s no reason to use it here; we can simply use angular.element and our component will still work when dropped into a project that doesn’t have jQuery.
  3. Third, even assuming jQuery was required for this directive to work, jqLite (angular.element) will always use jQuery if it was loaded! So we needn’t use the $ – we can just use angular.element.
  4. Fourth, closely related to the third, is that jqLite elements needn’t be wrapped in $ – the elementthat is passed to the link function would already be a jQuery element!
  5. And fifth, which we’ve mentioned in previous sections, why are we mixing template stuff into our logic?

This directive can be rewritten (even for very complicated cases!) much more simply like so:

.directive( 'myDirective', function () {
    return {
        scope: true,
        template: '<a class="btn" ng-class="{active: on}" ng-click="toggle()">Toggle me!</a>',
        link: function ( scope, element, attrs ) {
            scope.on = false;

            scope.toggle = function () {
                scope.on = !scope.on;
            };
        }
    };
});

Again, the template stuff is in the template, so you (or your users) can easily swap it out for one that meets any style necessary, and the logic never had to be touched. Reusability – boom!

And there are still all those other benefits, like testing – it’s easy! No matter what’s in the template, the directive’s internal API is never touched, so refactoring is easy. You can change the template as much as you want without touching the directive. And no matter what you change, your tests still pass.

w00t!

So if directives aren’t just collections of jQuery-like functions, what are they? Directives are actually extensions of HTML. If HTML doesn’t do something you need it to do, you write a directive to do it for you, and then use it just as if it was part of HTML.

Put another way, if AngularJS doesn’t do something out of the box, think how the team would accomplish it to fit right in with ngClick, ngClass, et al.

Summary

Don’t even use jQuery. Don’t even include it. It will hold you back. And when you come to a problem that you think you know how to solve in jQuery already, before you reach for the $, try to think about how to do it within the confines the AngularJS. If you don’t know, ask! 19 times out of 20, the best way to do it doesn’t need jQuery and to try to solve it with jQuery results in more work for you.

Dynamic/Runtime Log level configuration

There is often a need to modify/change the log level of your (J2EE) application or parts of your application, without a server restart. These may be done for various purposes, debugging being one very obvious need.

Here’s a simple way to achieve the same, wherein we once you have deployed the EAR (if your WAR is wrapped within it) or WAR, you can simply place a JSP file at the root folder of your WAR file, and viola!, you have a custom log level configuration console.

In a typical JBOSS installation this would be placed under : \server\\tmp\deploy\\

In a typical WAS installation this would be placed under : \AppServer\profiles\\installedApps\\\

Of course, you should have configured the log4j in your application properly.

Here is the configureLogging.jsp:

<%@ page language="java" contentType="text/html;charset=UTF-8" %>
<%@ page import="org.apache.log4j.Level" %>
<%@ page import="org.apache.log4j.LogManager" %>
<%@ page import="org.apache.log4j.Logger" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Enumeration" %>
<%@ page import="java.util.Set" %>
<%@ page import="java.util.Arrays" %>
<html>
<head>
    <title>Log Level Configuration</title>
    <style type="text/css">
        <!--
        #content {
            margin: 0px;
            padding: 0px;
            text-align: center;
            background-color: green;
            border: 1px solid #000;
            width: 100%;
        }

        body {
            position: relative;
            margin: 10px;
            padding: 0px;
            color: #333;
        }

        h1 {
            margin-top: 20px;
            font: 1.5em Verdana, Arial, Helvetica sans-serif;
        }

        h2 {
            margin-top: 10px;
            font: 0.75em Verdana, Arial, Helvetica sans-serif;
            text-align: left;
        }

        a, a:link, a:visited, a:active {
            color: blue;
            text-decoration: none;
            text-transform: uppercase;
        }

        table {
            width: 100%;
            background-color: #000;
            padding: 3px;
            border: 0px;
        }

        th {
            font-size: 0.75em;
            background-color: #eee;
            color: #000;
            padding-left: 5px;
            text-align: center;
            border: 1px solid #eee;
            white-space: nowrap;
        }

        td {
            font-size: 0.75em;
            background-color: #fff;
            white-space: nowrap;
        }

        td.center {
            font-size: 0.75em;
            background-color: #fff;
            text-align: center;
            white-space: nowrap;
        }

        .filterForm {
            font-size: 0.9em;
            background-color: #000;
            color: #fff;
            padding-left: 5px;
            text-align: left;
            border: 1px solid #000;
            white-space: nowrap;
        }

        .filterText {
            font-size: 0.75em;
            background-color: #ccc;
            color: #000;
            text-align: left;
            border: 1px solid #ccc;
            white-space: nowrap;
        }

        .filterButton {
            font-size: 0.75em;
            background-color: brown;
            color: white;
            padding-left: 5px;
            padding-right: 5px;
            text-align: center;
            border: 1px solid #ccc;
            width: 100px;
            white-space: nowrap;
        }

        – >
    </style>
</head>
<body onLoad="javascript:document.logFilterForm.logNameFilter.focus();">

    <%
        String containsFilter = "Contains";
        String beginsWithFilter = "Begins With";
        String[] logLevels = { "debug", "info", "warn", "error", "fatal", "off" };
        String targetOperation = (String) request.getParameter("operation");
        String targetLogger = (String) request.getParameter("logger");
        String targetLogLevel = (String) request.getParameter("newLogLevel");
        String logNameFilter = (String) request.getParameter("logNameFilter");
        String logNameFilterType = (String) request.getParameter("logNameFilterType");
    %>
<div id="content">
    <h1>Log Level Configuration</h1>

    <div>

        <form action="configureLogging.jsp" name="logFilterForm">
            Filter Loggers:&nbsp;&nbsp; <input name="logNameFilter" type="text"
                                               size="50″ value="<%=(logNameFilter == null ? "" : logNameFilter)%>"
            class="filterText" /> <input name="logNameFilterType" type="submit"
                                         value="<%=beginsWithFilter%>"/>&nbsp; <input
                name="logNameFilterType" type="submit" value="<%=containsFilter%>"
                class="filterButton"/>&nbsp; <input name="logNameClear"
                                                    type="button" value="Clear"
                                                    onmousedown='javascript:document.logFilterForm.logNameFilter.value="";'/>
            <input name="logNameReset" type="reset" value="Reset"
                   class="filterButton"/>

            <param name="operation" value="changeLogLevel"/>
        </form>
    </div>

    <table cellspacing="1″>
    <tr>
        <th width="25%">Logger</th>
        <th width="25%">Parent Logger</th>
        <th width="15%">Current Level</th>
        <th width="35%">Change Log Level To</th>
    </tr>

        <%
        Enumeration loggers = LogManager.getCurrentLoggers();
        HashMap loggersMap = new HashMap(128);
        Logger rootLogger = LogManager.getRootLogger();
        if (!loggersMap.containsKey(rootLogger.getName())) {
            loggersMap.put(rootLogger.getName(), rootLogger);
        }
        while (loggers.hasMoreElements()) {
            Logger logger = (Logger) loggers.nextElement();
            if (logNameFilter == null || logNameFilter.trim().length() == 0) {
                loggersMap.put(logger.getName(), logger);
            } else if (containsFilter.equals(logNameFilterType)) {
                if (logger.getName().toUpperCase().indexOf(logNameFilter.toUpperCase()) >= 0) {
                    loggersMap.put(logger.getName(), logger);
                }
            } else {
                // Either was no filter in IF, contains filter in ELSE IF, or begins with in ELSE
                if (logger.getName().startsWith(logNameFilter)) {
                    loggersMap.put(logger.getName(), logger);
                }
            }
        }
        Set loggerKeys = loggersMap.keySet();
        String[] keys = new String[loggerKeys.size()];
        keys = (String[]) loggerKeys.toArray(keys);
        Arrays.sort(keys, String.CASE_INSENSITIVE_ORDER);
        for (int i = 0; i < keys.length; i++) {
            Logger logger = (Logger) loggersMap.get(keys[i]);
            // MUST CHANGE THE LOG LEVEL ON LOGGER BEFORE GENERATING THE LINKS AND THE
            // CURRENT LOG LEVEL OR DISABLED LINK WON’T MATCH THE NEWLY CHANGED VALUES
            if ("changeLogLevel".equals(targetOperation)&& targetLogger.equals(logger.getName())) {
                Logger selectedLogger = (Logger) loggersMap.get(targetLogger);
                selectedLogger.setLevel(Level.toLevel(targetLogLevel));
            }
            String loggerName = null;
            String loggerEffectiveLevel = null;
            String loggerParent = null;
            if (logger != null) {
                loggerName = logger.getName();
                loggerEffectiveLevel = String.valueOf(logger
                        .getEffectiveLevel());
                loggerParent = (logger.getParent() == null ? null : logger
                        .getParent().getName());
            }
        %>
    <tr>
        <td><%=loggerName%>
        </td>

        <td><%=loggerParent%>
        </td>
        <td><%=loggerEffectiveLevel%>
        </td>
        <td>
                <%
            for (int cnt = 0; cnt < logLevels.length; cnt++) {

                    String url = "configureLogging.jsp?operation=changeLogLevel&logger="+ loggerName
                            + "&newLogLevel="
                            + logLevels[cnt]
                            + "&logNameFilter="
                            + (logNameFilter != null ? logNameFilter : "")
                            + "&logNameFilterType="
                            + (logNameFilterType != null ? logNameFilterType : "");
                    if (logger.getLevel() == Level.toLevel(logLevels[cnt])
                            || logger.getEffectiveLevel() == Level
                                    .toLevel(logLevels[cnt])) {
            %> [<%=logLevels[cnt].toUpperCase()%>] <%
                } else {
            %> <a href='<%=url%>'>[<%=logLevels[cnt]%>]</a>&nbsp; <%}}%>
        </td>
    </tr>
    <%}%>
        </table>
    </div>
</body>
</html>

Once you copy this JSP under root of your web app, you can access it by entering http://[host]:[port]/[context root]/configureLogging.jsp

git revert to commit

irst, it’s always worth noting that git reset –hard is a potentially dangerous command, since it throws away all your uncommitted changes. For safety, you should always check that the output of git status is clean (that is, empty) before using it.

Initially you say the following:

So I know that Git tracks changes I make to my application, and it holds on to them until I commit the changes, but here’s where I’m hung up:

In case this reveals a mistaken assumption, I should say that this isn’t correct. Git only records the state of the files when you stage them (with git add) or when you create a commit. Once you’ve created a commit which has your project files in a particular state, they’re very safe, but until then Git’s not really “tracking changes” to your files. (for example, even if you do git add to stage a new version of the file, that overwrites the previously staged version of that file in the staging area.)

In your question you then go on to ask the following:

When I want to revert to a previous commit I use: git reset –hard HEAD And git returns: HEAD is now at 820f417 micro

How do I then revert the files on my hard drive back to that previous commit?

If you do git reset --hard then Git will:

Make your current branch (typically master) back to point at .
Then make the files in your working tree and the index (“staging area”) the same as the versions committed in .
HEAD points to your current branch (or current commit), so all that git reset --hard HEAD will do is to throw away any uncommitted changes you have.

So, suppose the good commit that you want to go back to is f414f31. (You can find that via git log or any history browser.) You then have a few different options depending on exactly what you want to do:

Change your current branch to point to the older commit instead. You could do that with git reset –hard f414f31. However, this is rewriting the history of your branch, so you should avoid it if you’ve shared this branch with anyone. Also, the commits you did after f414f31 will no longer be in the history of your master branch.
Create a new commit that represents exactly the same state of the project as f414f31, but just adds that on to the history, so you don’t lose any history. You can do that using the steps suggested in this answer – something like:
git reset --hard f414f31
git reset --soft HEAD@{1}
git commit -m "Reverting to the state of the project at f414f31"